Skip to content

Commit 2554cb5

Browse files
authored
Improved error handling in iid package (#151)
* Improved error handling in iid package * Updated changelog
1 parent abce22e commit 2554cb5

File tree

4 files changed

+97
-27
lines changed

4 files changed

+97
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- [added] Added new functions for testing errors in the `iid` package
4+
(e.g. `iid.IsNotFound()`).
35
- [fixed] `auth.UpdateUser()` and `auth.DeleteUser()` return the expected
46
`UserNotFound` error when called with a non-existing uid.
57
- [added] Implemented the `auth.ImportUsers()` function for importing

iid/iid.go

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,77 @@ import (
2929

3030
const iidEndpoint = "https://console.firebase.google.com/v1"
3131

32-
var errorCodes = map[int]string{
33-
http.StatusBadRequest: "malformed instance id argument",
34-
http.StatusUnauthorized: "request not authorized",
35-
http.StatusForbidden: "project does not match instance ID or the client does not have sufficient privileges",
36-
http.StatusNotFound: "failed to find the instance id",
37-
http.StatusConflict: "already deleted",
38-
http.StatusTooManyRequests: "request throttled out by the backend server",
39-
http.StatusInternalServerError: "internal server error",
40-
http.StatusServiceUnavailable: "backend servers are over capacity",
32+
const (
33+
invalidArgument = "invalid-argument"
34+
unauthorized = "unauthorized"
35+
insufficientPermission = "insufficient-permission"
36+
notFound = "instance-id-not-found"
37+
alreadyDeleted = "instance-id-already-deleted"
38+
tooManyRequests = "too-many-requests"
39+
internalError = "internal-error"
40+
serverUnavailable = "server-unavailable"
41+
unknown = "unknown-error"
42+
)
43+
44+
var errorCodes = map[int]struct {
45+
code, message string
46+
}{
47+
http.StatusBadRequest: {invalidArgument, "malformed instance id argument"},
48+
http.StatusUnauthorized: {insufficientPermission, "request not authorized"},
49+
http.StatusForbidden: {
50+
insufficientPermission,
51+
"project does not match instance ID or the client does not have sufficient privileges",
52+
},
53+
http.StatusNotFound: {notFound, "failed to find the instance id"},
54+
http.StatusConflict: {alreadyDeleted, "already deleted"},
55+
http.StatusTooManyRequests: {tooManyRequests, "request throttled out by the backend server"},
56+
http.StatusInternalServerError: {internalError, "internal server error"},
57+
http.StatusServiceUnavailable: {serverUnavailable, "backend servers are over capacity"},
58+
}
59+
60+
// IsInvalidArgument checks if the given error was due to an invalid instance ID argument.
61+
func IsInvalidArgument(err error) bool {
62+
return internal.HasErrorCode(err, invalidArgument)
63+
}
64+
65+
// IsInsufficientPermission checks if the given error was due to the request not having the
66+
// required authorization. This could be due to the client not having the required permission
67+
// or the specified instance ID not matching the target Firebase project.
68+
func IsInsufficientPermission(err error) bool {
69+
return internal.HasErrorCode(err, insufficientPermission)
70+
}
71+
72+
// IsNotFound checks if the given error was due to a non existing instance ID.
73+
func IsNotFound(err error) bool {
74+
return internal.HasErrorCode(err, notFound)
75+
}
76+
77+
// IsAlreadyDeleted checks if the given error was due to the instance ID being already deleted from
78+
// the project.
79+
func IsAlreadyDeleted(err error) bool {
80+
return internal.HasErrorCode(err, alreadyDeleted)
81+
}
82+
83+
// IsTooManyRequests checks if the given error was due to the client sending too many requests
84+
// causing a server quota to exceed.
85+
func IsTooManyRequests(err error) bool {
86+
return internal.HasErrorCode(err, tooManyRequests)
87+
}
88+
89+
// IsInternal checks if the given error was due to an internal server error.
90+
func IsInternal(err error) bool {
91+
return internal.HasErrorCode(err, internalError)
92+
}
93+
94+
// IsServerUnavailable checks if the given error was due to the backend server being temporarily
95+
// unavailable.
96+
func IsServerUnavailable(err error) bool {
97+
return internal.HasErrorCode(err, serverUnavailable)
98+
}
99+
100+
// IsUnknown checks if the given error was due to unknown error returned by the backend server.
101+
func IsUnknown(err error) bool {
102+
return internal.HasErrorCode(err, unknown)
41103
}
42104

43105
// Client is the interface for the Firebase Instance ID service.
@@ -84,8 +146,11 @@ func (c *Client) DeleteInstanceID(ctx context.Context, iid string) error {
84146
return err
85147
}
86148

87-
if msg, ok := errorCodes[resp.Status]; ok {
88-
return fmt.Errorf("instance id %q: %s", iid, msg)
149+
if info, ok := errorCodes[resp.Status]; ok {
150+
return internal.Errorf(info.code, "instance id %q: %s", iid, info.message)
151+
}
152+
if err := resp.CheckStatus(http.StatusOK); err != nil {
153+
return internal.Error(unknown, err.Error())
89154
}
90-
return resp.CheckStatus(http.StatusOK)
155+
return nil
91156
}

iid/iid_test.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,22 @@ func TestDeleteInstanceIDError(t *testing.T) {
104104
}
105105
client.endpoint = ts.URL
106106

107-
for k, v := range errorCodes {
108-
status = k
107+
errorHandlers := map[int]func(error) bool{
108+
http.StatusBadRequest: IsInvalidArgument,
109+
http.StatusUnauthorized: IsInsufficientPermission,
110+
http.StatusForbidden: IsInsufficientPermission,
111+
http.StatusNotFound: IsNotFound,
112+
http.StatusConflict: IsAlreadyDeleted,
113+
http.StatusTooManyRequests: IsTooManyRequests,
114+
http.StatusInternalServerError: IsInternal,
115+
http.StatusServiceUnavailable: IsServerUnavailable,
116+
}
117+
118+
for code, check := range errorHandlers {
119+
status = code
120+
want := fmt.Sprintf("instance id %q: %s", "test-iid", errorCodes[code].message)
109121
err := client.DeleteInstanceID(ctx, "test-iid")
110-
if err == nil {
111-
t.Fatal("DeleteInstanceID() = nil; want = error")
112-
}
113-
114-
want := fmt.Sprintf("instance id %q: %s", "test-iid", v)
115-
if err.Error() != want {
122+
if err == nil || !check(err) || err.Error() != want {
116123
t.Errorf("DeleteInstanceID() = %v; want = %v", err, want)
117124
}
118125

@@ -149,13 +156,9 @@ func TestDeleteInstanceIDUnexpectedError(t *testing.T) {
149156
}
150157
client.endpoint = ts.URL
151158

152-
err = client.DeleteInstanceID(ctx, "test-iid")
153-
if err == nil {
154-
t.Fatal("DeleteInstanceID() = nil; want = error")
155-
}
156-
157159
want := "http error status: 511; reason: {}"
158-
if err.Error() != want {
160+
err = client.DeleteInstanceID(ctx, "test-iid")
161+
if err == nil || !IsUnknown(err) || err.Error() != want {
159162
t.Errorf("DeleteInstanceID() = %v; want = %v", err, want)
160163
}
161164

integration/iid/iid_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestNonExisting(t *testing.T) {
5858
t.Errorf("DeleteInstanceID(non-existing) = nil; want error")
5959
}
6060
want := `instance id "fictive-ID0": failed to find the instance id`
61-
if err.Error() != want {
61+
if !iid.IsNotFound(err) || err.Error() != want {
6262
t.Errorf("DeleteInstanceID(non-existing) = %v; want = %v", err, want)
6363
}
6464
}

0 commit comments

Comments
 (0)