Skip to content

Commit 074ee68

Browse files
authoredJun 2, 2022
Don't return InvalidArgument if error details are of unknown type (#94)
1 parent 69c8b41 commit 074ee68

File tree

6 files changed

+184
-26
lines changed

6 files changed

+184
-26
lines changed
 

‎LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License
22

3-
Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
3+
Copyright (c) 2022 Temporal Technologies Inc. All rights reserved.
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

‎go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ module go.temporal.io/api
33
go 1.16
44

55
require (
6-
github.com/gogo/googleapis v1.4.1 // indirect
6+
github.com/gogo/googleapis v1.4.1
77
github.com/gogo/protobuf v1.3.2
88
github.com/gogo/status v1.1.0
99
github.com/golang/mock v1.6.0
10+
github.com/stretchr/testify v1.7.0
1011
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
1112
google.golang.org/genproto v0.0.0-20220531173845-685668d2de03 // indirect
1213
google.golang.org/grpc v1.46.2

‎go.sum

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
1111
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
1212
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
1313
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
14+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
1415
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1516
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
1617
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -57,11 +58,13 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
5758
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
5859
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
5960
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
61+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
6062
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6163
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
6264
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
6365
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6466
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
67+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
6568
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
6669
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
6770
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -165,9 +168,11 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
165168
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
166169
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
167170
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
171+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
168172
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
169173
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
170174
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
175+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
171176
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
172177
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
173178
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

‎serviceerror/convert.go

+21-24
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func FromStatus(st *status.Status) error {
6363
return nil
6464
}
6565

66-
// Simple case. Code to serviceerror is one to one mapping and there are no error details.
66+
// Simple case. `st.Code()` to `serviceerror` is one to one mapping and there are no error details.
6767
switch st.Code() {
6868
case codes.DataLoss:
6969
return newDataLoss(st)
@@ -87,58 +87,52 @@ func FromStatus(st *status.Status) error {
8787
}
8888

8989
errDetails := extractErrorDetails(st)
90-
91-
// If there was an error during details extraction, it will go to errDetails.
92-
if err, ok := errDetails.(error); ok {
93-
return NewInvalidArgument(err.Error())
94-
}
90+
// If there was an error during details extraction, for example unknown message type,
91+
// which can happen when new error details are added and getting read by old clients,
92+
// then errDetails will be of type `error` with corresponding error inside.
93+
// This error is ignored and `serviceerror` is built using `st.Code()` only.
9594

9695
switch st.Code() {
9796
case codes.Internal:
98-
if errDetails == nil {
99-
return newInternal(st)
100-
}
10197
switch errDetails := errDetails.(type) {
10298
case *errordetails.SystemWorkflowFailure:
10399
return newSystemWorkflow(st, errDetails)
100+
default:
101+
return newInternal(st)
104102
}
105103
case codes.NotFound:
106-
if errDetails == nil {
107-
return newNotFound(st, nil)
108-
}
109104
switch errDetails := errDetails.(type) {
110105
case *errordetails.NotFoundFailure:
111106
return newNotFound(st, errDetails)
112107
case *errordetails.NamespaceNotFoundFailure:
113108
return newNamespaceNotFound(st, errDetails)
109+
default:
110+
return newNotFound(st, nil)
114111
}
115112
case codes.InvalidArgument:
116-
if errDetails == nil {
117-
return newInvalidArgument(st)
118-
}
119113
switch errDetails.(type) {
120114
case *errordetails.QueryFailedFailure:
121115
return newQueryFailed(st)
116+
default:
117+
return newInvalidArgument(st)
122118
}
123119
case codes.ResourceExhausted:
124-
if errDetails == nil {
125-
return newResourceExhausted(st, nil)
126-
}
127120
switch errDetails := errDetails.(type) {
128121
case *errordetails.ResourceExhaustedFailure:
129122
return newResourceExhausted(st, errDetails)
123+
default:
124+
return newResourceExhausted(st, nil)
130125
}
131126
case codes.AlreadyExists:
132-
if errDetails == nil {
133-
return newAlreadyExists(st)
134-
}
135127
switch errDetails := errDetails.(type) {
136128
case *errordetails.NamespaceAlreadyExistsFailure:
137129
return newNamespaceAlreadyExists(st)
138130
case *errordetails.WorkflowExecutionAlreadyStartedFailure:
139131
return newWorkflowExecutionAlreadyStarted(st, errDetails)
140132
case *errordetails.CancellationAlreadyRequestedFailure:
141133
return newCancellationAlreadyRequested(st)
134+
default:
135+
return newAlreadyExists(st)
142136
}
143137
case codes.FailedPrecondition:
144138
switch errDetails := errDetails.(type) {
@@ -152,17 +146,20 @@ func FromStatus(st *status.Status) error {
152146
return newServerVersionNotSupported(st, errDetails)
153147
case *errordetails.WorkflowNotReadyFailure:
154148
return newWorkflowNotReady(st)
149+
default:
150+
return newFailedPrecondition(st)
155151
}
156152
case codes.PermissionDenied:
157153
switch errDetails := errDetails.(type) {
158154
case *errordetails.PermissionDeniedFailure:
159155
return newPermissionDenied(st, errDetails)
156+
default:
157+
return newPermissionDenied(st, nil)
160158
}
161-
return newPermissionDenied(st, nil)
162159
}
163160

164-
// st.Code() should have error details, but it didn't (or error details are of a wrong type).
165-
// Then use standard gRPC error representation ("rpc error: code = %s desc = %s").
161+
// `st.Code()` has unknown value (should never happen).
162+
// Use standard gRPC error representation "rpc error: code = %s desc = %s".
166163
return st.Err()
167164
}
168165

‎serviceerror/convert_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2022 Temporal Technologies Inc. All rights reserved.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
// THE SOFTWARE.
22+
23+
package serviceerror_test
24+
25+
import (
26+
"errors"
27+
"testing"
28+
29+
"github.com/gogo/googleapis/google/rpc"
30+
"github.com/gogo/protobuf/types"
31+
"github.com/gogo/status"
32+
"github.com/stretchr/testify/require"
33+
"google.golang.org/grpc/codes"
34+
35+
"go.temporal.io/api/errordetails/v1"
36+
"go.temporal.io/api/serviceerror"
37+
)
38+
39+
func TestFromStatus_NotFound(t *testing.T) {
40+
var err error
41+
st1 := status.New(codes.NotFound, "Not found.")
42+
err1 := serviceerror.FromStatus(st1)
43+
require.IsType(t, &serviceerror.NotFound{}, err1)
44+
require.Equal(t, codes.NotFound, err1.(*serviceerror.NotFound).Status().Code())
45+
require.Equal(t, "Not found.", err1.(*serviceerror.NotFound).Message)
46+
47+
st2 := status.New(codes.NotFound, "Not found.")
48+
st2, err = st1.WithDetails(&errordetails.NamespaceNotFoundFailure{
49+
Namespace: "test-ns",
50+
})
51+
require.NoError(t, err)
52+
err2 := serviceerror.FromStatus(st2)
53+
require.IsType(t, &serviceerror.NamespaceNotFound{}, err2)
54+
require.Equal(t, codes.NotFound, err2.(*serviceerror.NamespaceNotFound).Status().Code())
55+
require.Equal(t, "Not found.", err2.(*serviceerror.NamespaceNotFound).Message)
56+
require.Equal(t, "test-ns", err2.(*serviceerror.NamespaceNotFound).Namespace)
57+
}
58+
59+
func TestFromStatus_UnknownErrorDetails(t *testing.T) {
60+
st1 := status.FromProto(&rpc.Status{
61+
Code: int32(codes.NotFound),
62+
Message: "Not found.",
63+
Details: []*types.Any{{TypeUrl: "type.googleapis.com/some.unknown.Type"}},
64+
})
65+
66+
err1 := serviceerror.FromStatus(st1)
67+
require.IsType(t, &serviceerror.NotFound{}, err1)
68+
require.Equal(t, codes.NotFound, err1.(*serviceerror.NotFound).Status().Code())
69+
require.Equal(t, "Not found.", err1.(*serviceerror.NotFound).Message)
70+
}
71+
72+
func TestToStatus_UnknownErrorDetails(t *testing.T) {
73+
err1 := status.ErrorProto(&rpc.Status{
74+
Code: int32(codes.NotFound),
75+
Message: "Not found.",
76+
Details: []*types.Any{{TypeUrl: "type.googleapis.com/some.unknown.Type"}},
77+
})
78+
79+
st1 := serviceerror.ToStatus(err1)
80+
require.Equal(t, codes.NotFound, st1.Code())
81+
require.Equal(t, "Not found.", st1.Message())
82+
require.Len(t, st1.Details(), 1)
83+
require.Equal(t, "type.googleapis.com/some.unknown.Type", st1.Proto().Details[0].TypeUrl)
84+
}
85+
86+
func TestToStatus_NotServiceError(t *testing.T) {
87+
err1 := errors.New("some error")
88+
st1 := serviceerror.ToStatus(err1)
89+
require.Equal(t, codes.Unknown, st1.Code())
90+
require.Equal(t, "some error", st1.Message())
91+
require.Len(t, st1.Details(), 0)
92+
}

‎serviceerror/failed_precondition.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in
13+
// all copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
// THE SOFTWARE.
22+
23+
package serviceerror
24+
25+
import (
26+
"github.com/gogo/status"
27+
"google.golang.org/grpc/codes"
28+
)
29+
30+
type (
31+
// FailedPrecondition represents failed precondition error.
32+
FailedPrecondition struct {
33+
Message string
34+
st *status.Status
35+
}
36+
)
37+
38+
// NewFailedPrecondition returns new FailedPrecondition error.
39+
func NewFailedPrecondition(message string) error {
40+
return &FailedPrecondition{
41+
Message: message,
42+
}
43+
}
44+
45+
// Error returns string message.
46+
func (e *FailedPrecondition) Error() string {
47+
return e.Message
48+
}
49+
50+
func (e *FailedPrecondition) Status() *status.Status {
51+
if e.st != nil {
52+
return e.st
53+
}
54+
55+
return status.New(codes.FailedPrecondition, e.Message)
56+
}
57+
58+
func newFailedPrecondition(st *status.Status) error {
59+
return &FailedPrecondition{
60+
Message: st.Message(),
61+
st: st,
62+
}
63+
}

0 commit comments

Comments
 (0)
Please sign in to comment.