go.uber.org/yarpc@v1.72.1/encoding/thrift/thriftrw-plugin-yarpc/exception.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package main 22 23 import ( 24 "fmt" 25 "path/filepath" 26 "strings" 27 28 "go.uber.org/thriftrw/compile" 29 "go.uber.org/thriftrw/plugin" 30 ) 31 32 const ( 33 _errorCodeAnnotationKey = "rpc.code" 34 ) 35 36 const yarpcerrorTemplate = ` 37 // Code generated by thriftrw-plugin-yarpc 38 // @generated 39 40 package <.Name> 41 42 <range $key, $val := .Types> 43 <if (isException $val)> 44 <$yarpcerrors := import "go.uber.org/yarpc/yarpcerrors" -> 45 // YARPCErrorCode returns <if isSetYARPCCode .Annotations>a <getYARPCErrorCode .><else>nil<end> for <$val.Name>. 46 // 47 // This is derived from the rpc.code annotation on the Thrift exception. 48 func (e *<$val.Name>) YARPCErrorCode() *<$yarpcerrors>.Code { 49 <if isSetYARPCCode .Annotations>code := <getYARPCErrorCode .> 50 return &code 51 <else> 52 return nil 53 <end>} 54 55 // Name is the error name for <$val.Name>. 56 func (e *<$val.Name>) YARPCErrorName() string { return <getYARPCErrorName .> } 57 <end> 58 <end> 59 ` 60 61 var ( 62 _gRPCCodeNameToYARPCErrorCodeType = map[string]string{ 63 // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto 64 "CANCELLED": "yarpcerrors.CodeCancelled", 65 "UNKNOWN": "yarpcerrors.CodeUnknown", 66 "INVALID_ARGUMENT": "yarpcerrors.CodeInvalidArgument", 67 "DEADLINE_EXCEEDED": "yarpcerrors.CodeDeadlineExceeded", 68 "NOT_FOUND": "yarpcerrors.CodeNotFound", 69 "ALREADY_EXISTS": "yarpcerrors.CodeAlreadyExists", 70 "PERMISSION_DENIED": "yarpcerrors.CodePermissionDenied", 71 "RESOURCE_EXHAUSTED": "yarpcerrors.CodeResourceExhausted", 72 "FAILED_PRECONDITION": "yarpcerrors.CodeFailedPrecondition", 73 "ABORTED": "yarpcerrors.CodeAborted", 74 "OUT_OF_RANGE": "yarpcerrors.CodeOutOfRange", 75 "UNIMPLEMENTED": "yarpcerrors.CodeUnimplemented", 76 "INTERNAL": "yarpcerrors.CodeInternal", 77 "UNAVAILABLE": "yarpcerrors.CodeUnavailable", 78 "DATA_LOSS": "yarpcerrors.CodeDataLoss", 79 "UNAUTHENTICATED": "yarpcerrors.CodeUnauthenticated", 80 } 81 82 _availableCodes = fmt.Sprintf(`Available codes: %s`, strings.Join( 83 // Codes are listed below in enum-order, derived from: 84 // - https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto 85 []string{ 86 "CANCELLED", 87 "UNKNOWN", 88 "INVALID_ARGUMENT", 89 "DEADLINE_EXCEEDED", 90 "NOT_FOUND", 91 "ALREADY_EXISTS", 92 "PERMISSION_DENIED", 93 "RESOURCE_EXHAUSTED", 94 "FAILED_PRECONDITION", 95 "ABORTED", 96 "OUT_OF_RANGE", 97 "UNIMPLEMENTED", 98 "INTERNAL", 99 "UNAVAILABLE", 100 "DATA_LOSS", 101 "UNAUTHENTICATED", 102 }, ",")) 103 ) 104 105 func yarpcErrorGenerator(data *moduleTemplateData, files map[string][]byte) error { 106 // kv.thrift => .../kv/types_yarpc.go 107 path := filepath.Join(data.Module.Directory, "types_yarpc.go") 108 109 // Get the original thrift file path 110 thriftFilePath := data.Module.GetThriftFilePath() 111 // and re-compile it. Plugins do not actually have access to the original 112 // thrift file's module, and this is how we gain access to it, since this 113 // generator writes extra methods to exception types defined in the Thrift 114 // file. 115 compiledModule, err := compile.Compile(thriftFilePath) 116 if err != nil { 117 return fmt.Errorf("error compiling the thrift file: %s", err.Error()) 118 } 119 120 templateOptions := append(templateOptions, 121 plugin.TemplateFunc("isException", func(t compile.TypeSpec) bool { 122 if t, ok := t.(*compile.StructSpec); ok { 123 return t.IsExceptionType() 124 } 125 return false 126 }), 127 plugin.TemplateFunc("isSetYARPCCode", func(a compile.Annotations) bool { 128 _, found := a[_errorCodeAnnotationKey] 129 return found 130 }), 131 plugin.TemplateFunc("getYARPCErrorCode", getYARPCErrorCode), 132 plugin.TemplateFunc("getYARPCErrorName", getYARPCErrorName), 133 ) 134 135 files[path], err = plugin.GoFileFromTemplate(path, yarpcerrorTemplate, compiledModule, templateOptions...) 136 return err 137 } 138 139 func getYARPCErrorCode(t *compile.StructSpec) string { 140 errorCodeString := t.Annotations[_errorCodeAnnotationKey] 141 yCode, ok := _gRPCCodeNameToYARPCErrorCodeType[errorCodeString] 142 if !ok { 143 panic(fmt.Sprintf("invalid rpc.code annotation for %q: %q\n%s", t.Name, errorCodeString, _availableCodes)) 144 } 145 return yCode 146 } 147 148 func getYARPCErrorName(t *compile.StructSpec) string { 149 return fmt.Sprintf("%q", t.ThriftName()) 150 }