diff --git a/auth/auth.go b/auth/auth.go index 6bd3b0f2..7f24b552 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -332,7 +332,7 @@ func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRev if c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant { return nil, &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant), + Message: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant), Ext: map[string]interface{}{ authErrorCode: tenantIDMismatch, }, @@ -428,7 +428,7 @@ func (c *baseClient) checkRevokedOrDisabled(ctx context.Context, token *Token, e if user.Disabled { return &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: "user has been disabled", + Message: "user has been disabled", Ext: map[string]interface{}{ authErrorCode: userDisabled, }, @@ -438,7 +438,7 @@ func (c *baseClient) checkRevokedOrDisabled(ctx context.Context, token *Token, e if token.IssuedAt*1000 < user.TokensValidAfterMillis { return &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: errMessage, + Message: errMessage, Ext: map[string]interface{}{ authErrorCode: errCode, }, diff --git a/auth/token_verifier.go b/auth/token_verifier.go index fe6cdf0c..827f976c 100644 --- a/auth/token_verifier.go +++ b/auth/token_verifier.go @@ -187,7 +187,7 @@ func (tv *tokenVerifier) verifyContent(token string, isEmulator bool) (*Token, e if token == "" { return nil, &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: fmt.Sprintf("%s must be a non-empty string", tv.shortName), + Message: fmt.Sprintf("%s must be a non-empty string", tv.shortName), Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode}, } } @@ -196,7 +196,7 @@ func (tv *tokenVerifier) verifyContent(token string, isEmulator bool) (*Token, e if err != nil { return nil, &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: fmt.Sprintf( + Message: fmt.Sprintf( "%s; see %s for details on how to retrieve a valid %s", err.Error(), tv.docURL, tv.shortName), Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode}, @@ -210,7 +210,7 @@ func (tv *tokenVerifier) verifyTimestamps(payload *Token) error { if (payload.IssuedAt - clockSkewSeconds) > tv.clock.Now().Unix() { return &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: fmt.Sprintf("%s issued at future timestamp: %d", tv.shortName, payload.IssuedAt), + Message: fmt.Sprintf("%s issued at future timestamp: %d", tv.shortName, payload.IssuedAt), Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode}, } } @@ -218,7 +218,7 @@ func (tv *tokenVerifier) verifyTimestamps(payload *Token) error { if (payload.Expires + clockSkewSeconds) < tv.clock.Now().Unix() { return &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: fmt.Sprintf("%s has expired at: %d", tv.shortName, payload.Expires), + Message: fmt.Sprintf("%s has expired at: %d", tv.shortName, payload.Expires), Ext: map[string]interface{}{authErrorCode: tv.expiredTokenCode}, } } @@ -231,7 +231,7 @@ func (tv *tokenVerifier) verifySignature(ctx context.Context, token string) erro if err != nil { return &internal.FirebaseError{ ErrorCode: internal.Unknown, - String: err.Error(), + Message: err.Error(), Ext: map[string]interface{}{authErrorCode: certificateFetchFailed}, } } @@ -239,7 +239,7 @@ func (tv *tokenVerifier) verifySignature(ctx context.Context, token string) erro if !tv.verifySignatureWithKeys(ctx, token, keys) { return &internal.FirebaseError{ ErrorCode: internal.InvalidArgument, - String: "failed to verify token signature", + Message: "failed to verify token signature", Ext: map[string]interface{}{authErrorCode: tv.invalidTokenCode}, } } diff --git a/auth/user_mgt.go b/auth/user_mgt.go index bee027e3..2ceede79 100644 --- a/auth/user_mgt.go +++ b/auth/user_mgt.go @@ -803,7 +803,7 @@ func (c *baseClient) GetUserByProviderUID(ctx context.Context, providerID string if len(getUsersResult.Users) == 0 { return nil, &internal.FirebaseError{ ErrorCode: internal.NotFound, - String: fmt.Sprintf("cannot find user from providerID: { %s, %s }", providerID, providerUID), + Message: fmt.Sprintf("cannot find user from providerID: { %s, %s }", providerID, providerUID), Response: nil, Ext: map[string]interface{}{ authErrorCode: userNotFound, @@ -848,7 +848,7 @@ func (c *baseClient) getUser(ctx context.Context, query *userQuery) (*UserRecord if len(parsed.Users) == 0 { return nil, &internal.FirebaseError{ ErrorCode: internal.NotFound, - String: fmt.Sprintf("no user exists with the %s", query.description()), + Message: fmt.Sprintf("no user exists with the %s", query.description()), Response: resp.LowLevelResponse(), Ext: map[string]interface{}{ authErrorCode: userNotFound, @@ -1487,9 +1487,9 @@ func handleHTTPError(resp *internal.Response) error { err.ErrorCode = authErr.code err.Ext[authErrorCode] = authErr.authCode if detail != "" { - err.String = fmt.Sprintf("%s: %s", authErr.message, detail) + err.Message = fmt.Sprintf("%s: %s", authErr.message, detail) } else { - err.String = authErr.message + err.Message = authErr.message } } diff --git a/db/db.go b/db/db.go index f30c534c..62e52440 100644 --- a/db/db.go +++ b/db/db.go @@ -150,7 +150,7 @@ func handleRTDBError(resp *internal.Response) error { } json.Unmarshal(resp.Body, &p) if p.Error != "" { - err.String = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error) + err.Message = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error) } return err diff --git a/errorutils/errorutils.go b/errorutils/errorutils.go index ebf8d197..6469a2d0 100644 --- a/errorutils/errorutils.go +++ b/errorutils/errorutils.go @@ -16,48 +16,140 @@ package errorutils import ( + "errors" "net/http" +) + +// ErrorCode represents the platform-wide error codes that can be raised by +// Admin SDK APIs. +type ErrorCode string + +const ( + // InvalidArgument is a OnePlatform error code. + InvalidArgument ErrorCode = "INVALID_ARGUMENT" + + // FailedPrecondition is a OnePlatform error code. + FailedPrecondition ErrorCode = "FAILED_PRECONDITION" + + // OutOfRange is a OnePlatform error code. + OutOfRange ErrorCode = "OUT_OF_RANGE" + + // Unauthenticated is a OnePlatform error code. + Unauthenticated ErrorCode = "UNAUTHENTICATED" + + // PermissionDenied is a OnePlatform error code. + PermissionDenied ErrorCode = "PERMISSION_DENIED" + + // NotFound is a OnePlatform error code. + NotFound ErrorCode = "NOT_FOUND" + + // Conflict is a custom error code that represents HTTP 409 responses. + // + // OnePlatform APIs typically respond with ABORTED or ALREADY_EXISTS explicitly. But a few + // old APIs send HTTP 409 Conflict without any additional details to distinguish between the two + // cases. For these we currently use this error code. As more APIs adopt OnePlatform conventions + // this will become less important. + Conflict ErrorCode = "CONFLICT" + + // Aborted is a OnePlatform error code. + Aborted ErrorCode = "ABORTED" + + // AlreadyExists is a OnePlatform error code. + AlreadyExists ErrorCode = "ALREADY_EXISTS" + + // ResourceExhausted is a OnePlatform error code. + ResourceExhausted ErrorCode = "RESOURCE_EXHAUSTED" + + // Cancelled is a OnePlatform error code. + Cancelled ErrorCode = "CANCELLED" + + // DataLoss is a OnePlatform error code. + DataLoss ErrorCode = "DATA_LOSS" + + // Unknown is a OnePlatform error code. + Unknown ErrorCode = "UNKNOWN" - "firebase.google.com/go/v4/internal" + // Internal is a OnePlatform error code. + Internal ErrorCode = "INTERNAL" + + // Unavailable is a OnePlatform error code. + Unavailable ErrorCode = "UNAVAILABLE" + + // DeadlineExceeded is a OnePlatform error code. + DeadlineExceeded ErrorCode = "DEADLINE_EXCEEDED" ) +// FirebaseError is an error type containing an: +// - error code string. +// - error message. +// - HTTP response that caused this error, if any. +// - additional metadata about the error. +type FirebaseError struct { + ErrorCode ErrorCode + Message string + Response *http.Response + Ext map[string]interface{} +} + +// Error implements the error interface. +func (fe *FirebaseError) Error() string { + return fe.Message +} + +// Code returns the canonical error code associated with this error. +func (fe *FirebaseError) Code() ErrorCode { + return fe.ErrorCode +} + +// HTTPResponse returns the HTTP response that caused this error, if any. +// Returns nil if the error was not caused by an HTTP response. +func (fe *FirebaseError) HTTPResponse() *http.Response { + return fe.Response +} + +// Extensions returns additional metadata associated with this error. +// Returns nil if no extensions are available. +func (fe *FirebaseError) Extensions() map[string]interface{} { + return fe.Ext +} + // IsInvalidArgument checks if the given error was due to an invalid client argument. func IsInvalidArgument(err error) bool { - return internal.HasPlatformErrorCode(err, internal.InvalidArgument) + return HasPlatformErrorCode(err, InvalidArgument) } // IsFailedPrecondition checks if the given error was because a request could not be executed // in the current system state, such as deleting a non-empty directory. func IsFailedPrecondition(err error) bool { - return internal.HasPlatformErrorCode(err, internal.FailedPrecondition) + return HasPlatformErrorCode(err, FailedPrecondition) } // IsOutOfRange checks if the given error due to an invalid range specified by the client. func IsOutOfRange(err error) bool { - return internal.HasPlatformErrorCode(err, internal.OutOfRange) + return HasPlatformErrorCode(err, OutOfRange) } // IsUnauthenticated checks if the given error was caused by an unauthenticated request. // // Unauthenticated requests are due to missing, invalid, or expired OAuth token. func IsUnauthenticated(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Unauthenticated) + return HasPlatformErrorCode(err, Unauthenticated) } -// IsPermissionDenied checks if the given error was due to a client not having suffificient +// IsPermissionDenied checks if the given error was due to a client not having sufficient // permissions. // // This can happen because the OAuth token does not have the right scopes, the client doesn't have // permission, or the API has not been enabled for the client project. func IsPermissionDenied(err error) bool { - return internal.HasPlatformErrorCode(err, internal.PermissionDenied) + return HasPlatformErrorCode(err, PermissionDenied) } // IsNotFound checks if the given error was due to a specified resource being not found. // // This may also occur when the request is rejected by undisclosed reasons, such as whitelisting. func IsNotFound(err error) bool { - return internal.HasPlatformErrorCode(err, internal.NotFound) + return HasPlatformErrorCode(err, NotFound) } // IsConflict checks if the given error was due to a concurrency conflict, such as a @@ -66,58 +158,58 @@ func IsNotFound(err error) bool { // This represents an HTTP 409 Conflict status code, without additional information to distinguish // between ABORTED or ALREADY_EXISTS error conditions. func IsConflict(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Conflict) + return HasPlatformErrorCode(err, Conflict) } // IsAborted checks if the given error was due to a concurrency conflict, such as a // read-modify-write conflict. func IsAborted(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Aborted) + return HasPlatformErrorCode(err, Aborted) } // IsAlreadyExists checks if the given error was because a resource that a client tried to create // already exists. func IsAlreadyExists(err error) bool { - return internal.HasPlatformErrorCode(err, internal.AlreadyExists) + return HasPlatformErrorCode(err, AlreadyExists) } // IsResourceExhausted checks if the given error was caused by either running out of a quota or // reaching a rate limit. func IsResourceExhausted(err error) bool { - return internal.HasPlatformErrorCode(err, internal.ResourceExhausted) + return HasPlatformErrorCode(err, ResourceExhausted) } // IsCancelled checks if the given error was due to the client cancelling a request. func IsCancelled(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Cancelled) + return HasPlatformErrorCode(err, Cancelled) } // IsDataLoss checks if the given error was due to an unrecoverable data loss or corruption. // // The client should report such errors to the end user. func IsDataLoss(err error) bool { - return internal.HasPlatformErrorCode(err, internal.DataLoss) + return HasPlatformErrorCode(err, DataLoss) } -// IsUnknown checks if the given error was cuased by an unknown server error. +// IsUnknown checks if the given error was caused by an unknown server error. // // This typically indicates a server bug. func IsUnknown(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Unknown) + return HasPlatformErrorCode(err, Unknown) } // IsInternal checks if the given error was due to an internal server error. // // This typically indicates a server bug. func IsInternal(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Internal) + return HasPlatformErrorCode(err, Internal) } // IsUnavailable checks if the given error was caused by an unavailable service. // // This typically indicates that the target service is temporarily down. func IsUnavailable(err error) bool { - return internal.HasPlatformErrorCode(err, internal.Unavailable) + return HasPlatformErrorCode(err, Unavailable) } // IsDeadlineExceeded checks if the given error was due a request exceeding a deadline. @@ -126,7 +218,7 @@ func IsUnavailable(err error) bool { // deadline (i.e. requested deadline is not enough for the server to process the request) and the // request did not finish within the deadline. func IsDeadlineExceeded(err error) bool { - return internal.HasPlatformErrorCode(err, internal.DeadlineExceeded) + return HasPlatformErrorCode(err, DeadlineExceeded) } // HTTPResponse returns the http.Response instance that caused the given error. @@ -136,10 +228,16 @@ func IsDeadlineExceeded(err error) bool { // Returns a buffered copy of the original response received from the network stack. It is safe to // read the response content from the returned http.Response. func HTTPResponse(err error) *http.Response { - fe, ok := err.(*internal.FirebaseError) + fe, ok := err.(*FirebaseError) if ok { return fe.Response } return nil } + +// HasPlatformErrorCode checks if the given error contains a specific error code. +func HasPlatformErrorCode(err error, code ErrorCode) bool { + var fe *FirebaseError + return errors.As(err, &fe) && fe.ErrorCode == code +} diff --git a/errorutils/errorutils_test.go b/errorutils/errorutils_test.go new file mode 100644 index 00000000..7e6a09f1 --- /dev/null +++ b/errorutils/errorutils_test.go @@ -0,0 +1,259 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errorutils + +import ( + "errors" + "fmt" + "net/http" + "testing" +) + +func TestFirebaseErrorImplementsError(t *testing.T) { + fe := &FirebaseError{ + ErrorCode: NotFound, + Message: "resource not found", + } + + var err error = fe + if err.Error() != "resource not found" { + t.Errorf("Error() = %q; want = %q", err.Error(), "resource not found") + } +} + +func TestHTTPResponseFunctionWithNilResponseField(t *testing.T) { + fe := &FirebaseError{ + ErrorCode: Internal, + Message: "internal error", + Response: nil, + } + + if HTTPResponse(fe) != nil { + t.Errorf("HTTPResponse(fe) = %v; want = nil", HTTPResponse(fe)) + } +} + +func TestHasPlatformErrorCode(t *testing.T) { + fe := &FirebaseError{ + ErrorCode: NotFound, + Message: "not found", + } + + if !HasPlatformErrorCode(fe, NotFound) { + t.Error("HasPlatformErrorCode(fe, NotFound) = false; want = true") + } + + if HasPlatformErrorCode(fe, Internal) { + t.Error("HasPlatformErrorCode(fe, Internal) = true; want = false") + } +} + +func TestHasPlatformErrorCodeWithNonFirebaseError(t *testing.T) { + err := errors.New("regular error") + + if HasPlatformErrorCode(err, NotFound) { + t.Error("HasPlatformErrorCode(err, NotFound) = true; want = false") + } +} + +func TestHasPlatformErrorCodeWithNil(t *testing.T) { + if HasPlatformErrorCode(nil, NotFound) { + t.Error("HasPlatformErrorCode(nil, NotFound) = true; want = false") + } +} + +func TestHTTPResponseFunction(t *testing.T) { + resp := &http.Response{StatusCode: http.StatusInternalServerError} + fe := &FirebaseError{ + ErrorCode: Internal, + Message: "internal error", + Response: resp, + } + + if HTTPResponse(fe) != resp { + t.Errorf("HTTPResponse(fe) = %v; want = %v", HTTPResponse(fe), resp) + } +} + +func TestHTTPResponseFunctionWithNonFirebaseError(t *testing.T) { + err := errors.New("regular error") + + if HTTPResponse(err) != nil { + t.Errorf("HTTPResponse(err) = %v; want = nil", HTTPResponse(err)) + } +} + +func TestHTTPResponseFunctionWithNil(t *testing.T) { + if HTTPResponse(nil) != nil { + t.Errorf("HTTPResponse(nil) = %v; want = nil", HTTPResponse(nil)) + } +} + +func TestIsErrorCodeFunctions(t *testing.T) { + testCases := []struct { + name string + code ErrorCode + checkFn func(error) bool + }{ + {"IsInvalidArgument", InvalidArgument, IsInvalidArgument}, + {"IsFailedPrecondition", FailedPrecondition, IsFailedPrecondition}, + {"IsOutOfRange", OutOfRange, IsOutOfRange}, + {"IsUnauthenticated", Unauthenticated, IsUnauthenticated}, + {"IsPermissionDenied", PermissionDenied, IsPermissionDenied}, + {"IsNotFound", NotFound, IsNotFound}, + {"IsConflict", Conflict, IsConflict}, + {"IsAborted", Aborted, IsAborted}, + {"IsAlreadyExists", AlreadyExists, IsAlreadyExists}, + {"IsResourceExhausted", ResourceExhausted, IsResourceExhausted}, + {"IsCancelled", Cancelled, IsCancelled}, + {"IsDataLoss", DataLoss, IsDataLoss}, + {"IsUnknown", Unknown, IsUnknown}, + {"IsInternal", Internal, IsInternal}, + {"IsUnavailable", Unavailable, IsUnavailable}, + {"IsDeadlineExceeded", DeadlineExceeded, IsDeadlineExceeded}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fe := &FirebaseError{ + ErrorCode: tc.code, + Message: "test error", + } + + if !tc.checkFn(fe) { + t.Errorf("%s check = false; want = true", tc.name) + } + }) + } +} + +func TestIsErrorCodeFunctionsWithWrongCode(t *testing.T) { + fe := &FirebaseError{ + ErrorCode: NotFound, + Message: "not found", + } + + checks := []struct { + name string + checkFn func(error) bool + }{ + {"IsInvalidArgument", IsInvalidArgument}, + {"IsFailedPrecondition", IsFailedPrecondition}, + {"IsOutOfRange", IsOutOfRange}, + {"IsUnauthenticated", IsUnauthenticated}, + {"IsPermissionDenied", IsPermissionDenied}, + {"IsConflict", IsConflict}, + {"IsAborted", IsAborted}, + {"IsAlreadyExists", IsAlreadyExists}, + {"IsResourceExhausted", IsResourceExhausted}, + {"IsCancelled", IsCancelled}, + {"IsDataLoss", IsDataLoss}, + {"IsUnknown", IsUnknown}, + {"IsInternal", IsInternal}, + {"IsUnavailable", IsUnavailable}, + {"IsDeadlineExceeded", IsDeadlineExceeded}, + } + + for _, tc := range checks { + t.Run(tc.name, func(t *testing.T) { + if tc.checkFn(fe) { + t.Errorf("%s(NotFoundError) = true; want = false", tc.name) + } + }) + } +} + +func TestIsErrorCodeFunctionsWithNonFirebaseError(t *testing.T) { + err := errors.New("regular error") + + checks := []struct { + name string + checkFn func(error) bool + }{ + {"IsInvalidArgument", IsInvalidArgument}, + {"IsNotFound", IsNotFound}, + {"IsInternal", IsInternal}, + } + + for _, tc := range checks { + t.Run(tc.name, func(t *testing.T) { + if tc.checkFn(err) { + t.Errorf("%s(regularError) = true; want = false", tc.name) + } + }) + } +} + +func TestIsErrorCodeFunctionsWithNil(t *testing.T) { + if IsNotFound(nil) { + t.Error("IsNotFound(nil) = true; want = false") + } + + if IsInternal(nil) { + t.Error("IsInternal(nil) = true; want = false") + } +} + +func TestErrorCodeValues(t *testing.T) { + // These values are part of the public API contract with OnePlatform + testCases := []struct { + code ErrorCode + want string + }{ + {InvalidArgument, "INVALID_ARGUMENT"}, + {FailedPrecondition, "FAILED_PRECONDITION"}, + {OutOfRange, "OUT_OF_RANGE"}, + {Unauthenticated, "UNAUTHENTICATED"}, + {PermissionDenied, "PERMISSION_DENIED"}, + {NotFound, "NOT_FOUND"}, + {Conflict, "CONFLICT"}, + {Aborted, "ABORTED"}, + {AlreadyExists, "ALREADY_EXISTS"}, + {ResourceExhausted, "RESOURCE_EXHAUSTED"}, + {Cancelled, "CANCELLED"}, + {DataLoss, "DATA_LOSS"}, + {Unknown, "UNKNOWN"}, + {Internal, "INTERNAL"}, + {Unavailable, "UNAVAILABLE"}, + {DeadlineExceeded, "DEADLINE_EXCEEDED"}, + } + + for _, tc := range testCases { + t.Run(tc.want, func(t *testing.T) { + if string(tc.code) != tc.want { + t.Errorf("ErrorCode = %q; want = %q", tc.code, tc.want) + } + }) + } +} + +func TestWrappedFirebaseErrorNotDetected(t *testing.T) { + // Documents current behavior: wrapped errors are NOT detected by IsXxx functions + // This is a known limitation - the SDK uses type assertion, not errors.As + fe := &FirebaseError{ + ErrorCode: NotFound, + Message: "not found", + } + wrapped := fmt.Errorf("wrapped: %w", fe) + + // Current behavior: wrapped errors return false + if IsNotFound(wrapped) { + t.Error("IsNotFound(wrapped) = true; current implementation should return false for wrapped errors") + } + + if HasPlatformErrorCode(wrapped, NotFound) { + t.Error("HasPlatformErrorCode(wrapped) = true; current implementation should return false for wrapped errors") + } +} diff --git a/iid/iid.go b/iid/iid.go index c97f6e0f..137c2310 100644 --- a/iid/iid.go +++ b/iid/iid.go @@ -152,7 +152,7 @@ func createError(resp *internal.Response) error { if msg, ok := errorMessages[resp.Status]; ok { requestPath := resp.LowLevelResponse().Request.URL.Path idx := strings.LastIndex(requestPath, "/") - err.String = fmt.Sprintf("instance id %q: %s", requestPath[idx+1:], msg) + err.Message = fmt.Sprintf("instance id %q: %s", requestPath[idx+1:], msg) } return err diff --git a/internal/errors.go b/internal/errors.go index 2c8b5d9c..2bbad4de 100644 --- a/internal/errors.go +++ b/internal/errors.go @@ -22,83 +22,39 @@ import ( "net/url" "os" "syscall" + + "firebase.google.com/go/v4/errorutils" ) -// ErrorCode represents the platform-wide error codes that can be raised by -// Admin SDK APIs. -type ErrorCode string +// ErrorCode alias to errorutils.ErrorCode +type ErrorCode = errorutils.ErrorCode +// Error code constants aliases to errorutils const ( - // InvalidArgument is a OnePlatform error code. - InvalidArgument ErrorCode = "INVALID_ARGUMENT" - - // FailedPrecondition is a OnePlatform error code. - FailedPrecondition ErrorCode = "FAILED_PRECONDITION" - - // OutOfRange is a OnePlatform error code. - OutOfRange ErrorCode = "OUT_OF_RANGE" - - // Unauthenticated is a OnePlatform error code. - Unauthenticated ErrorCode = "UNAUTHENTICATED" - - // PermissionDenied is a OnePlatform error code. - PermissionDenied ErrorCode = "PERMISSION_DENIED" - - // NotFound is a OnePlatform error code. - NotFound ErrorCode = "NOT_FOUND" - - // Conflict is a custom error code that represents HTTP 409 responses. - // - // OnePlatform APIs typically respond with ABORTED or ALREADY_EXISTS explicitly. But a few - // old APIs send HTTP 409 Conflict without any additional details to distinguish between the two - // cases. For these we currently use this error code. As more APIs adopt OnePlatform conventions - // this will become less important. - Conflict ErrorCode = "CONFLICT" - - // Aborted is a OnePlatform error code. - Aborted ErrorCode = "ABORTED" - - // AlreadyExists is a OnePlatform error code. - AlreadyExists ErrorCode = "ALREADY_EXISTS" - - // ResourceExhausted is a OnePlatform error code. - ResourceExhausted ErrorCode = "RESOURCE_EXHAUSTED" - - // Cancelled is a OnePlatform error code. - Cancelled ErrorCode = "CANCELLED" - - // DataLoss is a OnePlatform error code. - DataLoss ErrorCode = "DATA_LOSS" - - // Unknown is a OnePlatform error code. - Unknown ErrorCode = "UNKNOWN" - - // Internal is a OnePlatform error code. - Internal ErrorCode = "INTERNAL" - - // Unavailable is a OnePlatform error code. - Unavailable ErrorCode = "UNAVAILABLE" - - // DeadlineExceeded is a OnePlatform error code. - DeadlineExceeded ErrorCode = "DEADLINE_EXCEEDED" + InvalidArgument = errorutils.InvalidArgument + FailedPrecondition = errorutils.FailedPrecondition + OutOfRange = errorutils.OutOfRange + Unauthenticated = errorutils.Unauthenticated + PermissionDenied = errorutils.PermissionDenied + NotFound = errorutils.NotFound + Conflict = errorutils.Conflict + Aborted = errorutils.Aborted + AlreadyExists = errorutils.AlreadyExists + ResourceExhausted = errorutils.ResourceExhausted + Cancelled = errorutils.Cancelled + DataLoss = errorutils.DataLoss + Unknown = errorutils.Unknown + Internal = errorutils.Internal + Unavailable = errorutils.Unavailable + DeadlineExceeded = errorutils.DeadlineExceeded ) -// FirebaseError is an error type containing an error code string. -type FirebaseError struct { - ErrorCode ErrorCode - String string - Response *http.Response - Ext map[string]interface{} -} - -func (fe *FirebaseError) Error() string { - return fe.String -} +// FirebaseError is an alias to errorutils.FirebaseError for backwards compatibility. +type FirebaseError = errorutils.FirebaseError // HasPlatformErrorCode checks if the given error contains a specific error code. func HasPlatformErrorCode(err error, code ErrorCode) bool { - fe, ok := err.(*FirebaseError) - return ok && fe.ErrorCode == code + return errorutils.HasPlatformErrorCode(err, code) } var httpStatusToErrorCodes = map[int]ErrorCode{ @@ -121,7 +77,7 @@ func NewFirebaseError(resp *Response) *FirebaseError { return &FirebaseError{ ErrorCode: code, - String: fmt.Sprintf("unexpected http response with status: %d\n%s", resp.Status, string(resp.Body)), + Message: fmt.Sprintf("unexpected http response with status: %d\n%s", resp.Status, string(resp.Body)), Response: resp.LowLevelResponse(), Ext: make(map[string]interface{}), } @@ -147,7 +103,7 @@ func NewFirebaseErrorOnePlatform(resp *Response) *FirebaseError { } if gcpError.Error.Message != "" { - base.String = gcpError.Error.Message + base.Message = gcpError.Error.Message } return base @@ -169,7 +125,7 @@ func newFirebaseErrorTransport(err error) *FirebaseError { return &FirebaseError{ ErrorCode: code, - String: msg, + Message: msg, Ext: make(map[string]interface{}), } } diff --git a/messaging/topic_mgt.go b/messaging/topic_mgt.go index 0e7e9d0c..2e9962cf 100644 --- a/messaging/topic_mgt.go +++ b/messaging/topic_mgt.go @@ -156,7 +156,7 @@ func handleIIDError(resp *internal.Response) error { var ie iidErrorResponse json.Unmarshal(resp.Body, &ie) // ignore any json parse errors at this level if ie.Error != "" { - base.String = fmt.Sprintf("error while calling the iid service: %s", ie.Error) + base.Message = fmt.Sprintf("error while calling the iid service: %s", ie.Error) } return base diff --git a/remoteconfig/remoteconfig.go b/remoteconfig/remoteconfig.go index 55117cf5..db77eb78 100644 --- a/remoteconfig/remoteconfig.go +++ b/remoteconfig/remoteconfig.go @@ -111,7 +111,7 @@ func handleRemoteConfigError(resp *internal.Response) error { } json.Unmarshal(resp.Body, &p) if p.Error != "" { - err.String = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error) + err.Message = fmt.Sprintf("http error status: %d; reason: %s", resp.Status, p.Error) } return err