mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-14 12:18:35 +00:00

# Which Problems Are Solved The current maintained gRPC server in combination with a REST (grpc) gateway is getting harder and harder to maintain. Additionally, there have been and still are issues with supporting / displaying `oneOf`s correctly. We therefore decided to exchange the server implementation to connectRPC, which apart from supporting connect as protocol, also also "standard" gRCP clients as well as HTTP/1.1 / rest like clients, e.g. curl directly call the server without any additional gateway. # How the Problems Are Solved - All v2 services are moved to connectRPC implementation. (v1 services are still served as pure grpc servers) - All gRPC server interceptors were migrated / copied to a corresponding connectRPC interceptor. - API.ListGrpcServices and API. ListGrpcMethods were changed to include the connect services and endpoints. - gRPC server reflection was changed to a `StaticReflector` using the `ListGrpcServices` list. - The `grpc.Server` interfaces was split into different combinations to be able to handle the different cases (grpc server and prefixed gateway, connect server with grpc gateway, connect server only, ...) - Docs of services serving connectRPC only with no additional gateway (instance, webkey, project, app, org v2 beta) are changed to expose that - since the plugin is not yet available on buf, we download it using `postinstall` hook of the docs # Additional Changes - WebKey service is added as v2 service (in addition to the current v2beta) # Additional Context closes #9483 --------- Co-authored-by: Elio Bischof <elio@zitadel.com>
117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
package gerrors
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"connectrpc.com/connect"
|
|
"github.com/jackc/pgx/v5/pgconn"
|
|
"github.com/zitadel/logging"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/protoadapt"
|
|
|
|
commandErrors "github.com/zitadel/zitadel/internal/command/errors"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
"github.com/zitadel/zitadel/pkg/grpc/message"
|
|
)
|
|
|
|
func ZITADELToGRPCError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
code, key, id, ok := ExtractZITADELError(err)
|
|
if !ok {
|
|
return status.Convert(err).Err()
|
|
}
|
|
msg := key
|
|
msg += " (" + id + ")"
|
|
|
|
errorInfo := getErrorInfo(id, key, err)
|
|
|
|
s, err := status.New(code, msg).WithDetails(errorInfo)
|
|
if err != nil {
|
|
logging.WithError(err).WithField("logID", "GRPC-gIeRw").Debug("unable to add detail")
|
|
return status.New(code, msg).Err()
|
|
}
|
|
|
|
return s.Err()
|
|
}
|
|
|
|
func ZITADELToConnectError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
connectError := new(connect.Error)
|
|
if errors.As(err, &connectError) {
|
|
return err
|
|
}
|
|
code, key, id, ok := ExtractZITADELError(err)
|
|
if !ok {
|
|
return status.Convert(err).Err()
|
|
}
|
|
msg := key
|
|
msg += " (" + id + ")"
|
|
|
|
errorInfo := getErrorInfo(id, key, err)
|
|
|
|
cErr := connect.NewError(connect.Code(code), errors.New(msg))
|
|
if detail, detailErr := connect.NewErrorDetail(errorInfo.(proto.Message)); detailErr == nil {
|
|
cErr.AddDetail(detail)
|
|
}
|
|
return cErr
|
|
}
|
|
|
|
func ExtractZITADELError(err error) (c codes.Code, msg, id string, ok bool) {
|
|
if err == nil {
|
|
return codes.OK, "", "", false
|
|
}
|
|
connErr := new(pgconn.ConnectError)
|
|
if ok := errors.As(err, &connErr); ok {
|
|
return codes.Internal, "db connection error", "", true
|
|
}
|
|
zitadelErr := new(zerrors.ZitadelError)
|
|
if ok := errors.As(err, &zitadelErr); !ok {
|
|
return codes.Unknown, err.Error(), "", false
|
|
}
|
|
switch {
|
|
case zerrors.IsErrorAlreadyExists(err):
|
|
return codes.AlreadyExists, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsDeadlineExceeded(err):
|
|
return codes.DeadlineExceeded, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsInternal(err):
|
|
return codes.Internal, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsErrorInvalidArgument(err):
|
|
return codes.InvalidArgument, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsNotFound(err):
|
|
return codes.NotFound, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsPermissionDenied(err):
|
|
return codes.PermissionDenied, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsPreconditionFailed(err):
|
|
return codes.FailedPrecondition, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsUnauthenticated(err):
|
|
return codes.Unauthenticated, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsUnavailable(err):
|
|
return codes.Unavailable, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsUnimplemented(err):
|
|
return codes.Unimplemented, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
case zerrors.IsResourceExhausted(err):
|
|
return codes.ResourceExhausted, zitadelErr.GetMessage(), zitadelErr.GetID(), true
|
|
default:
|
|
return codes.Unknown, err.Error(), "", false
|
|
}
|
|
}
|
|
|
|
func getErrorInfo(id, key string, err error) protoadapt.MessageV1 {
|
|
var errorInfo protoadapt.MessageV1
|
|
|
|
var wpe *commandErrors.WrongPasswordError
|
|
if err != nil && errors.As(err, &wpe) {
|
|
errorInfo = &message.CredentialsCheckError{Id: id, Message: key, FailedAttempts: wpe.FailedAttempts}
|
|
} else {
|
|
errorInfo = &message.ErrorDetail{Id: id, Message: key}
|
|
}
|
|
|
|
return errorInfo
|
|
}
|