mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-25 20:00:27 +00:00
154 lines
5 KiB
Go
154 lines
5 KiB
Go
// The MIT License
|
|
//
|
|
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package serviceerror
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/gogo/status"
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"go.temporal.io/api/errordetails/v1"
|
|
)
|
|
|
|
// ToStatus converts service error to gogo gRPC Status.
|
|
// If error is not a service error it returns status with code Unknown.
|
|
func ToStatus(err error) *status.Status {
|
|
if err == nil {
|
|
return status.New(codes.OK, "")
|
|
}
|
|
|
|
if svcerr, ok := err.(ServiceError); ok {
|
|
return svcerr.Status()
|
|
}
|
|
|
|
// Special case for context.DeadlineExceeded and context.Canceled because they can happen in unpredictable places.
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
return status.New(codes.DeadlineExceeded, err.Error())
|
|
}
|
|
if errors.Is(err, context.Canceled) {
|
|
return status.New(codes.Canceled, err.Error())
|
|
}
|
|
|
|
// Internal logic of status.Convert is:
|
|
// - if err is already gogo Status or gRPC Status, then just return it (this should never happen though).
|
|
// - otherwise returns codes.Unknown with message from err.Error() (this might happen if some generic go error reach to this point).
|
|
return status.Convert(err)
|
|
}
|
|
|
|
// FromStatus converts gogo gRPC Status to service error.
|
|
func FromStatus(st *status.Status) error {
|
|
if st == nil || st.Code() == codes.OK {
|
|
return nil
|
|
}
|
|
|
|
// Simple case. Code to serviceerror is one to one mapping and there are no error details.
|
|
switch st.Code() {
|
|
case codes.Internal:
|
|
return newInternal(st)
|
|
case codes.DataLoss:
|
|
return newDataLoss(st)
|
|
case codes.ResourceExhausted:
|
|
return newResourceExhausted(st)
|
|
case codes.DeadlineExceeded:
|
|
return newDeadlineExceeded(st)
|
|
case codes.Canceled:
|
|
return newCanceled(st)
|
|
case codes.Unavailable:
|
|
return newUnavailable(st)
|
|
case codes.Unimplemented:
|
|
return newUnimplemented(st)
|
|
case codes.Unknown:
|
|
// Unwrap error message from unknown error.
|
|
return errors.New(st.Message())
|
|
// Unsupported codes.
|
|
case codes.OutOfRange,
|
|
codes.Unauthenticated:
|
|
// Use standard gRPC error representation for unsupported codes ("rpc error: code = %s desc = %s").
|
|
return st.Err()
|
|
}
|
|
|
|
errDetails := extractErrorDetails(st)
|
|
|
|
// If there was an error during details extraction, it will go to errDetails.
|
|
if err, ok := errDetails.(error); ok {
|
|
return NewInvalidArgument(err.Error())
|
|
}
|
|
|
|
switch st.Code() {
|
|
case codes.NotFound:
|
|
if errDetails == nil {
|
|
return newNotFound(st, nil)
|
|
}
|
|
switch errDetails := errDetails.(type) {
|
|
case *errordetails.NotFoundFailure:
|
|
return newNotFound(st, errDetails)
|
|
}
|
|
case codes.InvalidArgument:
|
|
if errDetails == nil {
|
|
return newInvalidArgument(st)
|
|
}
|
|
switch errDetails.(type) {
|
|
case *errordetails.QueryFailedFailure:
|
|
return newQueryFailed(st)
|
|
}
|
|
case codes.AlreadyExists:
|
|
switch errDetails := errDetails.(type) {
|
|
case *errordetails.NamespaceAlreadyExistsFailure:
|
|
return newNamespaceAlreadyExists(st)
|
|
case *errordetails.WorkflowExecutionAlreadyStartedFailure:
|
|
return newWorkflowExecutionAlreadyStarted(st, errDetails)
|
|
case *errordetails.CancellationAlreadyRequestedFailure:
|
|
return newCancellationAlreadyRequested(st)
|
|
}
|
|
case codes.FailedPrecondition:
|
|
switch errDetails := errDetails.(type) {
|
|
case *errordetails.NamespaceNotActiveFailure:
|
|
return newNamespaceNotActive(st, errDetails)
|
|
case *errordetails.ClientVersionNotSupportedFailure:
|
|
return newClientVersionNotSupported(st, errDetails)
|
|
case *errordetails.ServerVersionNotSupportedFailure:
|
|
return newServerVersionNotSupported(st, errDetails)
|
|
}
|
|
case codes.PermissionDenied:
|
|
switch errDetails := errDetails.(type) {
|
|
case *errordetails.PermissionDeniedFailure:
|
|
return newPermissionDenied(st, errDetails)
|
|
}
|
|
return newPermissionDenied(st, nil)
|
|
}
|
|
|
|
// st.Code() should have error details but it didn't (or error details are of a wrong type).
|
|
// Then use standard gRPC error representation ("rpc error: code = %s desc = %s").
|
|
return st.Err()
|
|
}
|
|
|
|
func extractErrorDetails(st *status.Status) interface{} {
|
|
details := st.Details()
|
|
if len(details) > 0 {
|
|
return details[0]
|
|
}
|
|
|
|
return nil
|
|
}
|