go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/grpc/appstatus/helpers.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package appstatus 16 17 import ( 18 "context" 19 20 "github.com/golang/protobuf/proto" 21 "google.golang.org/genproto/googleapis/rpc/errdetails" 22 "google.golang.org/grpc/codes" 23 "google.golang.org/grpc/status" 24 25 "go.chromium.org/luci/common/errors" 26 ) 27 28 // BadRequest annotates err as a bad request. 29 // The error message is shared with the requester as is. 30 func BadRequest(err error, details ...*errdetails.BadRequest) error { 31 s := status.Newf(codes.InvalidArgument, "bad request: %s", err) 32 33 if len(details) > 0 { 34 det := make([]proto.Message, len(details)) 35 for i, d := range details { 36 det[i] = d 37 } 38 39 s = MustWithDetails(s, det...) 40 } 41 42 return Attach(err, s) 43 } 44 45 // MustWithDetails adds details to a status and asserts it is successful. 46 func MustWithDetails(s *status.Status, details ...proto.Message) *status.Status { 47 s, err := s.WithDetails(details...) 48 if err != nil { 49 panic(err) 50 } 51 return s 52 } 53 54 // GRPCifyAndLog returns a GRPC error. If the error doesn't have a GRPC status 55 // attached by this package, internal error is assumed. Any internal or unknown 56 // errors are logged. 57 func GRPCifyAndLog(ctx context.Context, err error) error { 58 if err == nil { 59 return nil 60 } 61 62 s := statusFromError(err) 63 if s.Code() == codes.Internal || s.Code() == codes.Unknown { 64 errors.Log(ctx, err) 65 } 66 return s.Err() 67 } 68 69 // statusFromError returns a status to return to the client based on the error. 70 func statusFromError(err error) *status.Status { 71 if s, ok := Get(err); ok { 72 return s 73 } 74 75 if err := errors.Unwrap(err); err == context.DeadlineExceeded || err == context.Canceled { 76 return status.FromContextError(err) 77 } 78 79 return status.New(codes.Internal, "internal server error") 80 }