The code and data behind xeiaso.net
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

cmd: import twirp-openapi-gen, fix some bugs

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso b27ea6b8 4bca5244

+5496 -28
+201
cmd/twirp-openapi-gen/LICENSE
··· 1 + Apache License 2 + Version 2.0, January 2004 3 + http://www.apache.org/licenses/ 4 + 5 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 + 7 + 1. Definitions. 8 + 9 + "License" shall mean the terms and conditions for use, reproduction, 10 + and distribution as defined by Sections 1 through 9 of this document. 11 + 12 + "Licensor" shall mean the copyright owner or entity authorized by 13 + the copyright owner that is granting the License. 14 + 15 + "Legal Entity" shall mean the union of the acting entity and all 16 + other entities that control, are controlled by, or are under common 17 + control with that entity. For the purposes of this definition, 18 + "control" means (i) the power, direct or indirect, to cause the 19 + direction or management of such entity, whether by contract or 20 + otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 + outstanding shares, or (iii) beneficial ownership of such entity. 22 + 23 + "You" (or "Your") shall mean an individual or Legal Entity 24 + exercising permissions granted by this License. 25 + 26 + "Source" form shall mean the preferred form for making modifications, 27 + including but not limited to software source code, documentation 28 + source, and configuration files. 29 + 30 + "Object" form shall mean any form resulting from mechanical 31 + transformation or translation of a Source form, including but 32 + not limited to compiled object code, generated documentation, 33 + and conversions to other media types. 34 + 35 + "Work" shall mean the work of authorship, whether in Source or 36 + Object form, made available under the License, as indicated by a 37 + copyright notice that is included in or attached to the work 38 + (an example is provided in the Appendix below). 39 + 40 + "Derivative Works" shall mean any work, whether in Source or Object 41 + form, that is based on (or derived from) the Work and for which the 42 + editorial revisions, annotations, elaborations, or other modifications 43 + represent, as a whole, an original work of authorship. For the purposes 44 + of this License, Derivative Works shall not include works that remain 45 + separable from, or merely link (or bind by name) to the interfaces of, 46 + the Work and Derivative Works thereof. 47 + 48 + "Contribution" shall mean any work of authorship, including 49 + the original version of the Work and any modifications or additions 50 + to that Work or Derivative Works thereof, that is intentionally 51 + submitted to Licensor for inclusion in the Work by the copyright owner 52 + or by an individual or Legal Entity authorized to submit on behalf of 53 + the copyright owner. For the purposes of this definition, "submitted" 54 + means any form of electronic, verbal, or written communication sent 55 + to the Licensor or its representatives, including but not limited to 56 + communication on electronic mailing lists, source code control systems, 57 + and issue tracking systems that are managed by, or on behalf of, the 58 + Licensor for the purpose of discussing and improving the Work, but 59 + excluding communication that is conspicuously marked or otherwise 60 + designated in writing by the copyright owner as "Not a Contribution." 61 + 62 + "Contributor" shall mean Licensor and any individual or Legal Entity 63 + on behalf of whom a Contribution has been received by Licensor and 64 + subsequently incorporated within the Work. 65 + 66 + 2. Grant of Copyright License. Subject to the terms and conditions of 67 + this License, each Contributor hereby grants to You a perpetual, 68 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 + copyright license to reproduce, prepare Derivative Works of, 70 + publicly display, publicly perform, sublicense, and distribute the 71 + Work and such Derivative Works in Source or Object form. 72 + 73 + 3. Grant of Patent License. Subject to the terms and conditions of 74 + this License, each Contributor hereby grants to You a perpetual, 75 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 + (except as stated in this section) patent license to make, have made, 77 + use, offer to sell, sell, import, and otherwise transfer the Work, 78 + where such license applies only to those patent claims licensable 79 + by such Contributor that are necessarily infringed by their 80 + Contribution(s) alone or by combination of their Contribution(s) 81 + with the Work to which such Contribution(s) was submitted. If You 82 + institute patent litigation against any entity (including a 83 + cross-claim or counterclaim in a lawsuit) alleging that the Work 84 + or a Contribution incorporated within the Work constitutes direct 85 + or contributory patent infringement, then any patent licenses 86 + granted to You under this License for that Work shall terminate 87 + as of the date such litigation is filed. 88 + 89 + 4. Redistribution. You may reproduce and distribute copies of the 90 + Work or Derivative Works thereof in any medium, with or without 91 + modifications, and in Source or Object form, provided that You 92 + meet the following conditions: 93 + 94 + (a) You must give any other recipients of the Work or 95 + Derivative Works a copy of this License; and 96 + 97 + (b) You must cause any modified files to carry prominent notices 98 + stating that You changed the files; and 99 + 100 + (c) You must retain, in the Source form of any Derivative Works 101 + that You distribute, all copyright, patent, trademark, and 102 + attribution notices from the Source form of the Work, 103 + excluding those notices that do not pertain to any part of 104 + the Derivative Works; and 105 + 106 + (d) If the Work includes a "NOTICE" text file as part of its 107 + distribution, then any Derivative Works that You distribute must 108 + include a readable copy of the attribution notices contained 109 + within such NOTICE file, excluding those notices that do not 110 + pertain to any part of the Derivative Works, in at least one 111 + of the following places: within a NOTICE text file distributed 112 + as part of the Derivative Works; within the Source form or 113 + documentation, if provided along with the Derivative Works; or, 114 + within a display generated by the Derivative Works, if and 115 + wherever such third-party notices normally appear. The contents 116 + of the NOTICE file are for informational purposes only and 117 + do not modify the License. You may add Your own attribution 118 + notices within Derivative Works that You distribute, alongside 119 + or as an addendum to the NOTICE text from the Work, provided 120 + that such additional attribution notices cannot be construed 121 + as modifying the License. 122 + 123 + You may add Your own copyright statement to Your modifications and 124 + may provide additional or different license terms and conditions 125 + for use, reproduction, or distribution of Your modifications, or 126 + for any such Derivative Works as a whole, provided Your use, 127 + reproduction, and distribution of the Work otherwise complies with 128 + the conditions stated in this License. 129 + 130 + 5. Submission of Contributions. Unless You explicitly state otherwise, 131 + any Contribution intentionally submitted for inclusion in the Work 132 + by You to the Licensor shall be under the terms and conditions of 133 + this License, without any additional terms or conditions. 134 + Notwithstanding the above, nothing herein shall supersede or modify 135 + the terms of any separate license agreement you may have executed 136 + with Licensor regarding such Contributions. 137 + 138 + 6. Trademarks. This License does not grant permission to use the trade 139 + names, trademarks, service marks, or product names of the Licensor, 140 + except as required for reasonable and customary use in describing the 141 + origin of the Work and reproducing the content of the NOTICE file. 142 + 143 + 7. Disclaimer of Warranty. Unless required by applicable law or 144 + agreed to in writing, Licensor provides the Work (and each 145 + Contributor provides its Contributions) on an "AS IS" BASIS, 146 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 + implied, including, without limitation, any warranties or conditions 148 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 + PARTICULAR PURPOSE. You are solely responsible for determining the 150 + appropriateness of using or redistributing the Work and assume any 151 + risks associated with Your exercise of permissions under this License. 152 + 153 + 8. Limitation of Liability. In no event and under no legal theory, 154 + whether in tort (including negligence), contract, or otherwise, 155 + unless required by applicable law (such as deliberate and grossly 156 + negligent acts) or agreed to in writing, shall any Contributor be 157 + liable to You for damages, including any direct, indirect, special, 158 + incidental, or consequential damages of any character arising as a 159 + result of this License or out of the use or inability to use the 160 + Work (including but not limited to damages for loss of goodwill, 161 + work stoppage, computer failure or malfunction, or any and all 162 + other commercial damages or losses), even if such Contributor 163 + has been advised of the possibility of such damages. 164 + 165 + 9. Accepting Warranty or Additional Liability. While redistributing 166 + the Work or Derivative Works thereof, You may choose to offer, 167 + and charge a fee for, acceptance of support, warranty, indemnity, 168 + or other liability obligations and/or rights consistent with this 169 + License. However, in accepting such obligations, You may act only 170 + on Your own behalf and on Your sole responsibility, not on behalf 171 + of any other Contributor, and only if You agree to indemnify, 172 + defend, and hold each Contributor harmless for any liability 173 + incurred by, or claims asserted against, such Contributor by reason 174 + of your accepting any such warranty or additional liability. 175 + 176 + END OF TERMS AND CONDITIONS 177 + 178 + APPENDIX: How to apply the Apache License to your work. 179 + 180 + To apply the Apache License to your work, attach the following 181 + boilerplate notice, with the fields enclosed by brackets "[]" 182 + replaced with your own identifying information. (Don't include 183 + the brackets!) The text should be enclosed in the appropriate 184 + comment syntax for the file format. We also recommend that a 185 + file or class name and description of purpose be included on the 186 + same "printed page" as the copyright notice for easier 187 + identification within third-party archives. 188 + 189 + Copyright [yyyy] [name of copyright owner] 190 + 191 + Licensed under the Apache License, Version 2.0 (the "License"); 192 + you may not use this file except in compliance with the License. 193 + You may obtain a copy of the License at 194 + 195 + http://www.apache.org/licenses/LICENSE-2.0 196 + 197 + Unless required by applicable law or agreed to in writing, software 198 + distributed under the License is distributed on an "AS IS" BASIS, 199 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 + See the License for the specific language governing permissions and 201 + limitations under the License.
+3
cmd/twirp-openapi-gen/README.md
··· 1 + # twirp-openapi-gen 2 + 3 + This was imported from [github.com/blockthrough/twirp-openapi-gen](https://github.com/blockthrough/twirp-openapi-gen) and a few non-upstreamable changes were made. These changes are made in order to meet my needs in particular. If you want to use this tool, I recommend you to use the original repository.
+85
cmd/twirp-openapi-gen/internal/generator/aliases.go
··· 1 + package generator 2 + 3 + var typeAliases = map[string]struct { 4 + Type, Format string 5 + }{ 6 + // proto numeric types 7 + "int32": {Type: "integer", Format: "int32"}, 8 + "uint32": {Type: "integer", Format: "uint32"}, 9 + "sint32": {Type: "integer", Format: "int32"}, 10 + "fixed32": {Type: "integer", Format: "int32"}, 11 + "sfixed32": {Type: "integer", Format: "int32"}, 12 + 13 + // proto numeric types, 64bit 14 + "int64": {Type: "string", Format: "int64"}, 15 + "uint64": {Type: "string", Format: "uint64"}, 16 + "sint64": {Type: "string", Format: "int64"}, 17 + "fixed64": {Type: "string", Format: "int64"}, 18 + "sfixed64": {Type: "string", Format: "int64"}, 19 + 20 + "double": {Type: "number", Format: "double"}, 21 + "float": {Type: "number", Format: "float"}, 22 + 23 + // effectively copies google.protobuf.BytesValue 24 + "bytes": { 25 + Type: "string", 26 + Format: "byte", 27 + }, 28 + 29 + // It is what it is 30 + "bool": { 31 + Type: "boolean", 32 + Format: "boolean", 33 + }, 34 + 35 + "google.protobuf.Timestamp": { 36 + Type: "string", 37 + Format: "date-time", 38 + }, 39 + "google.protobuf.Duration": { 40 + Type: "string", 41 + }, 42 + "google.protobuf.StringValue": { 43 + Type: "string", 44 + }, 45 + "google.protobuf.BytesValue": { 46 + Type: "string", 47 + Format: "byte", 48 + }, 49 + "google.protobuf.Int32Value": { 50 + Type: "integer", 51 + Format: "int32", 52 + }, 53 + "google.protobuf.UInt32Value": { 54 + Type: "integer", 55 + Format: "uint32", 56 + }, 57 + "google.protobuf.Int64Value": { 58 + Type: "string", 59 + Format: "int64", 60 + }, 61 + "google.protobuf.UInt64Value": { 62 + Type: "string", 63 + Format: "uint64", 64 + }, 65 + "google.protobuf.FloatValue": { 66 + Type: "number", 67 + Format: "float", 68 + }, 69 + "google.protobuf.DoubleValue": { 70 + Type: "number", 71 + Format: "double", 72 + }, 73 + "google.protobuf.BoolValue": { 74 + Type: "boolean", 75 + Format: "boolean", 76 + }, 77 + "google.protobuf.Empty": { 78 + Type: "object", 79 + }, 80 + 81 + "google.type.DateTime": { 82 + Type: "string", 83 + Format: "date-time", 84 + }, 85 + }
+187
cmd/twirp-openapi-gen/internal/generator/generator.go
··· 1 + package generator 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "log/slog" 7 + "os" 8 + "path/filepath" 9 + 10 + "github.com/emicklei/proto" 11 + "github.com/getkin/kin-openapi/openapi3" 12 + "github.com/invopop/yaml" 13 + ) 14 + 15 + type generatorConfig struct { 16 + protoPaths []string 17 + servers []string 18 + title string 19 + docVersion string 20 + pathPrefix string 21 + format string 22 + } 23 + 24 + type Option func(config *generatorConfig) error 25 + 26 + func ProtoPaths(paths []string) Option { 27 + return func(config *generatorConfig) error { 28 + config.protoPaths = paths 29 + return nil 30 + } 31 + } 32 + 33 + func Servers(servers []string) Option { 34 + return func(config *generatorConfig) error { 35 + config.servers = servers 36 + return nil 37 + } 38 + } 39 + 40 + func Title(title string) Option { 41 + return func(config *generatorConfig) error { 42 + config.title = title 43 + return nil 44 + } 45 + } 46 + 47 + func DocVersion(version string) Option { 48 + return func(config *generatorConfig) error { 49 + config.docVersion = version 50 + return nil 51 + } 52 + } 53 + 54 + func PathPrefix(pathPrefix string) Option { 55 + return func(config *generatorConfig) error { 56 + config.pathPrefix = pathPrefix 57 + return nil 58 + } 59 + } 60 + 61 + func Format(format string) Option { 62 + return func(config *generatorConfig) error { 63 + config.format = format 64 + return nil 65 + } 66 + } 67 + 68 + type generator struct { 69 + openAPIV3 *openapi3.T 70 + 71 + conf *generatorConfig 72 + inputFiles []string 73 + packageName string 74 + 75 + importedFiles map[string]struct{} 76 + } 77 + 78 + func NewGenerator(inputFiles []string, options ...Option) (*generator, error) { 79 + conf := generatorConfig{} 80 + for _, opt := range options { 81 + if err := opt(&conf); err != nil { 82 + return nil, err 83 + } 84 + } 85 + 86 + if len(inputFiles) < 1 { 87 + return nil, fmt.Errorf("missing input files") 88 + } 89 + 90 + openAPIV3 := openapi3.T{ 91 + OpenAPI: "3.0.0", 92 + Info: &openapi3.Info{ 93 + Title: conf.title, 94 + Version: conf.docVersion, 95 + }, 96 + Paths: openapi3.Paths{}, 97 + Components: &openapi3.Components{ 98 + Schemas: map[string]*openapi3.SchemaRef{}, 99 + }, 100 + } 101 + 102 + for _, server := range conf.servers { 103 + openAPIV3.Servers = append(openAPIV3.Servers, &openapi3.Server{URL: server}) 104 + } 105 + 106 + slog.Debug("generating doc", "format", conf.format, "inputFiles", inputFiles) 107 + 108 + return &generator{ 109 + inputFiles: inputFiles, 110 + openAPIV3: &openAPIV3, 111 + conf: &conf, 112 + importedFiles: map[string]struct{}{}, 113 + }, nil 114 + } 115 + 116 + func (gen *generator) Generate(filename string) error { 117 + if _, err := gen.Parse(); err != nil { 118 + return err 119 + } 120 + 121 + if err := gen.Save(filename); err != nil { 122 + return err 123 + } 124 + 125 + return nil 126 + } 127 + 128 + func (gen *generator) Parse() (*openapi3.T, error) { 129 + for _, filename := range gen.inputFiles { 130 + protoFile, err := readProtoFile(filename, gen.conf.protoPaths) 131 + if err != nil { 132 + return nil, fmt.Errorf("readProtoFile: %w", err) 133 + } 134 + proto.Walk(protoFile, gen.Handlers()...) 135 + } 136 + 137 + slog.Debug("generated", "paths", len(gen.openAPIV3.Paths), "components", len(gen.openAPIV3.Components.Schemas)) 138 + return gen.openAPIV3, nil 139 + } 140 + 141 + func (gen *generator) Save(filename string) error { 142 + var by []byte 143 + var err error 144 + switch gen.conf.format { 145 + case "json": 146 + by, err = gen.JSON() 147 + case "yaml", "yml": 148 + by, err = gen.YAML() 149 + default: 150 + return fmt.Errorf("missing format") 151 + } 152 + if err != nil { 153 + return err 154 + } 155 + 156 + return os.WriteFile(filename, by, os.ModePerm^0111) 157 + } 158 + 159 + func (gen *generator) JSON() ([]byte, error) { 160 + return json.MarshalIndent(gen.openAPIV3, "", " ") 161 + } 162 + 163 + func (gen *generator) YAML() ([]byte, error) { 164 + return yaml.Marshal(gen.openAPIV3) 165 + } 166 + 167 + func readProtoFile(filename string, protoPaths []string) (*proto.Proto, error) { 168 + var file *os.File 169 + var err error 170 + for _, path := range append(protoPaths, "") { 171 + file, err = os.Open(filepath.Join(path, filename)) 172 + if err != nil { 173 + if os.IsNotExist(err) { 174 + continue 175 + } 176 + return nil, fmt.Errorf("Open: %w", err) 177 + } 178 + break 179 + } 180 + if file == nil { 181 + return nil, fmt.Errorf("could not read file %q", filename) 182 + } 183 + defer file.Close() 184 + 185 + parser := proto.NewParser(file) 186 + return parser.Parse() 187 + }
+317
cmd/twirp-openapi-gen/internal/generator/generator_test.go
··· 1 + package generator 2 + 3 + import ( 4 + "flag" 5 + "log/slog" 6 + "os" 7 + "strings" 8 + "testing" 9 + ) 10 + 11 + type ProtoRPC struct { 12 + name string 13 + input string 14 + output string 15 + desc string 16 + } 17 + 18 + type ProtoMessage struct { 19 + name string 20 + fields []ProtoField 21 + } 22 + 23 + type ProtoField struct { 24 + name string 25 + fieldType string 26 + format string 27 + desc string 28 + enums []string 29 + ref string 30 + itemsRef string 31 + itemsType string 32 + } 33 + 34 + var ( 35 + verbose = flag.Bool("slog.verbose", false, "print debug logs to the console") 36 + ) 37 + 38 + func init() { 39 + if *verbose { 40 + h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ 41 + AddSource: true, 42 + Level: slog.LevelDebug, 43 + }) 44 + slog.SetDefault(slog.New(h)) 45 + } else { 46 + slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ 47 + AddSource: true, 48 + }))) 49 + } 50 + } 51 + 52 + func TestGenerator(t *testing.T) { 53 + flag.Parse() 54 + 55 + opts := []Option{ 56 + ProtoPaths([]string{"./testdata/paymentapis", "./testdata/petapis"}), 57 + Servers([]string{"https://example.com"}), 58 + Title("Test"), 59 + DocVersion("0.1"), 60 + Format("json"), 61 + } 62 + gen, err := NewGenerator([]string{"./testdata/petapis/pet/v1/pet.proto"}, opts...) 63 + if err != nil { 64 + t.Fatal(err) 65 + } 66 + openAPI, err := gen.Parse() 67 + if err != nil { 68 + t.Fatal(err) 69 + } 70 + 71 + if err := gen.Save("./testdata/doc.json"); err != nil { 72 + t.Fatal(err) 73 + } 74 + 75 + pkgName := "pet.v1" 76 + serviceName := "PetStoreService" 77 + rpcs := []ProtoRPC{ 78 + { 79 + name: "GetPet", 80 + input: "GetPetRequest", 81 + output: "GetPetResponse", 82 + }, 83 + } 84 + messages := []ProtoMessage{ 85 + { 86 + name: "GetPetRequest", 87 + fields: []ProtoField{ 88 + { 89 + name: "pet_id", 90 + fieldType: "string", 91 + }, 92 + }, 93 + }, 94 + { 95 + name: "Pet", 96 + fields: []ProtoField{ 97 + { 98 + name: "pet_type", 99 + fieldType: "object", 100 + ref: "#/components/schemas/pet.v1.PetType", 101 + enums: []string{ 102 + "PET_TYPE_UNSPECIFIED", 103 + "PET_TYPE_CAT", 104 + "PET_TYPE_DOG", 105 + "PET_TYPE_SNAKE", 106 + "PET_TYPE_HAMSTER", 107 + }, 108 + }, 109 + { 110 + name: "pet_types", 111 + fieldType: "array", 112 + itemsRef: "#/components/schemas/pet.v1.PetType", 113 + }, 114 + { 115 + name: "tags", 116 + fieldType: "array", 117 + itemsType: "string", 118 + }, 119 + { 120 + name: "pet_id", 121 + fieldType: "string", 122 + desc: "pet_id is an auto-generated id for the pet\\nthe id uniquely identifies a pet in the system", 123 + }, 124 + { 125 + name: "name", 126 + fieldType: "string", 127 + }, 128 + { 129 + name: "created_at", 130 + fieldType: "string", 131 + format: "date-time", 132 + }, 133 + { 134 + name: "vet", 135 + fieldType: "object", 136 + ref: "#/components/schemas/pet.v1.Vet", 137 + }, 138 + { 139 + name: "vets", 140 + fieldType: "array", 141 + itemsRef: "#/components/schemas/pet.v1.Vet", 142 + itemsType: "object", 143 + }, 144 + }, 145 + }, 146 + } 147 + 148 + t.Run("RPC", func(t *testing.T) { 149 + for _, rpc := range rpcs { 150 + pathName := "/" + pkgName + "." + serviceName + "/" + rpc.name 151 + path, ok := openAPI.Paths[pathName] 152 + if !ok { 153 + t.Errorf("%s: missing rpc %q", pathName, rpc.name) 154 + } 155 + 156 + if path.Description != rpc.desc { 157 + t.Errorf("%s: expected desc %q but got %q", pathName, rpc.desc, path.Description) 158 + } 159 + 160 + post := path.Post 161 + if post == nil { 162 + t.Errorf("%s: missing post", pathName) 163 + continue 164 + } 165 + 166 + if post.Summary != rpc.name { 167 + t.Errorf("%s: expected summary %q but got %q", pathName, rpc.name, post.Summary) 168 + } 169 + 170 + requestBodyRef := post.RequestBody 171 + if requestBodyRef == nil { 172 + t.Errorf("%s: missing request body", pathName) 173 + continue 174 + } 175 + 176 + // request 177 + { 178 + requestBody := requestBodyRef.Value 179 + if requestBody == nil { 180 + t.Errorf("%s: missing request body", pathName) 181 + continue 182 + } 183 + 184 + mediaType, ok := requestBody.Content["application/json"] 185 + if !ok { 186 + t.Errorf("%s: missing content type", pathName) 187 + continue 188 + } 189 + 190 + if mediaType.Schema == nil { 191 + t.Errorf("%s: missing media type schema", pathName) 192 + continue 193 + } 194 + 195 + expectedRef := "#/components/schemas/" + pkgName + "." + rpc.input 196 + if mediaType.Schema.Ref != expectedRef { 197 + t.Errorf("%s: expected ref %q but got %q", pathName, expectedRef, mediaType.Schema.Ref) 198 + } 199 + } 200 + 201 + // response 202 + { 203 + respRef := post.Responses["200"] 204 + if respRef == nil { 205 + t.Errorf("%s: missing resp", pathName) 206 + continue 207 + } 208 + 209 + resp := respRef.Value 210 + if resp == nil { 211 + t.Errorf("%s: missing resp", pathName) 212 + continue 213 + } 214 + 215 + mediaType, ok := resp.Content["application/json"] 216 + if !ok { 217 + t.Errorf("%s: missing content type", pathName) 218 + continue 219 + } 220 + 221 + if mediaType.Schema == nil { 222 + t.Errorf("%s: missing media type schema", pathName) 223 + continue 224 + } 225 + 226 + expectedRef := "#/components/schemas/" + pkgName + "." + rpc.output 227 + if mediaType.Schema.Ref != expectedRef { 228 + t.Errorf("%s: expected ref %q but got %q", pathName, expectedRef, mediaType.Schema.Ref) 229 + } 230 + } 231 + } 232 + }) 233 + 234 + t.Run("Messages", func(*testing.T) { 235 + for _, message := range messages { 236 + schemaName := "" + pkgName + "." + message.name 237 + schema, ok := openAPI.Components.Schemas[schemaName] 238 + if !ok { 239 + t.Errorf("%s: missing message %q", schemaName, message.name) 240 + } 241 + if schema.Value == nil { 242 + t.Errorf("%s: missing component", schemaName) 243 + continue 244 + } 245 + properties := schema.Value.Properties 246 + for _, messageField := range message.fields { 247 + propertyRef, ok := properties[messageField.name] 248 + if !ok { 249 + t.Errorf("%s: missing property %q", schemaName, messageField.name) 250 + } 251 + 252 + if propertyRef == nil || propertyRef.Value == nil { 253 + t.Errorf("%s: missing property ref", schemaName) 254 + continue 255 + } 256 + 257 + property := propertyRef.Value 258 + if property.Type != messageField.fieldType { 259 + t.Errorf("%s: %q expected property type %q but got %q", schemaName, message.name, messageField.fieldType, property.Type) 260 + continue 261 + } 262 + 263 + if messageField.format != "" { 264 + if messageField.format != "" && property.Format != messageField.format { 265 + t.Errorf("%s: expected property format %q but got %q", schemaName, messageField.format, property.Format) 266 + continue 267 + } 268 + } 269 + 270 + if propertyRef.Ref != messageField.ref { 271 + t.Errorf("%s: %q expected reference %q but got %q", schemaName, messageField.name, messageField.ref, propertyRef.Ref) 272 + } 273 + 274 + // check the reference schema 275 + if messageField.ref != "" { 276 + refParts := strings.Split(messageField.ref, "/") 277 + // the reference schema has the format of #/components/schemas/<type> so we need to get the last part 278 + schemaRef, ok := openAPI.Components.Schemas[refParts[len(refParts)-1]] 279 + if !ok { 280 + t.Errorf("%s: %q expected reference schema %q but got nil", schemaName, messageField.name, messageField.ref) 281 + } else { 282 + // check if the schema reference has the expected enum values 283 + if len(messageField.enums) > 0 { 284 + if schemaRef.Value.Enum == nil { 285 + t.Errorf("%s: %q expected reference schema enums %q but got nil", schemaName, messageField.name, messageField.ref) 286 + } else { 287 + enums := map[string]struct{}{} 288 + for _, e := range schemaRef.Value.Enum { 289 + enums[e.(string)] = struct{}{} 290 + } 291 + for _, e := range messageField.enums { 292 + if _, ok := enums[e]; !ok { 293 + t.Errorf("%s: %q expected reference schema enum %q to have %q but got nil", schemaName, messageField.name, messageField.ref, e) 294 + } 295 + } 296 + } 297 + } 298 + } 299 + } 300 + 301 + if property.Type == "array" { 302 + if property.Items == nil || property.Items.Value == nil { 303 + t.Errorf("%s: missing property enum array items", schemaName) 304 + } 305 + // only check the array items type if it's not a reference 306 + if messageField.itemsRef == "" && (property.Items.Value.Type != messageField.itemsType) { 307 + t.Errorf("%s: expected %s items type %q but got %q", schemaName, messageField.name, messageField.itemsType, property.Items.Value.Type) 308 + } 309 + // check the array items reference schema 310 + if property.Items.Ref != messageField.itemsRef { 311 + t.Errorf("%s: expected %s items ref %q but got %q", schemaName, messageField.name, messageField.itemsRef, property.Items.Ref) 312 + } 313 + } 314 + } 315 + } 316 + }) 317 + }
+562
cmd/twirp-openapi-gen/internal/generator/handlers.go
··· 1 + package generator 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "log" 7 + "log/slog" 8 + "path/filepath" 9 + "strings" 10 + "unicode" 11 + 12 + "github.com/emicklei/proto" 13 + "github.com/getkin/kin-openapi/openapi3" 14 + ) 15 + 16 + const ( 17 + googleAnyType = "google.protobuf.Any" 18 + googleListValueType = "google.protobuf.ListValue" 19 + googleStructType = "google.protobuf.Struct" 20 + googleValueType = "google.protobuf.Value" 21 + 22 + googleMoneyType = "google.type.Money" 23 + ) 24 + 25 + var ( 26 + successDescription = "Success" 27 + ) 28 + 29 + func (gen *generator) Handlers() []proto.Handler { 30 + return []proto.Handler{ 31 + proto.WithPackage(gen.Package), 32 + proto.WithImport(gen.Import), 33 + proto.WithRPC(gen.RPC), 34 + proto.WithEnum(gen.Enum), 35 + proto.WithMessage(gen.Message), 36 + } 37 + } 38 + 39 + func (gen *generator) Package(pkg *proto.Package) { 40 + slog.Debug("Package handler", "package", pkg.Name) 41 + gen.packageName = pkg.Name 42 + } 43 + 44 + func (gen *generator) Import(i *proto.Import) { 45 + slog.Debug("Import handler", "package", gen.packageName, "filename", i.Filename) 46 + 47 + if _, ok := gen.importedFiles[i.Filename]; ok { 48 + return 49 + } 50 + gen.importedFiles[i.Filename] = struct{}{} 51 + 52 + // Instead of loading and generating the OpenAPI docs for the google proto definitions, 53 + // its known types are mapped to OpenAPI types; see aliases.go. 54 + if strings.Contains(i.Filename, "google/") { 55 + return 56 + } 57 + 58 + protoFile, err := readProtoFile(i.Filename, gen.conf.protoPaths) 59 + if err != nil { 60 + slog.Error("could not import file", "filename", i.Filename, "error", err) 61 + return 62 + } 63 + 64 + oldPackageName := gen.packageName 65 + 66 + // Override the package name for the next round of Walk calls to preserve the types full import path 67 + withPackage := func(pkg *proto.Package) { 68 + gen.packageName = pkg.Name 69 + } 70 + 71 + // additional files walked for messages and imports only 72 + proto.Walk(protoFile, 73 + proto.WithPackage(withPackage), 74 + proto.WithImport(gen.Import), 75 + proto.WithRPC(gen.RPC), 76 + proto.WithEnum(gen.Enum), 77 + proto.WithMessage(gen.Message), 78 + ) 79 + 80 + gen.packageName = oldPackageName 81 + } 82 + 83 + func (gen *generator) RPC(rpc *proto.RPC) { 84 + slog.Debug("RPC handler", "package", gen.packageName, "rpc", rpc.Name, "requestType", rpc.RequestType, "returnsType", rpc.ReturnsType) 85 + 86 + parent, ok := rpc.Parent.(*proto.Service) 87 + if !ok { 88 + log.Panicf("parent is not proto.service") 89 + } 90 + pathName := filepath.Join("/"+gen.conf.pathPrefix+"/", gen.packageName+"."+parent.Name, rpc.Name) 91 + 92 + var reqMediaType *openapi3.MediaType 93 + switch rpc.RequestType { 94 + case "google.protobuf.Empty": 95 + reqMediaType = openapi3.NewMediaType() 96 + default: 97 + if strings.Contains(rpc.RequestType, ".") { 98 + reqMediaType = &openapi3.MediaType{ 99 + Schema: &openapi3.SchemaRef{ 100 + Ref: fmt.Sprintf("#/components/schemas/%s", rpc.RequestType), 101 + }, 102 + } 103 + } else { 104 + reqMediaType = &openapi3.MediaType{ 105 + Schema: &openapi3.SchemaRef{ 106 + Ref: fmt.Sprintf("#/components/schemas/%s.%s", gen.packageName, rpc.RequestType), 107 + }, 108 + } 109 + } 110 + } 111 + 112 + var resMediaType *openapi3.MediaType 113 + switch rpc.ReturnsType { 114 + case "google.protobuf.Empty": 115 + resMediaType = openapi3.NewMediaType() 116 + default: 117 + if strings.Contains(rpc.ReturnsType, ".") { 118 + resMediaType = &openapi3.MediaType{ 119 + Schema: &openapi3.SchemaRef{ 120 + Ref: fmt.Sprintf("#/components/schemas/%s", rpc.ReturnsType), 121 + }, 122 + } 123 + } else { 124 + resMediaType = &openapi3.MediaType{ 125 + Schema: &openapi3.SchemaRef{ 126 + Ref: fmt.Sprintf("#/components/schemas/%s.%s", gen.packageName, rpc.ReturnsType), 127 + }, 128 + } 129 + } 130 + } 131 + 132 + // NOTE: Redocly does not read the "examples" (plural) field, only the "example" (singular) one. 133 + commentMsg, reqExamples, resExamples, err := parseComment(rpc.Comment) 134 + if err != nil { 135 + // TODO(dm): how can we surface the errors from the parser instead of panicking? 136 + log.Panicf("failed to parse comment %s ", err) 137 + } 138 + 139 + if len(reqExamples) > 0 { 140 + exampleObj := make(map[string]interface{}) 141 + for i, example := range reqExamples { 142 + exampleObj[fmt.Sprintf("example %d", i)] = example 143 + } 144 + reqMediaType.Example = exampleObj 145 + } 146 + if len(resExamples) > 0 { 147 + exampleObj := make(map[string]interface{}) 148 + for i, example := range resExamples { 149 + exampleObj[fmt.Sprintf("example %d", i)] = example 150 + } 151 + resMediaType.Example = exampleObj 152 + } 153 + 154 + gen.openAPIV3.Paths[pathName] = &openapi3.PathItem{ 155 + Post: &openapi3.Operation{ 156 + Description: commentMsg, 157 + Summary: rpc.Name, 158 + RequestBody: &openapi3.RequestBodyRef{ 159 + Value: &openapi3.RequestBody{ 160 + Content: openapi3.Content{"application/json": reqMediaType}, 161 + }, 162 + }, 163 + Responses: map[string]*openapi3.ResponseRef{ 164 + "200": { 165 + Value: &openapi3.Response{ 166 + Description: &successDescription, 167 + Content: openapi3.Content{"application/json": resMediaType}, 168 + }, 169 + }, 170 + }, 171 + }, 172 + } 173 + } 174 + 175 + func (gen *generator) Enum(enum *proto.Enum) { 176 + slog.Debug("Enum handler", "package", gen.packageName, "enum", enum.Name) 177 + values := []interface{}{} 178 + for _, element := range enum.Elements { 179 + enumField := element.(*proto.EnumField) 180 + values = append(values, enumField.Name) 181 + } 182 + 183 + gen.openAPIV3.Components.Schemas[gen.packageName+"."+enum.Name] = &openapi3.SchemaRef{ 184 + Value: &openapi3.Schema{ 185 + Description: description(enum.Comment), 186 + Type: "string", 187 + Enum: values, 188 + }, 189 + } 190 + } 191 + 192 + func (gen *generator) Message(msg *proto.Message) { 193 + slog.Debug("Message handler", "package", gen.packageName, "message", msg.Name) 194 + 195 + schemaProps := openapi3.Schemas{} 196 + 197 + for _, element := range msg.Elements { 198 + switch val := element.(type) { 199 + case *proto.Message: 200 + //logger.logd("proto.Message") 201 + gen.Message(val) 202 + case *proto.Comment: 203 + //logger.logd("proto.Comment") 204 + case *proto.Oneof: 205 + //logger.logd("proto.Oneof") 206 + case *proto.OneOfField: 207 + //logger.logd("proto.OneOfField") 208 + gen.addField(schemaProps, val.Field, false) 209 + case *proto.MapField: 210 + //logger.logd("proto.MapField") 211 + gen.addField(schemaProps, val.Field, false) 212 + case *proto.NormalField: 213 + //logger.logd("proto.NormalField %q %q", val.Field.Type, val.Field.Name) 214 + gen.addField(schemaProps, val.Field, val.Repeated) 215 + default: 216 + slog.Error("unknown field type", "type", fmt.Sprintf("%T", element)) 217 + } 218 + } 219 + 220 + gen.openAPIV3.Components.Schemas[gen.packageName+"."+msg.Name] = &openapi3.SchemaRef{ 221 + Value: &openapi3.Schema{ 222 + Description: description(msg.Comment), 223 + Type: "object", 224 + Properties: schemaProps, 225 + }, 226 + } 227 + } 228 + 229 + func (gen *generator) addField(schemaPropsV3 openapi3.Schemas, field *proto.Field, repeated bool) { 230 + fieldDescription := description(field.Comment) 231 + fieldName := field.Name 232 + fieldType := field.Type 233 + fieldFormat := field.Type 234 + // map proto types to openapi 235 + if p, ok := typeAliases[fieldType]; ok { 236 + fieldType = p.Type 237 + fieldFormat = p.Format 238 + } 239 + 240 + if fieldType == fieldFormat { 241 + fieldFormat = "" 242 + } 243 + 244 + switch fieldType { 245 + // Build the schema for native types that don't need to reference other schemas 246 + // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#data-types 247 + case "boolean", "integer", "number", "string", "object": 248 + fieldSchemaV3 := openapi3.SchemaRef{ 249 + Value: &openapi3.Schema{ 250 + Description: fieldDescription, 251 + Type: fieldType, 252 + Format: fieldFormat, 253 + }, 254 + } 255 + if !repeated { 256 + schemaPropsV3[fieldName] = &fieldSchemaV3 257 + return 258 + } 259 + schemaPropsV3[fieldName] = &openapi3.SchemaRef{ 260 + Value: &openapi3.Schema{ 261 + Description: fieldDescription, 262 + Type: "array", 263 + Format: fieldFormat, 264 + Items: &fieldSchemaV3, 265 + }, 266 + } 267 + return 268 + 269 + // generate the schema for google well known complex types: https://protobuf.dev/reference/protobuf/google.protobuf/#index 270 + case googleAnyType: 271 + slog.Debug("any", "name", fieldName, "type", fieldType, "format", fieldFormat) 272 + gen.addGoogleAnySchema() 273 + case googleListValueType: 274 + slog.Debug("ListValue", "name", fieldName, "type", fieldType, "format", fieldFormat) 275 + gen.addGoogleListValueSchema() 276 + case googleStructType: 277 + slog.Debug("Struct", "name", fieldName, "type", fieldType, "format", fieldFormat) 278 + gen.addGoogleValueSchema() // struct depends on value 279 + gen.addGoogleStructSchema() 280 + case googleValueType: 281 + slog.Debug("Value", "name", fieldName, "type", fieldType, "format", fieldFormat) 282 + gen.addGoogleValueSchema() 283 + case googleMoneyType: 284 + slog.Debug("Money", "name", fieldName, "type", fieldType, "format", fieldFormat) 285 + gen.addGoogleMoneySchema() 286 + default: 287 + slog.Debug("Default", "name", fieldName, "type", fieldType, "format", fieldFormat) 288 + } 289 + 290 + // prefix custom types with the package name 291 + ref := fmt.Sprintf("#/components/schemas/%s", fieldType) 292 + if !strings.Contains(fieldType, ".") { 293 + ref = fmt.Sprintf("#/components/schemas/%s.%s", gen.packageName, fieldType) 294 + } 295 + 296 + if !repeated { 297 + schemaPropsV3[fieldName] = &openapi3.SchemaRef{ 298 + Ref: ref, 299 + Value: &openapi3.Schema{ 300 + Description: fieldDescription, 301 + Type: "object", 302 + }, 303 + } 304 + return 305 + } 306 + 307 + schemaPropsV3[fieldName] = &openapi3.SchemaRef{ 308 + Value: &openapi3.Schema{ 309 + Description: fieldDescription, 310 + Type: "array", 311 + Items: &openapi3.SchemaRef{ 312 + Ref: ref, 313 + Value: &openapi3.Schema{ 314 + Type: "object", 315 + }, 316 + }, 317 + }, 318 + } 319 + } 320 + 321 + // addGoogleAnySchema adds a schema item for the google.protobuf.Any type. 322 + func (gen *generator) addGoogleAnySchema() { 323 + if _, ok := gen.openAPIV3.Components.Schemas[googleAnyType]; ok { 324 + return 325 + } 326 + gen.openAPIV3.Components.Schemas[googleAnyType] = &openapi3.SchemaRef{ 327 + Value: &openapi3.Schema{ 328 + Description: ` 329 + The JSON representation of an Any value uses the regular 330 + representation of the deserialized, embedded message, with an 331 + additional field @type which contains the type URL. Example: 332 + 333 + package google.profile; 334 + message Person { 335 + string first_name = 1; 336 + string last_name = 2; 337 + } 338 + 339 + { 340 + "@type": "type.googleapis.com/google.profile.Person", 341 + "firstName": <string>, 342 + "lastName": <string> 343 + } 344 + 345 + If the embedded message type is well-known and has a custom JSON 346 + representation, that representation will be embedded adding a field 347 + value which holds the custom JSON in addition to the @type 348 + field. Example (for message [google.protobuf.Duration][]): 349 + 350 + { 351 + "@type": "type.googleapis.com/google.protobuf.Duration", 352 + "value": "1.212s" 353 + } 354 + `, 355 + Type: "object", 356 + Properties: openapi3.Schemas{ 357 + "@type": &openapi3.SchemaRef{ 358 + Value: &openapi3.Schema{ 359 + Description: "", 360 + Type: "string", 361 + Format: "", 362 + }, 363 + }, 364 + }, 365 + }, 366 + } 367 + } 368 + 369 + // addGoogleAnySchema adds a schema item for the google.protobuf.ListValue type. 370 + func (gen *generator) addGoogleListValueSchema() { 371 + if _, ok := gen.openAPIV3.Components.Schemas[googleListValueType]; ok { 372 + return 373 + } 374 + gen.openAPIV3.Components.Schemas[googleListValueType] = &openapi3.SchemaRef{ 375 + Value: &openapi3.Schema{ 376 + Description: ` 377 + ListValue is a wrapper around a repeated field of values. 378 + The JSON representation for ListValue is JSON array. 379 + `, 380 + Type: "array", 381 + Items: &openapi3.SchemaRef{ 382 + Value: &openapi3.Schema{ 383 + OneOf: openapi3.SchemaRefs{ 384 + &openapi3.SchemaRef{ 385 + Value: &openapi3.Schema{ 386 + Type: "string", 387 + }, 388 + }, 389 + &openapi3.SchemaRef{ 390 + Value: &openapi3.Schema{ 391 + Type: "number", 392 + }, 393 + }, 394 + &openapi3.SchemaRef{ 395 + Value: &openapi3.Schema{ 396 + Type: "integer", 397 + }, 398 + }, 399 + &openapi3.SchemaRef{ 400 + Value: &openapi3.Schema{ 401 + Type: "boolean", 402 + }, 403 + }, 404 + &openapi3.SchemaRef{ 405 + Value: &openapi3.Schema{ 406 + Type: "array", 407 + }, 408 + }, 409 + &openapi3.SchemaRef{ 410 + Value: &openapi3.Schema{ 411 + Type: "object", 412 + }, 413 + }, 414 + }, 415 + }, 416 + }, 417 + }, 418 + } 419 + } 420 + 421 + func (gen *generator) addGoogleStructSchema() { 422 + if _, ok := gen.openAPIV3.Components.Schemas[googleStructType]; ok { 423 + return 424 + } 425 + 426 + gen.openAPIV3.Components.Schemas[googleStructType] = &openapi3.SchemaRef{ 427 + Value: &openapi3.Schema{ 428 + Description: ` 429 + Struct represents a structured data value, consisting of fields 430 + which map to dynamically typed values. In some languages, 431 + Struct might be supported by a native representation. For example, 432 + in scripting languages like JS a struct is represented as 433 + an object. The details of that representation are described 434 + together with the proto support for the language. 435 + 436 + The JSON representation for Struct is JSON object. 437 + `, 438 + Type: "object", 439 + Properties: openapi3.Schemas{ 440 + "fields": &openapi3.SchemaRef{ 441 + Value: &openapi3.Schema{ 442 + Description: "Unordered map of dynamically typed values.", 443 + Type: "object", 444 + AdditionalProperties: openapi3.AdditionalProperties{ 445 + Schema: &openapi3.SchemaRef{ 446 + Ref: "#/components/schemas/google.protobuf.Value", 447 + }, 448 + }, 449 + }, 450 + }, 451 + }, 452 + }, 453 + } 454 + } 455 + 456 + func (gen *generator) addGoogleValueSchema() { 457 + if _, ok := gen.openAPIV3.Components.Schemas[googleValueType]; ok { 458 + return 459 + } 460 + 461 + gen.openAPIV3.Components.Schemas[googleValueType] = &openapi3.SchemaRef{ 462 + Value: &openapi3.Schema{ 463 + Description: ` 464 + Value represents a dynamically typed value which can be either 465 + null, a number, a string, a boolean, a recursive struct value, or a 466 + list of values. A producer of value is expected to set one of that 467 + variants, absence of any variant indicates an error. 468 + 469 + The JSON representation for Value is JSON value. 470 + `, 471 + OneOf: openapi3.SchemaRefs{ 472 + &openapi3.SchemaRef{Value: &openapi3.Schema{Type: "string"}}, 473 + &openapi3.SchemaRef{Value: &openapi3.Schema{Type: "number"}}, 474 + &openapi3.SchemaRef{Value: &openapi3.Schema{Type: "integer"}}, 475 + &openapi3.SchemaRef{Value: &openapi3.Schema{Type: "boolean"}}, 476 + &openapi3.SchemaRef{Ref: "#/components/schemas/google.protobuf.Struct"}, 477 + &openapi3.SchemaRef{Ref: "#/components/schemas/google.protobuf.ListValue"}, 478 + }, 479 + }, 480 + } 481 + } 482 + 483 + func (gen *generator) addGoogleMoneySchema() { 484 + if _, ok := gen.openAPIV3.Components.Schemas[googleMoneyType]; ok { 485 + return 486 + } 487 + 488 + gen.openAPIV3.Components.Schemas[googleMoneyType] = &openapi3.SchemaRef{ 489 + Value: &openapi3.Schema{ 490 + Description: `Represents an amount of money with its currency type`, 491 + Type: "object", 492 + Properties: openapi3.Schemas{ 493 + "currency_code": &openapi3.SchemaRef{ 494 + Value: &openapi3.Schema{ 495 + Description: "The 3-letter currency code defined in ISO 4217.", 496 + Type: "string", 497 + }, 498 + }, 499 + "units": &openapi3.SchemaRef{ 500 + Value: &openapi3.Schema{ 501 + Description: "The whole units of the amount.\nFor example if `currencyCode` is `\"USD\"`, then 1 unit is one US dollar.", 502 + Type: "integer", 503 + Format: "int64", 504 + }, 505 + }, 506 + "nanos": &openapi3.SchemaRef{ 507 + Value: &openapi3.Schema{ 508 + Description: "Number of nano (10^-9) units of the amount.\nThe value must be between -999,999,999 and +999,999,999 inclusive.\nIf `units` is positive, `nanos` must be positive or zero.\nIf `units` is zero, `nanos` can be positive, zero, or negative.\nIf `units` is negative, `nanos` must be negative or zero.\nFor example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.", 509 + Type: "integer", 510 + Format: "int32", 511 + }, 512 + }, 513 + }, 514 + }, 515 + } 516 + } 517 + 518 + func description(comment *proto.Comment) string { 519 + if comment == nil { 520 + return "" 521 + } 522 + result := []string{} 523 + for _, line := range comment.Lines { 524 + line = strings.TrimSpace(line) 525 + if len(line) > 0 { 526 + result = append(result, line) 527 + } 528 + } 529 + return strings.Join(result, "\n") 530 + } 531 + 532 + // parseComment parses the comment for an RPC method and returns the description, request examples, and response examples. 533 + // it looks for the labels req-example: and res-example: to extract the JSON payload samples. 534 + func parseComment(comment *proto.Comment) (string, []map[string]interface{}, []map[string]interface{}, error) { 535 + if comment == nil { 536 + return "", nil, nil, nil 537 + } 538 + reqExamples := []map[string]interface{}{} 539 + respExamples := []map[string]interface{}{} 540 + message := "" 541 + for _, line := range comment.Lines { 542 + line = strings.TrimLeftFunc(line, unicode.IsSpace) 543 + if strings.HasPrefix(line, "req-example:") { 544 + parts := strings.Split(line, "req-example:") 545 + example := map[string]interface{}{} 546 + if err := json.Unmarshal([]byte(parts[1]), &example); err != nil { 547 + return "", nil, nil, fmt.Errorf("failed to parse req-example %q: %v", parts[1], err) 548 + } 549 + reqExamples = append(reqExamples, example) 550 + } else if strings.HasPrefix(line, "res-example:") { 551 + parts := strings.Split(line, "res-example:") 552 + example := map[string]interface{}{} 553 + if err := json.Unmarshal([]byte(parts[1]), &example); err != nil { 554 + return "", nil, nil, fmt.Errorf("failed to parse res-example %q: %v", parts[1], err) 555 + } 556 + respExamples = append(respExamples, example) 557 + } else { 558 + message = fmt.Sprintf("%s\n%s", message, line) 559 + } 560 + } 561 + return message, reqExamples, respExamples, nil 562 + }
+405
cmd/twirp-openapi-gen/internal/generator/testdata/doc.json
··· 1 + { 2 + "components": { 3 + "schemas": { 4 + "google.protobuf.Any": { 5 + "description": "\nThe JSON representation of an Any value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field @type which contains the type URL. Example:\n\n\tpackage google.profile;\n\tmessage Person {\n\t string first_name = 1;\n\t string last_name = 2;\n\t}\n\n\t{\n\t \"@type\": \"type.googleapis.com/google.profile.Person\",\n\t \"firstName\": \u003cstring\u003e,\n\t \"lastName\": \u003cstring\u003e\n\t}\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\nvalue which holds the custom JSON in addition to the @type\nfield. Example (for message [google.protobuf.Duration][]):\n\n\t{\n\t \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n\t \"value\": \"1.212s\"\n\t}\n", 6 + "properties": { 7 + "@type": { 8 + "type": "string" 9 + } 10 + }, 11 + "type": "object" 12 + }, 13 + "google.protobuf.ListValue": { 14 + "description": "\nListValue is a wrapper around a repeated field of values.\nThe JSON representation for ListValue is JSON array.\n", 15 + "items": { 16 + "oneOf": [ 17 + { 18 + "type": "string" 19 + }, 20 + { 21 + "type": "number" 22 + }, 23 + { 24 + "type": "integer" 25 + }, 26 + { 27 + "type": "boolean" 28 + }, 29 + { 30 + "type": "array" 31 + }, 32 + { 33 + "type": "object" 34 + } 35 + ] 36 + }, 37 + "type": "array" 38 + }, 39 + "google.protobuf.Struct": { 40 + "description": "\nStruct represents a structured data value, consisting of fields\nwhich map to dynamically typed values. In some languages, \nStruct might be supported by a native representation. For example,\nin scripting languages like JS a struct is represented as\nan object. The details of that representation are described\ntogether with the proto support for the language.\n\nThe JSON representation for Struct is JSON object.\n", 41 + "properties": { 42 + "fields": { 43 + "additionalProperties": { 44 + "$ref": "#/components/schemas/google.protobuf.Value" 45 + }, 46 + "description": "Unordered map of dynamically typed values.", 47 + "type": "object" 48 + } 49 + }, 50 + "type": "object" 51 + }, 52 + "google.protobuf.Value": { 53 + "description": "\nValue represents a dynamically typed value which can be either\nnull, a number, a string, a boolean, a recursive struct value, or a\nlist of values. A producer of value is expected to set one of that\nvariants, absence of any variant indicates an error.\n\t\t\t\t\nThe JSON representation for Value is JSON value.\n", 54 + "oneOf": [ 55 + { 56 + "type": "string" 57 + }, 58 + { 59 + "type": "number" 60 + }, 61 + { 62 + "type": "integer" 63 + }, 64 + { 65 + "type": "boolean" 66 + }, 67 + { 68 + "$ref": "#/components/schemas/google.protobuf.Struct" 69 + }, 70 + { 71 + "$ref": "#/components/schemas/google.protobuf.ListValue" 72 + } 73 + ] 74 + }, 75 + "google.type.Money": { 76 + "description": "Represents an amount of money with its currency type", 77 + "properties": { 78 + "currency_code": { 79 + "description": "The 3-letter currency code defined in ISO 4217.", 80 + "type": "string" 81 + }, 82 + "nanos": { 83 + "description": "Number of nano (10^-9) units of the amount.\nThe value must be between -999,999,999 and +999,999,999 inclusive.\nIf `units` is positive, `nanos` must be positive or zero.\nIf `units` is zero, `nanos` can be positive, zero, or negative.\nIf `units` is negative, `nanos` must be negative or zero.\nFor example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.", 84 + "format": "int32", 85 + "type": "integer" 86 + }, 87 + "units": { 88 + "description": "The whole units of the amount.\nFor example if `currencyCode` is `\"USD\"`, then 1 unit is one US dollar.", 89 + "format": "int64", 90 + "type": "integer" 91 + } 92 + }, 93 + "type": "object" 94 + }, 95 + "payment.v1alpha1.Order": { 96 + "description": "Order represents a monetary order.", 97 + "properties": { 98 + "amount": { 99 + "$ref": "#/components/schemas/google.type.Money" 100 + }, 101 + "order_id": { 102 + "type": "string" 103 + }, 104 + "payment_provider": { 105 + "$ref": "#/components/schemas/payment.v1alpha1.PaymentProvider" 106 + }, 107 + "recipient_id": { 108 + "type": "string" 109 + } 110 + }, 111 + "type": "object" 112 + }, 113 + "payment.v1alpha1.PaymentProvider": { 114 + "description": "PaymentProvider represents the supported set\nof payment providers.", 115 + "enum": [ 116 + "PAYMENT_PROVIDER_UNSPECIFIED", 117 + "PAYMENT_PROVIDER_STRIPE", 118 + "PAYMENT_PROVIDER_PAYPAL", 119 + "PAYMENT_PROVIDER_APPLE" 120 + ], 121 + "type": "string" 122 + }, 123 + "pet.v1.DeletePetRequest": { 124 + "properties": { 125 + "pet_id": { 126 + "type": "string" 127 + } 128 + }, 129 + "type": "object" 130 + }, 131 + "pet.v1.GetPetRequest": { 132 + "description": "GetPetRequest is the request object for GetPet\nThe message accepts a pet id as an input", 133 + "properties": { 134 + "pet_id": { 135 + "type": "string" 136 + } 137 + }, 138 + "type": "object" 139 + }, 140 + "pet.v1.GetPetResponse": { 141 + "properties": { 142 + "pet": { 143 + "$ref": "#/components/schemas/pet.v1.Pet" 144 + } 145 + }, 146 + "type": "object" 147 + }, 148 + "pet.v1.Pet": { 149 + "description": "Pet represents a pet in the pet store.", 150 + "properties": { 151 + "created_at": { 152 + "format": "date-time", 153 + "type": "string" 154 + }, 155 + "details": { 156 + "items": { 157 + "$ref": "#/components/schemas/google.protobuf.Any" 158 + }, 159 + "type": "array" 160 + }, 161 + "labels": { 162 + "$ref": "#/components/schemas/google.protobuf.ListValue" 163 + }, 164 + "metadata": { 165 + "$ref": "#/components/schemas/google.protobuf.Struct" 166 + }, 167 + "name": { 168 + "type": "string" 169 + }, 170 + "payment_provider": { 171 + "$ref": "#/components/schemas/payment.v1alpha1.PaymentProvider" 172 + }, 173 + "pet_id": { 174 + "description": "pet_id is an auto-generated id for the pet\nthe id uniquely identifies a pet in the system", 175 + "type": "string" 176 + }, 177 + "pet_type": { 178 + "$ref": "#/components/schemas/pet.v1.PetType" 179 + }, 180 + "pet_types": { 181 + "items": { 182 + "$ref": "#/components/schemas/pet.v1.PetType" 183 + }, 184 + "type": "array" 185 + }, 186 + "tags": { 187 + "items": { 188 + "type": "string" 189 + }, 190 + "type": "array" 191 + }, 192 + "vet": { 193 + "$ref": "#/components/schemas/pet.v1.Vet" 194 + }, 195 + "vets": { 196 + "items": { 197 + "$ref": "#/components/schemas/pet.v1.Vet" 198 + }, 199 + "type": "array" 200 + } 201 + }, 202 + "type": "object" 203 + }, 204 + "pet.v1.PetType": { 205 + "description": "PetType represents the different types of pets in the pet store.", 206 + "enum": [ 207 + "PET_TYPE_UNSPECIFIED", 208 + "PET_TYPE_CAT", 209 + "PET_TYPE_DOG", 210 + "PET_TYPE_SNAKE", 211 + "PET_TYPE_HAMSTER" 212 + ], 213 + "type": "string" 214 + }, 215 + "pet.v1.PurchasePetRequest": { 216 + "properties": { 217 + "order": { 218 + "$ref": "#/components/schemas/payment.v1alpha1.Order" 219 + }, 220 + "pet_id": { 221 + "type": "string" 222 + } 223 + }, 224 + "type": "object" 225 + }, 226 + "pet.v1.PurchasePetResponse": { 227 + "type": "object" 228 + }, 229 + "pet.v1.PutPetRequest": { 230 + "properties": { 231 + "name": { 232 + "type": "string" 233 + }, 234 + "pet_type": { 235 + "$ref": "#/components/schemas/pet.v1.PetType" 236 + } 237 + }, 238 + "type": "object" 239 + }, 240 + "pet.v1.PutPetResponse": { 241 + "properties": { 242 + "pet": { 243 + "$ref": "#/components/schemas/pet.v1.Pet" 244 + } 245 + }, 246 + "type": "object" 247 + }, 248 + "pet.v1.UpdatePetRequest": { 249 + "properties": { 250 + "metadata": { 251 + "$ref": "#/components/schemas/google.protobuf.Struct" 252 + }, 253 + "pet_id": { 254 + "type": "string" 255 + } 256 + }, 257 + "type": "object" 258 + }, 259 + "pet.v1.UpdatePetResponse": { 260 + "properties": { 261 + "pet": { 262 + "$ref": "#/components/schemas/pet.v1.Pet" 263 + } 264 + }, 265 + "type": "object" 266 + }, 267 + "pet.v1.Vet": { 268 + "properties": { 269 + "name": { 270 + "type": "string" 271 + } 272 + }, 273 + "type": "object" 274 + } 275 + } 276 + }, 277 + "info": { 278 + "title": "Test", 279 + "version": "0.1" 280 + }, 281 + "openapi": "3.0.0", 282 + "paths": { 283 + "/pet.v1.PetStoreService/DeletePet": { 284 + "post": { 285 + "requestBody": { 286 + "content": { 287 + "application/json": { 288 + "schema": { 289 + "$ref": "#/components/schemas/pet.v1.DeletePetRequest" 290 + } 291 + } 292 + } 293 + }, 294 + "responses": { 295 + "200": { 296 + "content": { 297 + "application/json": {} 298 + }, 299 + "description": "Success" 300 + } 301 + }, 302 + "summary": "DeletePet" 303 + } 304 + }, 305 + "/pet.v1.PetStoreService/GetPet": { 306 + "post": { 307 + "description": "\nGetPet returns details about a pet\nIt accepts a pet id as an input and returns back the matching pet object", 308 + "requestBody": { 309 + "content": { 310 + "application/json": { 311 + "example": { 312 + "example 0": { 313 + "pet_id": "123" 314 + }, 315 + "example 1": { 316 + "pet_id": "456" 317 + } 318 + }, 319 + "schema": { 320 + "$ref": "#/components/schemas/pet.v1.GetPetRequest" 321 + } 322 + } 323 + } 324 + }, 325 + "responses": { 326 + "200": { 327 + "content": { 328 + "application/json": { 329 + "example": { 330 + "example 0": { 331 + "pet": { 332 + "name": "toby" 333 + } 334 + } 335 + }, 336 + "schema": { 337 + "$ref": "#/components/schemas/pet.v1.GetPetResponse" 338 + } 339 + } 340 + }, 341 + "description": "Success" 342 + } 343 + }, 344 + "summary": "GetPet" 345 + } 346 + }, 347 + "/pet.v1.PetStoreService/PurchasePet": { 348 + "post": { 349 + "requestBody": { 350 + "content": { 351 + "application/json": { 352 + "schema": { 353 + "$ref": "#/components/schemas/pet.v1.PurchasePetRequest" 354 + } 355 + } 356 + } 357 + }, 358 + "responses": { 359 + "200": { 360 + "content": { 361 + "application/json": { 362 + "schema": { 363 + "$ref": "#/components/schemas/pet.v1.PurchasePetResponse" 364 + } 365 + } 366 + }, 367 + "description": "Success" 368 + } 369 + }, 370 + "summary": "PurchasePet" 371 + } 372 + }, 373 + "/pet.v1.PetStoreService/UpdatePet": { 374 + "post": { 375 + "requestBody": { 376 + "content": { 377 + "application/json": { 378 + "schema": { 379 + "$ref": "#/components/schemas/pet.v1.UpdatePetRequest" 380 + } 381 + } 382 + } 383 + }, 384 + "responses": { 385 + "200": { 386 + "content": { 387 + "application/json": { 388 + "schema": { 389 + "$ref": "#/components/schemas/pet.v1.UpdatePetResponse" 390 + } 391 + } 392 + }, 393 + "description": "Success" 394 + } 395 + }, 396 + "summary": "UpdatePet" 397 + } 398 + } 399 + }, 400 + "servers": [ 401 + { 402 + "url": "https://example.com" 403 + } 404 + ] 405 + }
+263
cmd/twirp-openapi-gen/internal/generator/testdata/gen/go/payment/v1alpha1/payment.pb.go
··· 1 + // Code generated by protoc-gen-go. DO NOT EDIT. 2 + // versions: 3 + // protoc-gen-go v1.31.0 4 + // protoc (unknown) 5 + // source: payment/v1alpha1/payment.proto 6 + 7 + package v1alpha1 8 + 9 + import ( 10 + money "google.golang.org/genproto/googleapis/type/money" 11 + protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 + protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 + reflect "reflect" 14 + sync "sync" 15 + ) 16 + 17 + const ( 18 + // Verify that this generated code is sufficiently up-to-date. 19 + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 + // Verify that runtime/protoimpl is sufficiently up-to-date. 21 + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 + ) 23 + 24 + // PaymentProvider represents the supported set 25 + // of payment providers. 26 + type PaymentProvider int32 27 + 28 + const ( 29 + PaymentProvider_PAYMENT_PROVIDER_UNSPECIFIED PaymentProvider = 0 30 + PaymentProvider_PAYMENT_PROVIDER_STRIPE PaymentProvider = 1 31 + PaymentProvider_PAYMENT_PROVIDER_PAYPAL PaymentProvider = 2 32 + PaymentProvider_PAYMENT_PROVIDER_APPLE PaymentProvider = 3 33 + ) 34 + 35 + // Enum value maps for PaymentProvider. 36 + var ( 37 + PaymentProvider_name = map[int32]string{ 38 + 0: "PAYMENT_PROVIDER_UNSPECIFIED", 39 + 1: "PAYMENT_PROVIDER_STRIPE", 40 + 2: "PAYMENT_PROVIDER_PAYPAL", 41 + 3: "PAYMENT_PROVIDER_APPLE", 42 + } 43 + PaymentProvider_value = map[string]int32{ 44 + "PAYMENT_PROVIDER_UNSPECIFIED": 0, 45 + "PAYMENT_PROVIDER_STRIPE": 1, 46 + "PAYMENT_PROVIDER_PAYPAL": 2, 47 + "PAYMENT_PROVIDER_APPLE": 3, 48 + } 49 + ) 50 + 51 + func (x PaymentProvider) Enum() *PaymentProvider { 52 + p := new(PaymentProvider) 53 + *p = x 54 + return p 55 + } 56 + 57 + func (x PaymentProvider) String() string { 58 + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 59 + } 60 + 61 + func (PaymentProvider) Descriptor() protoreflect.EnumDescriptor { 62 + return file_payment_v1alpha1_payment_proto_enumTypes[0].Descriptor() 63 + } 64 + 65 + func (PaymentProvider) Type() protoreflect.EnumType { 66 + return &file_payment_v1alpha1_payment_proto_enumTypes[0] 67 + } 68 + 69 + func (x PaymentProvider) Number() protoreflect.EnumNumber { 70 + return protoreflect.EnumNumber(x) 71 + } 72 + 73 + // Deprecated: Use PaymentProvider.Descriptor instead. 74 + func (PaymentProvider) EnumDescriptor() ([]byte, []int) { 75 + return file_payment_v1alpha1_payment_proto_rawDescGZIP(), []int{0} 76 + } 77 + 78 + // Order represents a monetary order. 79 + type Order struct { 80 + state protoimpl.MessageState 81 + sizeCache protoimpl.SizeCache 82 + unknownFields protoimpl.UnknownFields 83 + 84 + OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` 85 + RecipientId string `protobuf:"bytes,2,opt,name=recipient_id,json=recipientId,proto3" json:"recipient_id,omitempty"` 86 + Amount *money.Money `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` 87 + PaymentProvider PaymentProvider `protobuf:"varint,4,opt,name=payment_provider,json=paymentProvider,proto3,enum=payment.v1alpha1.PaymentProvider" json:"payment_provider,omitempty"` 88 + } 89 + 90 + func (x *Order) Reset() { 91 + *x = Order{} 92 + if protoimpl.UnsafeEnabled { 93 + mi := &file_payment_v1alpha1_payment_proto_msgTypes[0] 94 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 95 + ms.StoreMessageInfo(mi) 96 + } 97 + } 98 + 99 + func (x *Order) String() string { 100 + return protoimpl.X.MessageStringOf(x) 101 + } 102 + 103 + func (*Order) ProtoMessage() {} 104 + 105 + func (x *Order) ProtoReflect() protoreflect.Message { 106 + mi := &file_payment_v1alpha1_payment_proto_msgTypes[0] 107 + if protoimpl.UnsafeEnabled && x != nil { 108 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 109 + if ms.LoadMessageInfo() == nil { 110 + ms.StoreMessageInfo(mi) 111 + } 112 + return ms 113 + } 114 + return mi.MessageOf(x) 115 + } 116 + 117 + // Deprecated: Use Order.ProtoReflect.Descriptor instead. 118 + func (*Order) Descriptor() ([]byte, []int) { 119 + return file_payment_v1alpha1_payment_proto_rawDescGZIP(), []int{0} 120 + } 121 + 122 + func (x *Order) GetOrderId() string { 123 + if x != nil { 124 + return x.OrderId 125 + } 126 + return "" 127 + } 128 + 129 + func (x *Order) GetRecipientId() string { 130 + if x != nil { 131 + return x.RecipientId 132 + } 133 + return "" 134 + } 135 + 136 + func (x *Order) GetAmount() *money.Money { 137 + if x != nil { 138 + return x.Amount 139 + } 140 + return nil 141 + } 142 + 143 + func (x *Order) GetPaymentProvider() PaymentProvider { 144 + if x != nil { 145 + return x.PaymentProvider 146 + } 147 + return PaymentProvider_PAYMENT_PROVIDER_UNSPECIFIED 148 + } 149 + 150 + var File_payment_v1alpha1_payment_proto protoreflect.FileDescriptor 151 + 152 + var file_payment_v1alpha1_payment_proto_rawDesc = []byte{ 153 + 0x0a, 0x1e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 154 + 0x61, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 155 + 0x12, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 156 + 0x61, 0x31, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 157 + 0x6d, 0x6f, 0x6e, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x01, 0x0a, 0x05, 158 + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 159 + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 160 + 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 161 + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 162 + 0x74, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 163 + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 0x70, 164 + 0x65, 0x2e, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 165 + 0x4c, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 166 + 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x70, 0x61, 0x79, 0x6d, 167 + 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 168 + 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x0f, 0x70, 0x61, 169 + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2a, 0x89, 0x01, 170 + 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 171 + 0x72, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x4f, 172 + 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 173 + 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 174 + 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x50, 0x45, 0x10, 0x01, 175 + 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, 176 + 0x49, 0x44, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x59, 0x50, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 177 + 0x16, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 178 + 0x52, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x45, 0x10, 0x03, 0x42, 0xe4, 0x01, 0x0a, 0x14, 0x63, 0x6f, 179 + 0x6d, 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 180 + 0x61, 0x31, 0x42, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 181 + 0x50, 0x01, 0x5a, 0x5d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 182 + 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x2f, 0x74, 0x77, 0x69, 0x72, 183 + 0x70, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2d, 0x67, 0x65, 0x6e, 0x2f, 0x69, 0x6e, 184 + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 185 + 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 186 + 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 187 + 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x10, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 188 + 0x74, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x10, 0x50, 0x61, 0x79, 189 + 0x6d, 0x65, 0x6e, 0x74, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x1c, 190 + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 191 + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x11, 0x50, 192 + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 193 + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 194 + } 195 + 196 + var ( 197 + file_payment_v1alpha1_payment_proto_rawDescOnce sync.Once 198 + file_payment_v1alpha1_payment_proto_rawDescData = file_payment_v1alpha1_payment_proto_rawDesc 199 + ) 200 + 201 + func file_payment_v1alpha1_payment_proto_rawDescGZIP() []byte { 202 + file_payment_v1alpha1_payment_proto_rawDescOnce.Do(func() { 203 + file_payment_v1alpha1_payment_proto_rawDescData = protoimpl.X.CompressGZIP(file_payment_v1alpha1_payment_proto_rawDescData) 204 + }) 205 + return file_payment_v1alpha1_payment_proto_rawDescData 206 + } 207 + 208 + var file_payment_v1alpha1_payment_proto_enumTypes = make([]protoimpl.EnumInfo, 1) 209 + var file_payment_v1alpha1_payment_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 210 + var file_payment_v1alpha1_payment_proto_goTypes = []interface{}{ 211 + (PaymentProvider)(0), // 0: payment.v1alpha1.PaymentProvider 212 + (*Order)(nil), // 1: payment.v1alpha1.Order 213 + (*money.Money)(nil), // 2: google.type.Money 214 + } 215 + var file_payment_v1alpha1_payment_proto_depIdxs = []int32{ 216 + 2, // 0: payment.v1alpha1.Order.amount:type_name -> google.type.Money 217 + 0, // 1: payment.v1alpha1.Order.payment_provider:type_name -> payment.v1alpha1.PaymentProvider 218 + 2, // [2:2] is the sub-list for method output_type 219 + 2, // [2:2] is the sub-list for method input_type 220 + 2, // [2:2] is the sub-list for extension type_name 221 + 2, // [2:2] is the sub-list for extension extendee 222 + 0, // [0:2] is the sub-list for field type_name 223 + } 224 + 225 + func init() { file_payment_v1alpha1_payment_proto_init() } 226 + func file_payment_v1alpha1_payment_proto_init() { 227 + if File_payment_v1alpha1_payment_proto != nil { 228 + return 229 + } 230 + if !protoimpl.UnsafeEnabled { 231 + file_payment_v1alpha1_payment_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 232 + switch v := v.(*Order); i { 233 + case 0: 234 + return &v.state 235 + case 1: 236 + return &v.sizeCache 237 + case 2: 238 + return &v.unknownFields 239 + default: 240 + return nil 241 + } 242 + } 243 + } 244 + type x struct{} 245 + out := protoimpl.TypeBuilder{ 246 + File: protoimpl.DescBuilder{ 247 + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 248 + RawDescriptor: file_payment_v1alpha1_payment_proto_rawDesc, 249 + NumEnums: 1, 250 + NumMessages: 1, 251 + NumExtensions: 0, 252 + NumServices: 0, 253 + }, 254 + GoTypes: file_payment_v1alpha1_payment_proto_goTypes, 255 + DependencyIndexes: file_payment_v1alpha1_payment_proto_depIdxs, 256 + EnumInfos: file_payment_v1alpha1_payment_proto_enumTypes, 257 + MessageInfos: file_payment_v1alpha1_payment_proto_msgTypes, 258 + }.Build() 259 + File_payment_v1alpha1_payment_proto = out.File 260 + file_payment_v1alpha1_payment_proto_rawDesc = nil 261 + file_payment_v1alpha1_payment_proto_goTypes = nil 262 + file_payment_v1alpha1_payment_proto_depIdxs = nil 263 + }
+896
cmd/twirp-openapi-gen/internal/generator/testdata/gen/go/pet/v1/pet.pb.go
··· 1 + // Code generated by protoc-gen-go. DO NOT EDIT. 2 + // versions: 3 + // protoc-gen-go v1.31.0 4 + // protoc (unknown) 5 + // source: pet/v1/pet.proto 6 + 7 + package v1 8 + 9 + import ( 10 + v1alpha1 "github.com/blockthrough/twirp-openapi-gen/internal/generator/testdata/gen/go/payment/v1alpha1" 11 + datetime "google.golang.org/genproto/googleapis/type/datetime" 12 + protoreflect "google.golang.org/protobuf/reflect/protoreflect" 13 + protoimpl "google.golang.org/protobuf/runtime/protoimpl" 14 + anypb "google.golang.org/protobuf/types/known/anypb" 15 + emptypb "google.golang.org/protobuf/types/known/emptypb" 16 + structpb "google.golang.org/protobuf/types/known/structpb" 17 + _ "google.golang.org/protobuf/types/known/wrapperspb" 18 + reflect "reflect" 19 + sync "sync" 20 + ) 21 + 22 + const ( 23 + // Verify that this generated code is sufficiently up-to-date. 24 + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 25 + // Verify that runtime/protoimpl is sufficiently up-to-date. 26 + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 27 + ) 28 + 29 + // PetType represents the different types of pets in the pet store. 30 + type PetType int32 31 + 32 + const ( 33 + PetType_PET_TYPE_UNSPECIFIED PetType = 0 34 + PetType_PET_TYPE_CAT PetType = 1 35 + PetType_PET_TYPE_DOG PetType = 2 36 + PetType_PET_TYPE_SNAKE PetType = 3 37 + PetType_PET_TYPE_HAMSTER PetType = 4 38 + ) 39 + 40 + // Enum value maps for PetType. 41 + var ( 42 + PetType_name = map[int32]string{ 43 + 0: "PET_TYPE_UNSPECIFIED", 44 + 1: "PET_TYPE_CAT", 45 + 2: "PET_TYPE_DOG", 46 + 3: "PET_TYPE_SNAKE", 47 + 4: "PET_TYPE_HAMSTER", 48 + } 49 + PetType_value = map[string]int32{ 50 + "PET_TYPE_UNSPECIFIED": 0, 51 + "PET_TYPE_CAT": 1, 52 + "PET_TYPE_DOG": 2, 53 + "PET_TYPE_SNAKE": 3, 54 + "PET_TYPE_HAMSTER": 4, 55 + } 56 + ) 57 + 58 + func (x PetType) Enum() *PetType { 59 + p := new(PetType) 60 + *p = x 61 + return p 62 + } 63 + 64 + func (x PetType) String() string { 65 + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 66 + } 67 + 68 + func (PetType) Descriptor() protoreflect.EnumDescriptor { 69 + return file_pet_v1_pet_proto_enumTypes[0].Descriptor() 70 + } 71 + 72 + func (PetType) Type() protoreflect.EnumType { 73 + return &file_pet_v1_pet_proto_enumTypes[0] 74 + } 75 + 76 + func (x PetType) Number() protoreflect.EnumNumber { 77 + return protoreflect.EnumNumber(x) 78 + } 79 + 80 + // Deprecated: Use PetType.Descriptor instead. 81 + func (PetType) EnumDescriptor() ([]byte, []int) { 82 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{0} 83 + } 84 + 85 + // GetPetRequest is the request object for GetPet 86 + // The message accepts a pet id as an input 87 + type GetPetRequest struct { 88 + state protoimpl.MessageState 89 + sizeCache protoimpl.SizeCache 90 + unknownFields protoimpl.UnknownFields 91 + 92 + PetId string `protobuf:"bytes,1,opt,name=pet_id,json=petId,proto3" json:"pet_id,omitempty"` 93 + } 94 + 95 + func (x *GetPetRequest) Reset() { 96 + *x = GetPetRequest{} 97 + if protoimpl.UnsafeEnabled { 98 + mi := &file_pet_v1_pet_proto_msgTypes[0] 99 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 100 + ms.StoreMessageInfo(mi) 101 + } 102 + } 103 + 104 + func (x *GetPetRequest) String() string { 105 + return protoimpl.X.MessageStringOf(x) 106 + } 107 + 108 + func (*GetPetRequest) ProtoMessage() {} 109 + 110 + func (x *GetPetRequest) ProtoReflect() protoreflect.Message { 111 + mi := &file_pet_v1_pet_proto_msgTypes[0] 112 + if protoimpl.UnsafeEnabled && x != nil { 113 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 114 + if ms.LoadMessageInfo() == nil { 115 + ms.StoreMessageInfo(mi) 116 + } 117 + return ms 118 + } 119 + return mi.MessageOf(x) 120 + } 121 + 122 + // Deprecated: Use GetPetRequest.ProtoReflect.Descriptor instead. 123 + func (*GetPetRequest) Descriptor() ([]byte, []int) { 124 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{0} 125 + } 126 + 127 + func (x *GetPetRequest) GetPetId() string { 128 + if x != nil { 129 + return x.PetId 130 + } 131 + return "" 132 + } 133 + 134 + type GetPetResponse struct { 135 + state protoimpl.MessageState 136 + sizeCache protoimpl.SizeCache 137 + unknownFields protoimpl.UnknownFields 138 + 139 + Pet *Pet `protobuf:"bytes,1,opt,name=pet,proto3" json:"pet,omitempty"` 140 + } 141 + 142 + func (x *GetPetResponse) Reset() { 143 + *x = GetPetResponse{} 144 + if protoimpl.UnsafeEnabled { 145 + mi := &file_pet_v1_pet_proto_msgTypes[1] 146 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 147 + ms.StoreMessageInfo(mi) 148 + } 149 + } 150 + 151 + func (x *GetPetResponse) String() string { 152 + return protoimpl.X.MessageStringOf(x) 153 + } 154 + 155 + func (*GetPetResponse) ProtoMessage() {} 156 + 157 + func (x *GetPetResponse) ProtoReflect() protoreflect.Message { 158 + mi := &file_pet_v1_pet_proto_msgTypes[1] 159 + if protoimpl.UnsafeEnabled && x != nil { 160 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 161 + if ms.LoadMessageInfo() == nil { 162 + ms.StoreMessageInfo(mi) 163 + } 164 + return ms 165 + } 166 + return mi.MessageOf(x) 167 + } 168 + 169 + // Deprecated: Use GetPetResponse.ProtoReflect.Descriptor instead. 170 + func (*GetPetResponse) Descriptor() ([]byte, []int) { 171 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{1} 172 + } 173 + 174 + func (x *GetPetResponse) GetPet() *Pet { 175 + if x != nil { 176 + return x.Pet 177 + } 178 + return nil 179 + } 180 + 181 + type PutPetRequest struct { 182 + state protoimpl.MessageState 183 + sizeCache protoimpl.SizeCache 184 + unknownFields protoimpl.UnknownFields 185 + 186 + PetType PetType `protobuf:"varint,1,opt,name=pet_type,json=petType,proto3,enum=pet.v1.PetType" json:"pet_type,omitempty"` 187 + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 188 + } 189 + 190 + func (x *PutPetRequest) Reset() { 191 + *x = PutPetRequest{} 192 + if protoimpl.UnsafeEnabled { 193 + mi := &file_pet_v1_pet_proto_msgTypes[2] 194 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 195 + ms.StoreMessageInfo(mi) 196 + } 197 + } 198 + 199 + func (x *PutPetRequest) String() string { 200 + return protoimpl.X.MessageStringOf(x) 201 + } 202 + 203 + func (*PutPetRequest) ProtoMessage() {} 204 + 205 + func (x *PutPetRequest) ProtoReflect() protoreflect.Message { 206 + mi := &file_pet_v1_pet_proto_msgTypes[2] 207 + if protoimpl.UnsafeEnabled && x != nil { 208 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 209 + if ms.LoadMessageInfo() == nil { 210 + ms.StoreMessageInfo(mi) 211 + } 212 + return ms 213 + } 214 + return mi.MessageOf(x) 215 + } 216 + 217 + // Deprecated: Use PutPetRequest.ProtoReflect.Descriptor instead. 218 + func (*PutPetRequest) Descriptor() ([]byte, []int) { 219 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{2} 220 + } 221 + 222 + func (x *PutPetRequest) GetPetType() PetType { 223 + if x != nil { 224 + return x.PetType 225 + } 226 + return PetType_PET_TYPE_UNSPECIFIED 227 + } 228 + 229 + func (x *PutPetRequest) GetName() string { 230 + if x != nil { 231 + return x.Name 232 + } 233 + return "" 234 + } 235 + 236 + type PutPetResponse struct { 237 + state protoimpl.MessageState 238 + sizeCache protoimpl.SizeCache 239 + unknownFields protoimpl.UnknownFields 240 + 241 + Pet *Pet `protobuf:"bytes,1,opt,name=pet,proto3" json:"pet,omitempty"` 242 + } 243 + 244 + func (x *PutPetResponse) Reset() { 245 + *x = PutPetResponse{} 246 + if protoimpl.UnsafeEnabled { 247 + mi := &file_pet_v1_pet_proto_msgTypes[3] 248 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 249 + ms.StoreMessageInfo(mi) 250 + } 251 + } 252 + 253 + func (x *PutPetResponse) String() string { 254 + return protoimpl.X.MessageStringOf(x) 255 + } 256 + 257 + func (*PutPetResponse) ProtoMessage() {} 258 + 259 + func (x *PutPetResponse) ProtoReflect() protoreflect.Message { 260 + mi := &file_pet_v1_pet_proto_msgTypes[3] 261 + if protoimpl.UnsafeEnabled && x != nil { 262 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 263 + if ms.LoadMessageInfo() == nil { 264 + ms.StoreMessageInfo(mi) 265 + } 266 + return ms 267 + } 268 + return mi.MessageOf(x) 269 + } 270 + 271 + // Deprecated: Use PutPetResponse.ProtoReflect.Descriptor instead. 272 + func (*PutPetResponse) Descriptor() ([]byte, []int) { 273 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{3} 274 + } 275 + 276 + func (x *PutPetResponse) GetPet() *Pet { 277 + if x != nil { 278 + return x.Pet 279 + } 280 + return nil 281 + } 282 + 283 + type DeletePetRequest struct { 284 + state protoimpl.MessageState 285 + sizeCache protoimpl.SizeCache 286 + unknownFields protoimpl.UnknownFields 287 + 288 + PetId string `protobuf:"bytes,1,opt,name=pet_id,json=petId,proto3" json:"pet_id,omitempty"` 289 + } 290 + 291 + func (x *DeletePetRequest) Reset() { 292 + *x = DeletePetRequest{} 293 + if protoimpl.UnsafeEnabled { 294 + mi := &file_pet_v1_pet_proto_msgTypes[4] 295 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 296 + ms.StoreMessageInfo(mi) 297 + } 298 + } 299 + 300 + func (x *DeletePetRequest) String() string { 301 + return protoimpl.X.MessageStringOf(x) 302 + } 303 + 304 + func (*DeletePetRequest) ProtoMessage() {} 305 + 306 + func (x *DeletePetRequest) ProtoReflect() protoreflect.Message { 307 + mi := &file_pet_v1_pet_proto_msgTypes[4] 308 + if protoimpl.UnsafeEnabled && x != nil { 309 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 310 + if ms.LoadMessageInfo() == nil { 311 + ms.StoreMessageInfo(mi) 312 + } 313 + return ms 314 + } 315 + return mi.MessageOf(x) 316 + } 317 + 318 + // Deprecated: Use DeletePetRequest.ProtoReflect.Descriptor instead. 319 + func (*DeletePetRequest) Descriptor() ([]byte, []int) { 320 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{4} 321 + } 322 + 323 + func (x *DeletePetRequest) GetPetId() string { 324 + if x != nil { 325 + return x.PetId 326 + } 327 + return "" 328 + } 329 + 330 + type PurchasePetRequest struct { 331 + state protoimpl.MessageState 332 + sizeCache protoimpl.SizeCache 333 + unknownFields protoimpl.UnknownFields 334 + 335 + PetId string `protobuf:"bytes,1,opt,name=pet_id,json=petId,proto3" json:"pet_id,omitempty"` 336 + Order *v1alpha1.Order `protobuf:"bytes,2,opt,name=order,proto3" json:"order,omitempty"` 337 + } 338 + 339 + func (x *PurchasePetRequest) Reset() { 340 + *x = PurchasePetRequest{} 341 + if protoimpl.UnsafeEnabled { 342 + mi := &file_pet_v1_pet_proto_msgTypes[5] 343 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 344 + ms.StoreMessageInfo(mi) 345 + } 346 + } 347 + 348 + func (x *PurchasePetRequest) String() string { 349 + return protoimpl.X.MessageStringOf(x) 350 + } 351 + 352 + func (*PurchasePetRequest) ProtoMessage() {} 353 + 354 + func (x *PurchasePetRequest) ProtoReflect() protoreflect.Message { 355 + mi := &file_pet_v1_pet_proto_msgTypes[5] 356 + if protoimpl.UnsafeEnabled && x != nil { 357 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 358 + if ms.LoadMessageInfo() == nil { 359 + ms.StoreMessageInfo(mi) 360 + } 361 + return ms 362 + } 363 + return mi.MessageOf(x) 364 + } 365 + 366 + // Deprecated: Use PurchasePetRequest.ProtoReflect.Descriptor instead. 367 + func (*PurchasePetRequest) Descriptor() ([]byte, []int) { 368 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{5} 369 + } 370 + 371 + func (x *PurchasePetRequest) GetPetId() string { 372 + if x != nil { 373 + return x.PetId 374 + } 375 + return "" 376 + } 377 + 378 + func (x *PurchasePetRequest) GetOrder() *v1alpha1.Order { 379 + if x != nil { 380 + return x.Order 381 + } 382 + return nil 383 + } 384 + 385 + type PurchasePetResponse struct { 386 + state protoimpl.MessageState 387 + sizeCache protoimpl.SizeCache 388 + unknownFields protoimpl.UnknownFields 389 + } 390 + 391 + func (x *PurchasePetResponse) Reset() { 392 + *x = PurchasePetResponse{} 393 + if protoimpl.UnsafeEnabled { 394 + mi := &file_pet_v1_pet_proto_msgTypes[6] 395 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 396 + ms.StoreMessageInfo(mi) 397 + } 398 + } 399 + 400 + func (x *PurchasePetResponse) String() string { 401 + return protoimpl.X.MessageStringOf(x) 402 + } 403 + 404 + func (*PurchasePetResponse) ProtoMessage() {} 405 + 406 + func (x *PurchasePetResponse) ProtoReflect() protoreflect.Message { 407 + mi := &file_pet_v1_pet_proto_msgTypes[6] 408 + if protoimpl.UnsafeEnabled && x != nil { 409 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 410 + if ms.LoadMessageInfo() == nil { 411 + ms.StoreMessageInfo(mi) 412 + } 413 + return ms 414 + } 415 + return mi.MessageOf(x) 416 + } 417 + 418 + // Deprecated: Use PurchasePetResponse.ProtoReflect.Descriptor instead. 419 + func (*PurchasePetResponse) Descriptor() ([]byte, []int) { 420 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{6} 421 + } 422 + 423 + // Pet represents a pet in the pet store. 424 + type Pet struct { 425 + state protoimpl.MessageState 426 + sizeCache protoimpl.SizeCache 427 + unknownFields protoimpl.UnknownFields 428 + 429 + PetType PetType `protobuf:"varint,1,opt,name=pet_type,json=petType,proto3,enum=pet.v1.PetType" json:"pet_type,omitempty"` 430 + PetTypes []PetType `protobuf:"varint,2,rep,packed,name=pet_types,json=petTypes,proto3,enum=pet.v1.PetType" json:"pet_types,omitempty"` 431 + PaymentProvider v1alpha1.PaymentProvider `protobuf:"varint,22,opt,name=payment_provider,json=paymentProvider,proto3,enum=payment.v1alpha1.PaymentProvider" json:"payment_provider,omitempty"` 432 + // pet_id is an auto-generated id for the pet 433 + // the id uniquely identifies a pet in the system 434 + PetId string `protobuf:"bytes,3,opt,name=pet_id,json=petId,proto3" json:"pet_id,omitempty"` 435 + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` 436 + CreatedAt *datetime.DateTime `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` 437 + Details []*anypb.Any `protobuf:"bytes,6,rep,name=details,proto3" json:"details,omitempty"` 438 + Vet *Pet_Vet `protobuf:"bytes,7,opt,name=vet,proto3" json:"vet,omitempty"` 439 + Vets []*Pet_Vet `protobuf:"bytes,8,rep,name=vets,proto3" json:"vets,omitempty"` 440 + Labels *structpb.ListValue `protobuf:"bytes,101,opt,name=labels,proto3" json:"labels,omitempty"` 441 + Tags []string `protobuf:"bytes,102,rep,name=tags,proto3" json:"tags,omitempty"` 442 + } 443 + 444 + func (x *Pet) Reset() { 445 + *x = Pet{} 446 + if protoimpl.UnsafeEnabled { 447 + mi := &file_pet_v1_pet_proto_msgTypes[7] 448 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 449 + ms.StoreMessageInfo(mi) 450 + } 451 + } 452 + 453 + func (x *Pet) String() string { 454 + return protoimpl.X.MessageStringOf(x) 455 + } 456 + 457 + func (*Pet) ProtoMessage() {} 458 + 459 + func (x *Pet) ProtoReflect() protoreflect.Message { 460 + mi := &file_pet_v1_pet_proto_msgTypes[7] 461 + if protoimpl.UnsafeEnabled && x != nil { 462 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 463 + if ms.LoadMessageInfo() == nil { 464 + ms.StoreMessageInfo(mi) 465 + } 466 + return ms 467 + } 468 + return mi.MessageOf(x) 469 + } 470 + 471 + // Deprecated: Use Pet.ProtoReflect.Descriptor instead. 472 + func (*Pet) Descriptor() ([]byte, []int) { 473 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{7} 474 + } 475 + 476 + func (x *Pet) GetPetType() PetType { 477 + if x != nil { 478 + return x.PetType 479 + } 480 + return PetType_PET_TYPE_UNSPECIFIED 481 + } 482 + 483 + func (x *Pet) GetPetTypes() []PetType { 484 + if x != nil { 485 + return x.PetTypes 486 + } 487 + return nil 488 + } 489 + 490 + func (x *Pet) GetPaymentProvider() v1alpha1.PaymentProvider { 491 + if x != nil { 492 + return x.PaymentProvider 493 + } 494 + return v1alpha1.PaymentProvider(0) 495 + } 496 + 497 + func (x *Pet) GetPetId() string { 498 + if x != nil { 499 + return x.PetId 500 + } 501 + return "" 502 + } 503 + 504 + func (x *Pet) GetName() string { 505 + if x != nil { 506 + return x.Name 507 + } 508 + return "" 509 + } 510 + 511 + func (x *Pet) GetCreatedAt() *datetime.DateTime { 512 + if x != nil { 513 + return x.CreatedAt 514 + } 515 + return nil 516 + } 517 + 518 + func (x *Pet) GetDetails() []*anypb.Any { 519 + if x != nil { 520 + return x.Details 521 + } 522 + return nil 523 + } 524 + 525 + func (x *Pet) GetVet() *Pet_Vet { 526 + if x != nil { 527 + return x.Vet 528 + } 529 + return nil 530 + } 531 + 532 + func (x *Pet) GetVets() []*Pet_Vet { 533 + if x != nil { 534 + return x.Vets 535 + } 536 + return nil 537 + } 538 + 539 + func (x *Pet) GetLabels() *structpb.ListValue { 540 + if x != nil { 541 + return x.Labels 542 + } 543 + return nil 544 + } 545 + 546 + func (x *Pet) GetTags() []string { 547 + if x != nil { 548 + return x.Tags 549 + } 550 + return nil 551 + } 552 + 553 + type Pet_Vet struct { 554 + state protoimpl.MessageState 555 + sizeCache protoimpl.SizeCache 556 + unknownFields protoimpl.UnknownFields 557 + 558 + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 559 + } 560 + 561 + func (x *Pet_Vet) Reset() { 562 + *x = Pet_Vet{} 563 + if protoimpl.UnsafeEnabled { 564 + mi := &file_pet_v1_pet_proto_msgTypes[8] 565 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 566 + ms.StoreMessageInfo(mi) 567 + } 568 + } 569 + 570 + func (x *Pet_Vet) String() string { 571 + return protoimpl.X.MessageStringOf(x) 572 + } 573 + 574 + func (*Pet_Vet) ProtoMessage() {} 575 + 576 + func (x *Pet_Vet) ProtoReflect() protoreflect.Message { 577 + mi := &file_pet_v1_pet_proto_msgTypes[8] 578 + if protoimpl.UnsafeEnabled && x != nil { 579 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 580 + if ms.LoadMessageInfo() == nil { 581 + ms.StoreMessageInfo(mi) 582 + } 583 + return ms 584 + } 585 + return mi.MessageOf(x) 586 + } 587 + 588 + // Deprecated: Use Pet_Vet.ProtoReflect.Descriptor instead. 589 + func (*Pet_Vet) Descriptor() ([]byte, []int) { 590 + return file_pet_v1_pet_proto_rawDescGZIP(), []int{7, 0} 591 + } 592 + 593 + func (x *Pet_Vet) GetName() string { 594 + if x != nil { 595 + return x.Name 596 + } 597 + return "" 598 + } 599 + 600 + var File_pet_v1_pet_proto protoreflect.FileDescriptor 601 + 602 + var file_pet_v1_pet_proto_rawDesc = []byte{ 603 + 0x0a, 0x10, 0x70, 0x65, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 604 + 0x74, 0x6f, 0x12, 0x06, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x70, 0x61, 0x79, 0x6d, 605 + 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x70, 0x61, 0x79, 606 + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x67, 0x6f, 0x6f, 0x67, 607 + 0x6c, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 608 + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 609 + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 610 + 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 611 + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 612 + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 613 + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 614 + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 615 + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x0d, 616 + 0x47, 0x65, 0x74, 0x50, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 617 + 0x06, 0x70, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 618 + 0x65, 0x74, 0x49, 0x64, 0x22, 0x2f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x74, 0x52, 0x65, 619 + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x03, 0x70, 0x65, 0x74, 0x18, 0x01, 0x20, 620 + 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x74, 621 + 0x52, 0x03, 0x70, 0x65, 0x74, 0x22, 0x4f, 0x0a, 0x0d, 0x50, 0x75, 0x74, 0x50, 0x65, 0x74, 0x52, 622 + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x65, 0x74, 0x5f, 0x74, 0x79, 623 + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 624 + 0x31, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x70, 0x65, 0x74, 0x54, 0x79, 625 + 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 626 + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x75, 0x74, 0x50, 0x65, 0x74, 627 + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x03, 0x70, 0x65, 0x74, 0x18, 628 + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 629 + 0x65, 0x74, 0x52, 0x03, 0x70, 0x65, 0x74, 0x22, 0x29, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 630 + 0x65, 0x50, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x70, 631 + 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x65, 0x74, 632 + 0x49, 0x64, 0x22, 0x5a, 0x0a, 0x12, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x65, 633 + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x70, 0x65, 0x74, 0x5f, 634 + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x65, 0x74, 0x49, 0x64, 0x12, 635 + 0x2d, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 636 + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 637 + 0x31, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x15, 638 + 0x0a, 0x13, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x65, 0x74, 0x52, 0x65, 0x73, 639 + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe9, 0x03, 0x0a, 0x03, 0x50, 0x65, 0x74, 0x12, 0x2a, 0x0a, 640 + 0x08, 0x70, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 641 + 0x0f, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 642 + 0x52, 0x07, 0x70, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x09, 0x70, 0x65, 0x74, 643 + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 644 + 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x70, 645 + 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 646 + 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 647 + 0x0e, 0x32, 0x21, 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 648 + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 649 + 0x69, 0x64, 0x65, 0x72, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 650 + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x70, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 651 + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x65, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 652 + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 653 + 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 654 + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x79, 655 + 0x70, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x09, 0x63, 0x72, 0x65, 656 + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 657 + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 658 + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 659 + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x21, 0x0a, 0x03, 0x76, 0x65, 0x74, 0x18, 0x07, 0x20, 660 + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x74, 661 + 0x2e, 0x56, 0x65, 0x74, 0x52, 0x03, 0x76, 0x65, 0x74, 0x12, 0x23, 0x0a, 0x04, 0x76, 0x65, 0x74, 662 + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 663 + 0x2e, 0x50, 0x65, 0x74, 0x2e, 0x56, 0x65, 0x74, 0x52, 0x04, 0x76, 0x65, 0x74, 0x73, 0x12, 0x32, 664 + 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 665 + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 666 + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 667 + 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x66, 0x20, 0x03, 0x28, 0x09, 668 + 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x19, 0x0a, 0x03, 0x56, 0x65, 0x74, 0x12, 0x12, 0x0a, 669 + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 670 + 0x65, 0x2a, 0x71, 0x0a, 0x07, 0x50, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 671 + 0x50, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 672 + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x54, 0x5f, 0x54, 0x59, 673 + 0x50, 0x45, 0x5f, 0x43, 0x41, 0x54, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x45, 0x54, 0x5f, 674 + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x47, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x45, 675 + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4e, 0x41, 0x4b, 0x45, 0x10, 0x03, 0x12, 0x14, 676 + 0x0a, 0x10, 0x50, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x41, 0x4d, 0x53, 0x54, 677 + 0x45, 0x52, 0x10, 0x04, 0x32, 0xd7, 0x01, 0x0a, 0x0f, 0x50, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 678 + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x50, 679 + 0x65, 0x74, 0x12, 0x15, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 680 + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x65, 0x74, 0x2e, 681 + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 682 + 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x74, 683 + 0x12, 0x18, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 684 + 0x50, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 685 + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 686 + 0x74, 0x79, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 687 + 0x50, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x72, 688 + 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 689 + 0x1b, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 690 + 0x65, 0x50, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xa4, 691 + 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x50, 692 + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x53, 0x67, 0x69, 0x74, 0x68, 0x75, 693 + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x68, 0x72, 0x6f, 0x75, 694 + 0x67, 0x68, 0x2f, 0x74, 0x77, 0x69, 0x72, 0x70, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 695 + 0x2d, 0x67, 0x65, 0x6e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 696 + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 697 + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x65, 0x74, 0x2f, 0x76, 0x31, 0xa2, 0x02, 698 + 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x50, 0x65, 0x74, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 699 + 0x50, 0x65, 0x74, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x50, 0x65, 0x74, 0x5c, 0x56, 0x31, 0x5c, 700 + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x50, 0x65, 701 + 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 702 + } 703 + 704 + var ( 705 + file_pet_v1_pet_proto_rawDescOnce sync.Once 706 + file_pet_v1_pet_proto_rawDescData = file_pet_v1_pet_proto_rawDesc 707 + ) 708 + 709 + func file_pet_v1_pet_proto_rawDescGZIP() []byte { 710 + file_pet_v1_pet_proto_rawDescOnce.Do(func() { 711 + file_pet_v1_pet_proto_rawDescData = protoimpl.X.CompressGZIP(file_pet_v1_pet_proto_rawDescData) 712 + }) 713 + return file_pet_v1_pet_proto_rawDescData 714 + } 715 + 716 + var file_pet_v1_pet_proto_enumTypes = make([]protoimpl.EnumInfo, 1) 717 + var file_pet_v1_pet_proto_msgTypes = make([]protoimpl.MessageInfo, 9) 718 + var file_pet_v1_pet_proto_goTypes = []interface{}{ 719 + (PetType)(0), // 0: pet.v1.PetType 720 + (*GetPetRequest)(nil), // 1: pet.v1.GetPetRequest 721 + (*GetPetResponse)(nil), // 2: pet.v1.GetPetResponse 722 + (*PutPetRequest)(nil), // 3: pet.v1.PutPetRequest 723 + (*PutPetResponse)(nil), // 4: pet.v1.PutPetResponse 724 + (*DeletePetRequest)(nil), // 5: pet.v1.DeletePetRequest 725 + (*PurchasePetRequest)(nil), // 6: pet.v1.PurchasePetRequest 726 + (*PurchasePetResponse)(nil), // 7: pet.v1.PurchasePetResponse 727 + (*Pet)(nil), // 8: pet.v1.Pet 728 + (*Pet_Vet)(nil), // 9: pet.v1.Pet.Vet 729 + (*v1alpha1.Order)(nil), // 10: payment.v1alpha1.Order 730 + (v1alpha1.PaymentProvider)(0), // 11: payment.v1alpha1.PaymentProvider 731 + (*datetime.DateTime)(nil), // 12: google.type.DateTime 732 + (*anypb.Any)(nil), // 13: google.protobuf.Any 733 + (*structpb.ListValue)(nil), // 14: google.protobuf.ListValue 734 + (*emptypb.Empty)(nil), // 15: google.protobuf.Empty 735 + } 736 + var file_pet_v1_pet_proto_depIdxs = []int32{ 737 + 8, // 0: pet.v1.GetPetResponse.pet:type_name -> pet.v1.Pet 738 + 0, // 1: pet.v1.PutPetRequest.pet_type:type_name -> pet.v1.PetType 739 + 8, // 2: pet.v1.PutPetResponse.pet:type_name -> pet.v1.Pet 740 + 10, // 3: pet.v1.PurchasePetRequest.order:type_name -> payment.v1alpha1.Order 741 + 0, // 4: pet.v1.Pet.pet_type:type_name -> pet.v1.PetType 742 + 0, // 5: pet.v1.Pet.pet_types:type_name -> pet.v1.PetType 743 + 11, // 6: pet.v1.Pet.payment_provider:type_name -> payment.v1alpha1.PaymentProvider 744 + 12, // 7: pet.v1.Pet.created_at:type_name -> google.type.DateTime 745 + 13, // 8: pet.v1.Pet.details:type_name -> google.protobuf.Any 746 + 9, // 9: pet.v1.Pet.vet:type_name -> pet.v1.Pet.Vet 747 + 9, // 10: pet.v1.Pet.vets:type_name -> pet.v1.Pet.Vet 748 + 14, // 11: pet.v1.Pet.labels:type_name -> google.protobuf.ListValue 749 + 1, // 12: pet.v1.PetStoreService.GetPet:input_type -> pet.v1.GetPetRequest 750 + 5, // 13: pet.v1.PetStoreService.DeletePet:input_type -> pet.v1.DeletePetRequest 751 + 6, // 14: pet.v1.PetStoreService.PurchasePet:input_type -> pet.v1.PurchasePetRequest 752 + 2, // 15: pet.v1.PetStoreService.GetPet:output_type -> pet.v1.GetPetResponse 753 + 15, // 16: pet.v1.PetStoreService.DeletePet:output_type -> google.protobuf.Empty 754 + 7, // 17: pet.v1.PetStoreService.PurchasePet:output_type -> pet.v1.PurchasePetResponse 755 + 15, // [15:18] is the sub-list for method output_type 756 + 12, // [12:15] is the sub-list for method input_type 757 + 12, // [12:12] is the sub-list for extension type_name 758 + 12, // [12:12] is the sub-list for extension extendee 759 + 0, // [0:12] is the sub-list for field type_name 760 + } 761 + 762 + func init() { file_pet_v1_pet_proto_init() } 763 + func file_pet_v1_pet_proto_init() { 764 + if File_pet_v1_pet_proto != nil { 765 + return 766 + } 767 + if !protoimpl.UnsafeEnabled { 768 + file_pet_v1_pet_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 769 + switch v := v.(*GetPetRequest); i { 770 + case 0: 771 + return &v.state 772 + case 1: 773 + return &v.sizeCache 774 + case 2: 775 + return &v.unknownFields 776 + default: 777 + return nil 778 + } 779 + } 780 + file_pet_v1_pet_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 781 + switch v := v.(*GetPetResponse); i { 782 + case 0: 783 + return &v.state 784 + case 1: 785 + return &v.sizeCache 786 + case 2: 787 + return &v.unknownFields 788 + default: 789 + return nil 790 + } 791 + } 792 + file_pet_v1_pet_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 793 + switch v := v.(*PutPetRequest); i { 794 + case 0: 795 + return &v.state 796 + case 1: 797 + return &v.sizeCache 798 + case 2: 799 + return &v.unknownFields 800 + default: 801 + return nil 802 + } 803 + } 804 + file_pet_v1_pet_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 805 + switch v := v.(*PutPetResponse); i { 806 + case 0: 807 + return &v.state 808 + case 1: 809 + return &v.sizeCache 810 + case 2: 811 + return &v.unknownFields 812 + default: 813 + return nil 814 + } 815 + } 816 + file_pet_v1_pet_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { 817 + switch v := v.(*DeletePetRequest); i { 818 + case 0: 819 + return &v.state 820 + case 1: 821 + return &v.sizeCache 822 + case 2: 823 + return &v.unknownFields 824 + default: 825 + return nil 826 + } 827 + } 828 + file_pet_v1_pet_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { 829 + switch v := v.(*PurchasePetRequest); i { 830 + case 0: 831 + return &v.state 832 + case 1: 833 + return &v.sizeCache 834 + case 2: 835 + return &v.unknownFields 836 + default: 837 + return nil 838 + } 839 + } 840 + file_pet_v1_pet_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { 841 + switch v := v.(*PurchasePetResponse); i { 842 + case 0: 843 + return &v.state 844 + case 1: 845 + return &v.sizeCache 846 + case 2: 847 + return &v.unknownFields 848 + default: 849 + return nil 850 + } 851 + } 852 + file_pet_v1_pet_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { 853 + switch v := v.(*Pet); i { 854 + case 0: 855 + return &v.state 856 + case 1: 857 + return &v.sizeCache 858 + case 2: 859 + return &v.unknownFields 860 + default: 861 + return nil 862 + } 863 + } 864 + file_pet_v1_pet_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { 865 + switch v := v.(*Pet_Vet); i { 866 + case 0: 867 + return &v.state 868 + case 1: 869 + return &v.sizeCache 870 + case 2: 871 + return &v.unknownFields 872 + default: 873 + return nil 874 + } 875 + } 876 + } 877 + type x struct{} 878 + out := protoimpl.TypeBuilder{ 879 + File: protoimpl.DescBuilder{ 880 + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 881 + RawDescriptor: file_pet_v1_pet_proto_rawDesc, 882 + NumEnums: 1, 883 + NumMessages: 9, 884 + NumExtensions: 0, 885 + NumServices: 1, 886 + }, 887 + GoTypes: file_pet_v1_pet_proto_goTypes, 888 + DependencyIndexes: file_pet_v1_pet_proto_depIdxs, 889 + EnumInfos: file_pet_v1_pet_proto_enumTypes, 890 + MessageInfos: file_pet_v1_pet_proto_msgTypes, 891 + }.Build() 892 + File_pet_v1_pet_proto = out.File 893 + file_pet_v1_pet_proto_rawDesc = nil 894 + file_pet_v1_pet_proto_goTypes = nil 895 + file_pet_v1_pet_proto_depIdxs = nil 896 + }
+1705
cmd/twirp-openapi-gen/internal/generator/testdata/gen/go/pet/v1/pet.twirp.go
··· 1 + // Code generated by protoc-gen-twirp v8.1.3, DO NOT EDIT. 2 + // source: pet/v1/pet.proto 3 + 4 + package v1 5 + 6 + import context "context" 7 + import fmt "fmt" 8 + import http "net/http" 9 + import io "io" 10 + import json "encoding/json" 11 + import strconv "strconv" 12 + import strings "strings" 13 + 14 + import protojson "google.golang.org/protobuf/encoding/protojson" 15 + import proto "google.golang.org/protobuf/proto" 16 + import twirp "github.com/twitchtv/twirp" 17 + import ctxsetters "github.com/twitchtv/twirp/ctxsetters" 18 + 19 + import google_protobuf1 "google.golang.org/protobuf/types/known/emptypb" 20 + 21 + import bytes "bytes" 22 + import errors "errors" 23 + import path "path" 24 + import url "net/url" 25 + 26 + // Version compatibility assertion. 27 + // If the constant is not defined in the package, that likely means 28 + // the package needs to be updated to work with this generated code. 29 + // See https://twitchtv.github.io/twirp/docs/version_matrix.html 30 + const _ = twirp.TwirpPackageMinVersion_8_1_0 31 + 32 + // ========================= 33 + // PetStoreService Interface 34 + // ========================= 35 + 36 + type PetStoreService interface { 37 + // GetPet returns details about a pet 38 + // It accepts a pet id as an input and returns back the matching pet object 39 + // req-example: { "pet_id": "123" } 40 + // req-example: { "pet_id": "456" } 41 + // res-example: { "pet": {"name": "toby"} } 42 + GetPet(context.Context, *GetPetRequest) (*GetPetResponse, error) 43 + 44 + DeletePet(context.Context, *DeletePetRequest) (*google_protobuf1.Empty, error) 45 + 46 + PurchasePet(context.Context, *PurchasePetRequest) (*PurchasePetResponse, error) 47 + } 48 + 49 + // =============================== 50 + // PetStoreService Protobuf Client 51 + // =============================== 52 + 53 + type petStoreServiceProtobufClient struct { 54 + client HTTPClient 55 + urls [3]string 56 + interceptor twirp.Interceptor 57 + opts twirp.ClientOptions 58 + } 59 + 60 + // NewPetStoreServiceProtobufClient creates a Protobuf client that implements the PetStoreService interface. 61 + // It communicates using Protobuf and can be configured with a custom HTTPClient. 62 + func NewPetStoreServiceProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) PetStoreService { 63 + if c, ok := client.(*http.Client); ok { 64 + client = withoutRedirects(c) 65 + } 66 + 67 + clientOpts := twirp.ClientOptions{} 68 + for _, o := range opts { 69 + o(&clientOpts) 70 + } 71 + 72 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 73 + literalURLs := false 74 + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 75 + var pathPrefix string 76 + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 77 + pathPrefix = "/twirp" // default prefix 78 + } 79 + 80 + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 81 + serviceURL := sanitizeBaseURL(baseURL) 82 + serviceURL += baseServicePath(pathPrefix, "pet.v1", "PetStoreService") 83 + urls := [3]string{ 84 + serviceURL + "GetPet", 85 + serviceURL + "DeletePet", 86 + serviceURL + "PurchasePet", 87 + } 88 + 89 + return &petStoreServiceProtobufClient{ 90 + client: client, 91 + urls: urls, 92 + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 93 + opts: clientOpts, 94 + } 95 + } 96 + 97 + func (c *petStoreServiceProtobufClient) GetPet(ctx context.Context, in *GetPetRequest) (*GetPetResponse, error) { 98 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 99 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 100 + ctx = ctxsetters.WithMethodName(ctx, "GetPet") 101 + caller := c.callGetPet 102 + if c.interceptor != nil { 103 + caller = func(ctx context.Context, req *GetPetRequest) (*GetPetResponse, error) { 104 + resp, err := c.interceptor( 105 + func(ctx context.Context, req interface{}) (interface{}, error) { 106 + typedReq, ok := req.(*GetPetRequest) 107 + if !ok { 108 + return nil, twirp.InternalError("failed type assertion req.(*GetPetRequest) when calling interceptor") 109 + } 110 + return c.callGetPet(ctx, typedReq) 111 + }, 112 + )(ctx, req) 113 + if resp != nil { 114 + typedResp, ok := resp.(*GetPetResponse) 115 + if !ok { 116 + return nil, twirp.InternalError("failed type assertion resp.(*GetPetResponse) when calling interceptor") 117 + } 118 + return typedResp, err 119 + } 120 + return nil, err 121 + } 122 + } 123 + return caller(ctx, in) 124 + } 125 + 126 + func (c *petStoreServiceProtobufClient) callGetPet(ctx context.Context, in *GetPetRequest) (*GetPetResponse, error) { 127 + out := new(GetPetResponse) 128 + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 129 + if err != nil { 130 + twerr, ok := err.(twirp.Error) 131 + if !ok { 132 + twerr = twirp.InternalErrorWith(err) 133 + } 134 + callClientError(ctx, c.opts.Hooks, twerr) 135 + return nil, err 136 + } 137 + 138 + callClientResponseReceived(ctx, c.opts.Hooks) 139 + 140 + return out, nil 141 + } 142 + 143 + func (c *petStoreServiceProtobufClient) DeletePet(ctx context.Context, in *DeletePetRequest) (*google_protobuf1.Empty, error) { 144 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 145 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 146 + ctx = ctxsetters.WithMethodName(ctx, "DeletePet") 147 + caller := c.callDeletePet 148 + if c.interceptor != nil { 149 + caller = func(ctx context.Context, req *DeletePetRequest) (*google_protobuf1.Empty, error) { 150 + resp, err := c.interceptor( 151 + func(ctx context.Context, req interface{}) (interface{}, error) { 152 + typedReq, ok := req.(*DeletePetRequest) 153 + if !ok { 154 + return nil, twirp.InternalError("failed type assertion req.(*DeletePetRequest) when calling interceptor") 155 + } 156 + return c.callDeletePet(ctx, typedReq) 157 + }, 158 + )(ctx, req) 159 + if resp != nil { 160 + typedResp, ok := resp.(*google_protobuf1.Empty) 161 + if !ok { 162 + return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf1.Empty) when calling interceptor") 163 + } 164 + return typedResp, err 165 + } 166 + return nil, err 167 + } 168 + } 169 + return caller(ctx, in) 170 + } 171 + 172 + func (c *petStoreServiceProtobufClient) callDeletePet(ctx context.Context, in *DeletePetRequest) (*google_protobuf1.Empty, error) { 173 + out := new(google_protobuf1.Empty) 174 + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) 175 + if err != nil { 176 + twerr, ok := err.(twirp.Error) 177 + if !ok { 178 + twerr = twirp.InternalErrorWith(err) 179 + } 180 + callClientError(ctx, c.opts.Hooks, twerr) 181 + return nil, err 182 + } 183 + 184 + callClientResponseReceived(ctx, c.opts.Hooks) 185 + 186 + return out, nil 187 + } 188 + 189 + func (c *petStoreServiceProtobufClient) PurchasePet(ctx context.Context, in *PurchasePetRequest) (*PurchasePetResponse, error) { 190 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 191 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 192 + ctx = ctxsetters.WithMethodName(ctx, "PurchasePet") 193 + caller := c.callPurchasePet 194 + if c.interceptor != nil { 195 + caller = func(ctx context.Context, req *PurchasePetRequest) (*PurchasePetResponse, error) { 196 + resp, err := c.interceptor( 197 + func(ctx context.Context, req interface{}) (interface{}, error) { 198 + typedReq, ok := req.(*PurchasePetRequest) 199 + if !ok { 200 + return nil, twirp.InternalError("failed type assertion req.(*PurchasePetRequest) when calling interceptor") 201 + } 202 + return c.callPurchasePet(ctx, typedReq) 203 + }, 204 + )(ctx, req) 205 + if resp != nil { 206 + typedResp, ok := resp.(*PurchasePetResponse) 207 + if !ok { 208 + return nil, twirp.InternalError("failed type assertion resp.(*PurchasePetResponse) when calling interceptor") 209 + } 210 + return typedResp, err 211 + } 212 + return nil, err 213 + } 214 + } 215 + return caller(ctx, in) 216 + } 217 + 218 + func (c *petStoreServiceProtobufClient) callPurchasePet(ctx context.Context, in *PurchasePetRequest) (*PurchasePetResponse, error) { 219 + out := new(PurchasePetResponse) 220 + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) 221 + if err != nil { 222 + twerr, ok := err.(twirp.Error) 223 + if !ok { 224 + twerr = twirp.InternalErrorWith(err) 225 + } 226 + callClientError(ctx, c.opts.Hooks, twerr) 227 + return nil, err 228 + } 229 + 230 + callClientResponseReceived(ctx, c.opts.Hooks) 231 + 232 + return out, nil 233 + } 234 + 235 + // =========================== 236 + // PetStoreService JSON Client 237 + // =========================== 238 + 239 + type petStoreServiceJSONClient struct { 240 + client HTTPClient 241 + urls [3]string 242 + interceptor twirp.Interceptor 243 + opts twirp.ClientOptions 244 + } 245 + 246 + // NewPetStoreServiceJSONClient creates a JSON client that implements the PetStoreService interface. 247 + // It communicates using JSON and can be configured with a custom HTTPClient. 248 + func NewPetStoreServiceJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) PetStoreService { 249 + if c, ok := client.(*http.Client); ok { 250 + client = withoutRedirects(c) 251 + } 252 + 253 + clientOpts := twirp.ClientOptions{} 254 + for _, o := range opts { 255 + o(&clientOpts) 256 + } 257 + 258 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 259 + literalURLs := false 260 + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 261 + var pathPrefix string 262 + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 263 + pathPrefix = "/twirp" // default prefix 264 + } 265 + 266 + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 267 + serviceURL := sanitizeBaseURL(baseURL) 268 + serviceURL += baseServicePath(pathPrefix, "pet.v1", "PetStoreService") 269 + urls := [3]string{ 270 + serviceURL + "GetPet", 271 + serviceURL + "DeletePet", 272 + serviceURL + "PurchasePet", 273 + } 274 + 275 + return &petStoreServiceJSONClient{ 276 + client: client, 277 + urls: urls, 278 + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 279 + opts: clientOpts, 280 + } 281 + } 282 + 283 + func (c *petStoreServiceJSONClient) GetPet(ctx context.Context, in *GetPetRequest) (*GetPetResponse, error) { 284 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 285 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 286 + ctx = ctxsetters.WithMethodName(ctx, "GetPet") 287 + caller := c.callGetPet 288 + if c.interceptor != nil { 289 + caller = func(ctx context.Context, req *GetPetRequest) (*GetPetResponse, error) { 290 + resp, err := c.interceptor( 291 + func(ctx context.Context, req interface{}) (interface{}, error) { 292 + typedReq, ok := req.(*GetPetRequest) 293 + if !ok { 294 + return nil, twirp.InternalError("failed type assertion req.(*GetPetRequest) when calling interceptor") 295 + } 296 + return c.callGetPet(ctx, typedReq) 297 + }, 298 + )(ctx, req) 299 + if resp != nil { 300 + typedResp, ok := resp.(*GetPetResponse) 301 + if !ok { 302 + return nil, twirp.InternalError("failed type assertion resp.(*GetPetResponse) when calling interceptor") 303 + } 304 + return typedResp, err 305 + } 306 + return nil, err 307 + } 308 + } 309 + return caller(ctx, in) 310 + } 311 + 312 + func (c *petStoreServiceJSONClient) callGetPet(ctx context.Context, in *GetPetRequest) (*GetPetResponse, error) { 313 + out := new(GetPetResponse) 314 + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 315 + if err != nil { 316 + twerr, ok := err.(twirp.Error) 317 + if !ok { 318 + twerr = twirp.InternalErrorWith(err) 319 + } 320 + callClientError(ctx, c.opts.Hooks, twerr) 321 + return nil, err 322 + } 323 + 324 + callClientResponseReceived(ctx, c.opts.Hooks) 325 + 326 + return out, nil 327 + } 328 + 329 + func (c *petStoreServiceJSONClient) DeletePet(ctx context.Context, in *DeletePetRequest) (*google_protobuf1.Empty, error) { 330 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 331 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 332 + ctx = ctxsetters.WithMethodName(ctx, "DeletePet") 333 + caller := c.callDeletePet 334 + if c.interceptor != nil { 335 + caller = func(ctx context.Context, req *DeletePetRequest) (*google_protobuf1.Empty, error) { 336 + resp, err := c.interceptor( 337 + func(ctx context.Context, req interface{}) (interface{}, error) { 338 + typedReq, ok := req.(*DeletePetRequest) 339 + if !ok { 340 + return nil, twirp.InternalError("failed type assertion req.(*DeletePetRequest) when calling interceptor") 341 + } 342 + return c.callDeletePet(ctx, typedReq) 343 + }, 344 + )(ctx, req) 345 + if resp != nil { 346 + typedResp, ok := resp.(*google_protobuf1.Empty) 347 + if !ok { 348 + return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf1.Empty) when calling interceptor") 349 + } 350 + return typedResp, err 351 + } 352 + return nil, err 353 + } 354 + } 355 + return caller(ctx, in) 356 + } 357 + 358 + func (c *petStoreServiceJSONClient) callDeletePet(ctx context.Context, in *DeletePetRequest) (*google_protobuf1.Empty, error) { 359 + out := new(google_protobuf1.Empty) 360 + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[1], in, out) 361 + if err != nil { 362 + twerr, ok := err.(twirp.Error) 363 + if !ok { 364 + twerr = twirp.InternalErrorWith(err) 365 + } 366 + callClientError(ctx, c.opts.Hooks, twerr) 367 + return nil, err 368 + } 369 + 370 + callClientResponseReceived(ctx, c.opts.Hooks) 371 + 372 + return out, nil 373 + } 374 + 375 + func (c *petStoreServiceJSONClient) PurchasePet(ctx context.Context, in *PurchasePetRequest) (*PurchasePetResponse, error) { 376 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 377 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 378 + ctx = ctxsetters.WithMethodName(ctx, "PurchasePet") 379 + caller := c.callPurchasePet 380 + if c.interceptor != nil { 381 + caller = func(ctx context.Context, req *PurchasePetRequest) (*PurchasePetResponse, error) { 382 + resp, err := c.interceptor( 383 + func(ctx context.Context, req interface{}) (interface{}, error) { 384 + typedReq, ok := req.(*PurchasePetRequest) 385 + if !ok { 386 + return nil, twirp.InternalError("failed type assertion req.(*PurchasePetRequest) when calling interceptor") 387 + } 388 + return c.callPurchasePet(ctx, typedReq) 389 + }, 390 + )(ctx, req) 391 + if resp != nil { 392 + typedResp, ok := resp.(*PurchasePetResponse) 393 + if !ok { 394 + return nil, twirp.InternalError("failed type assertion resp.(*PurchasePetResponse) when calling interceptor") 395 + } 396 + return typedResp, err 397 + } 398 + return nil, err 399 + } 400 + } 401 + return caller(ctx, in) 402 + } 403 + 404 + func (c *petStoreServiceJSONClient) callPurchasePet(ctx context.Context, in *PurchasePetRequest) (*PurchasePetResponse, error) { 405 + out := new(PurchasePetResponse) 406 + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[2], in, out) 407 + if err != nil { 408 + twerr, ok := err.(twirp.Error) 409 + if !ok { 410 + twerr = twirp.InternalErrorWith(err) 411 + } 412 + callClientError(ctx, c.opts.Hooks, twerr) 413 + return nil, err 414 + } 415 + 416 + callClientResponseReceived(ctx, c.opts.Hooks) 417 + 418 + return out, nil 419 + } 420 + 421 + // ============================== 422 + // PetStoreService Server Handler 423 + // ============================== 424 + 425 + type petStoreServiceServer struct { 426 + PetStoreService 427 + interceptor twirp.Interceptor 428 + hooks *twirp.ServerHooks 429 + pathPrefix string // prefix for routing 430 + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response 431 + jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names 432 + } 433 + 434 + // NewPetStoreServiceServer builds a TwirpServer that can be used as an http.Handler to handle 435 + // HTTP requests that are routed to the right method in the provided svc implementation. 436 + // The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). 437 + func NewPetStoreServiceServer(svc PetStoreService, opts ...interface{}) TwirpServer { 438 + serverOpts := newServerOpts(opts) 439 + 440 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 441 + jsonSkipDefaults := false 442 + _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) 443 + jsonCamelCase := false 444 + _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) 445 + var pathPrefix string 446 + if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 447 + pathPrefix = "/twirp" // default prefix 448 + } 449 + 450 + return &petStoreServiceServer{ 451 + PetStoreService: svc, 452 + hooks: serverOpts.Hooks, 453 + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), 454 + pathPrefix: pathPrefix, 455 + jsonSkipDefaults: jsonSkipDefaults, 456 + jsonCamelCase: jsonCamelCase, 457 + } 458 + } 459 + 460 + // writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. 461 + // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) 462 + func (s *petStoreServiceServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { 463 + writeError(ctx, resp, err, s.hooks) 464 + } 465 + 466 + // handleRequestBodyError is used to handle error when the twirp server cannot read request 467 + func (s *petStoreServiceServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { 468 + if context.Canceled == ctx.Err() { 469 + s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) 470 + return 471 + } 472 + if context.DeadlineExceeded == ctx.Err() { 473 + s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) 474 + return 475 + } 476 + s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) 477 + } 478 + 479 + // PetStoreServicePathPrefix is a convenience constant that may identify URL paths. 480 + // Should be used with caution, it only matches routes generated by Twirp Go clients, 481 + // with the default "/twirp" prefix and default CamelCase service and method names. 482 + // More info: https://twitchtv.github.io/twirp/docs/routing.html 483 + const PetStoreServicePathPrefix = "/twirp/pet.v1.PetStoreService/" 484 + 485 + func (s *petStoreServiceServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { 486 + ctx := req.Context() 487 + ctx = ctxsetters.WithPackageName(ctx, "pet.v1") 488 + ctx = ctxsetters.WithServiceName(ctx, "PetStoreService") 489 + ctx = ctxsetters.WithResponseWriter(ctx, resp) 490 + 491 + var err error 492 + ctx, err = callRequestReceived(ctx, s.hooks) 493 + if err != nil { 494 + s.writeError(ctx, resp, err) 495 + return 496 + } 497 + 498 + if req.Method != "POST" { 499 + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) 500 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 501 + return 502 + } 503 + 504 + // Verify path format: [<prefix>]/<package>.<Service>/<Method> 505 + prefix, pkgService, method := parseTwirpPath(req.URL.Path) 506 + if pkgService != "pet.v1.PetStoreService" { 507 + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 508 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 509 + return 510 + } 511 + if prefix != s.pathPrefix { 512 + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) 513 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 514 + return 515 + } 516 + 517 + switch method { 518 + case "GetPet": 519 + s.serveGetPet(ctx, resp, req) 520 + return 521 + case "DeletePet": 522 + s.serveDeletePet(ctx, resp, req) 523 + return 524 + case "PurchasePet": 525 + s.servePurchasePet(ctx, resp, req) 526 + return 527 + default: 528 + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 529 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 530 + return 531 + } 532 + } 533 + 534 + func (s *petStoreServiceServer) serveGetPet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 535 + header := req.Header.Get("Content-Type") 536 + i := strings.Index(header, ";") 537 + if i == -1 { 538 + i = len(header) 539 + } 540 + switch strings.TrimSpace(strings.ToLower(header[:i])) { 541 + case "application/json": 542 + s.serveGetPetJSON(ctx, resp, req) 543 + case "application/protobuf": 544 + s.serveGetPetProtobuf(ctx, resp, req) 545 + default: 546 + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 547 + twerr := badRouteError(msg, req.Method, req.URL.Path) 548 + s.writeError(ctx, resp, twerr) 549 + } 550 + } 551 + 552 + func (s *petStoreServiceServer) serveGetPetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 553 + var err error 554 + ctx = ctxsetters.WithMethodName(ctx, "GetPet") 555 + ctx, err = callRequestRouted(ctx, s.hooks) 556 + if err != nil { 557 + s.writeError(ctx, resp, err) 558 + return 559 + } 560 + 561 + d := json.NewDecoder(req.Body) 562 + rawReqBody := json.RawMessage{} 563 + if err := d.Decode(&rawReqBody); err != nil { 564 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 565 + return 566 + } 567 + reqContent := new(GetPetRequest) 568 + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 569 + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 570 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 571 + return 572 + } 573 + 574 + handler := s.PetStoreService.GetPet 575 + if s.interceptor != nil { 576 + handler = func(ctx context.Context, req *GetPetRequest) (*GetPetResponse, error) { 577 + resp, err := s.interceptor( 578 + func(ctx context.Context, req interface{}) (interface{}, error) { 579 + typedReq, ok := req.(*GetPetRequest) 580 + if !ok { 581 + return nil, twirp.InternalError("failed type assertion req.(*GetPetRequest) when calling interceptor") 582 + } 583 + return s.PetStoreService.GetPet(ctx, typedReq) 584 + }, 585 + )(ctx, req) 586 + if resp != nil { 587 + typedResp, ok := resp.(*GetPetResponse) 588 + if !ok { 589 + return nil, twirp.InternalError("failed type assertion resp.(*GetPetResponse) when calling interceptor") 590 + } 591 + return typedResp, err 592 + } 593 + return nil, err 594 + } 595 + } 596 + 597 + // Call service method 598 + var respContent *GetPetResponse 599 + func() { 600 + defer ensurePanicResponses(ctx, resp, s.hooks) 601 + respContent, err = handler(ctx, reqContent) 602 + }() 603 + 604 + if err != nil { 605 + s.writeError(ctx, resp, err) 606 + return 607 + } 608 + if respContent == nil { 609 + s.writeError(ctx, resp, twirp.InternalError("received a nil *GetPetResponse and nil error while calling GetPet. nil responses are not supported")) 610 + return 611 + } 612 + 613 + ctx = callResponsePrepared(ctx, s.hooks) 614 + 615 + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 616 + respBytes, err := marshaler.Marshal(respContent) 617 + if err != nil { 618 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 619 + return 620 + } 621 + 622 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 623 + resp.Header().Set("Content-Type", "application/json") 624 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 625 + resp.WriteHeader(http.StatusOK) 626 + 627 + if n, err := resp.Write(respBytes); err != nil { 628 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 629 + twerr := twirp.NewError(twirp.Unknown, msg) 630 + ctx = callError(ctx, s.hooks, twerr) 631 + } 632 + callResponseSent(ctx, s.hooks) 633 + } 634 + 635 + func (s *petStoreServiceServer) serveGetPetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 636 + var err error 637 + ctx = ctxsetters.WithMethodName(ctx, "GetPet") 638 + ctx, err = callRequestRouted(ctx, s.hooks) 639 + if err != nil { 640 + s.writeError(ctx, resp, err) 641 + return 642 + } 643 + 644 + buf, err := io.ReadAll(req.Body) 645 + if err != nil { 646 + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 647 + return 648 + } 649 + reqContent := new(GetPetRequest) 650 + if err = proto.Unmarshal(buf, reqContent); err != nil { 651 + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 652 + return 653 + } 654 + 655 + handler := s.PetStoreService.GetPet 656 + if s.interceptor != nil { 657 + handler = func(ctx context.Context, req *GetPetRequest) (*GetPetResponse, error) { 658 + resp, err := s.interceptor( 659 + func(ctx context.Context, req interface{}) (interface{}, error) { 660 + typedReq, ok := req.(*GetPetRequest) 661 + if !ok { 662 + return nil, twirp.InternalError("failed type assertion req.(*GetPetRequest) when calling interceptor") 663 + } 664 + return s.PetStoreService.GetPet(ctx, typedReq) 665 + }, 666 + )(ctx, req) 667 + if resp != nil { 668 + typedResp, ok := resp.(*GetPetResponse) 669 + if !ok { 670 + return nil, twirp.InternalError("failed type assertion resp.(*GetPetResponse) when calling interceptor") 671 + } 672 + return typedResp, err 673 + } 674 + return nil, err 675 + } 676 + } 677 + 678 + // Call service method 679 + var respContent *GetPetResponse 680 + func() { 681 + defer ensurePanicResponses(ctx, resp, s.hooks) 682 + respContent, err = handler(ctx, reqContent) 683 + }() 684 + 685 + if err != nil { 686 + s.writeError(ctx, resp, err) 687 + return 688 + } 689 + if respContent == nil { 690 + s.writeError(ctx, resp, twirp.InternalError("received a nil *GetPetResponse and nil error while calling GetPet. nil responses are not supported")) 691 + return 692 + } 693 + 694 + ctx = callResponsePrepared(ctx, s.hooks) 695 + 696 + respBytes, err := proto.Marshal(respContent) 697 + if err != nil { 698 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 699 + return 700 + } 701 + 702 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 703 + resp.Header().Set("Content-Type", "application/protobuf") 704 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 705 + resp.WriteHeader(http.StatusOK) 706 + if n, err := resp.Write(respBytes); err != nil { 707 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 708 + twerr := twirp.NewError(twirp.Unknown, msg) 709 + ctx = callError(ctx, s.hooks, twerr) 710 + } 711 + callResponseSent(ctx, s.hooks) 712 + } 713 + 714 + func (s *petStoreServiceServer) serveDeletePet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 715 + header := req.Header.Get("Content-Type") 716 + i := strings.Index(header, ";") 717 + if i == -1 { 718 + i = len(header) 719 + } 720 + switch strings.TrimSpace(strings.ToLower(header[:i])) { 721 + case "application/json": 722 + s.serveDeletePetJSON(ctx, resp, req) 723 + case "application/protobuf": 724 + s.serveDeletePetProtobuf(ctx, resp, req) 725 + default: 726 + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 727 + twerr := badRouteError(msg, req.Method, req.URL.Path) 728 + s.writeError(ctx, resp, twerr) 729 + } 730 + } 731 + 732 + func (s *petStoreServiceServer) serveDeletePetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 733 + var err error 734 + ctx = ctxsetters.WithMethodName(ctx, "DeletePet") 735 + ctx, err = callRequestRouted(ctx, s.hooks) 736 + if err != nil { 737 + s.writeError(ctx, resp, err) 738 + return 739 + } 740 + 741 + d := json.NewDecoder(req.Body) 742 + rawReqBody := json.RawMessage{} 743 + if err := d.Decode(&rawReqBody); err != nil { 744 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 745 + return 746 + } 747 + reqContent := new(DeletePetRequest) 748 + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 749 + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 750 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 751 + return 752 + } 753 + 754 + handler := s.PetStoreService.DeletePet 755 + if s.interceptor != nil { 756 + handler = func(ctx context.Context, req *DeletePetRequest) (*google_protobuf1.Empty, error) { 757 + resp, err := s.interceptor( 758 + func(ctx context.Context, req interface{}) (interface{}, error) { 759 + typedReq, ok := req.(*DeletePetRequest) 760 + if !ok { 761 + return nil, twirp.InternalError("failed type assertion req.(*DeletePetRequest) when calling interceptor") 762 + } 763 + return s.PetStoreService.DeletePet(ctx, typedReq) 764 + }, 765 + )(ctx, req) 766 + if resp != nil { 767 + typedResp, ok := resp.(*google_protobuf1.Empty) 768 + if !ok { 769 + return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf1.Empty) when calling interceptor") 770 + } 771 + return typedResp, err 772 + } 773 + return nil, err 774 + } 775 + } 776 + 777 + // Call service method 778 + var respContent *google_protobuf1.Empty 779 + func() { 780 + defer ensurePanicResponses(ctx, resp, s.hooks) 781 + respContent, err = handler(ctx, reqContent) 782 + }() 783 + 784 + if err != nil { 785 + s.writeError(ctx, resp, err) 786 + return 787 + } 788 + if respContent == nil { 789 + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling DeletePet. nil responses are not supported")) 790 + return 791 + } 792 + 793 + ctx = callResponsePrepared(ctx, s.hooks) 794 + 795 + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 796 + respBytes, err := marshaler.Marshal(respContent) 797 + if err != nil { 798 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 799 + return 800 + } 801 + 802 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 803 + resp.Header().Set("Content-Type", "application/json") 804 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 805 + resp.WriteHeader(http.StatusOK) 806 + 807 + if n, err := resp.Write(respBytes); err != nil { 808 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 809 + twerr := twirp.NewError(twirp.Unknown, msg) 810 + ctx = callError(ctx, s.hooks, twerr) 811 + } 812 + callResponseSent(ctx, s.hooks) 813 + } 814 + 815 + func (s *petStoreServiceServer) serveDeletePetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 816 + var err error 817 + ctx = ctxsetters.WithMethodName(ctx, "DeletePet") 818 + ctx, err = callRequestRouted(ctx, s.hooks) 819 + if err != nil { 820 + s.writeError(ctx, resp, err) 821 + return 822 + } 823 + 824 + buf, err := io.ReadAll(req.Body) 825 + if err != nil { 826 + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 827 + return 828 + } 829 + reqContent := new(DeletePetRequest) 830 + if err = proto.Unmarshal(buf, reqContent); err != nil { 831 + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 832 + return 833 + } 834 + 835 + handler := s.PetStoreService.DeletePet 836 + if s.interceptor != nil { 837 + handler = func(ctx context.Context, req *DeletePetRequest) (*google_protobuf1.Empty, error) { 838 + resp, err := s.interceptor( 839 + func(ctx context.Context, req interface{}) (interface{}, error) { 840 + typedReq, ok := req.(*DeletePetRequest) 841 + if !ok { 842 + return nil, twirp.InternalError("failed type assertion req.(*DeletePetRequest) when calling interceptor") 843 + } 844 + return s.PetStoreService.DeletePet(ctx, typedReq) 845 + }, 846 + )(ctx, req) 847 + if resp != nil { 848 + typedResp, ok := resp.(*google_protobuf1.Empty) 849 + if !ok { 850 + return nil, twirp.InternalError("failed type assertion resp.(*google_protobuf1.Empty) when calling interceptor") 851 + } 852 + return typedResp, err 853 + } 854 + return nil, err 855 + } 856 + } 857 + 858 + // Call service method 859 + var respContent *google_protobuf1.Empty 860 + func() { 861 + defer ensurePanicResponses(ctx, resp, s.hooks) 862 + respContent, err = handler(ctx, reqContent) 863 + }() 864 + 865 + if err != nil { 866 + s.writeError(ctx, resp, err) 867 + return 868 + } 869 + if respContent == nil { 870 + s.writeError(ctx, resp, twirp.InternalError("received a nil *google_protobuf1.Empty and nil error while calling DeletePet. nil responses are not supported")) 871 + return 872 + } 873 + 874 + ctx = callResponsePrepared(ctx, s.hooks) 875 + 876 + respBytes, err := proto.Marshal(respContent) 877 + if err != nil { 878 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 879 + return 880 + } 881 + 882 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 883 + resp.Header().Set("Content-Type", "application/protobuf") 884 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 885 + resp.WriteHeader(http.StatusOK) 886 + if n, err := resp.Write(respBytes); err != nil { 887 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 888 + twerr := twirp.NewError(twirp.Unknown, msg) 889 + ctx = callError(ctx, s.hooks, twerr) 890 + } 891 + callResponseSent(ctx, s.hooks) 892 + } 893 + 894 + func (s *petStoreServiceServer) servePurchasePet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 895 + header := req.Header.Get("Content-Type") 896 + i := strings.Index(header, ";") 897 + if i == -1 { 898 + i = len(header) 899 + } 900 + switch strings.TrimSpace(strings.ToLower(header[:i])) { 901 + case "application/json": 902 + s.servePurchasePetJSON(ctx, resp, req) 903 + case "application/protobuf": 904 + s.servePurchasePetProtobuf(ctx, resp, req) 905 + default: 906 + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 907 + twerr := badRouteError(msg, req.Method, req.URL.Path) 908 + s.writeError(ctx, resp, twerr) 909 + } 910 + } 911 + 912 + func (s *petStoreServiceServer) servePurchasePetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 913 + var err error 914 + ctx = ctxsetters.WithMethodName(ctx, "PurchasePet") 915 + ctx, err = callRequestRouted(ctx, s.hooks) 916 + if err != nil { 917 + s.writeError(ctx, resp, err) 918 + return 919 + } 920 + 921 + d := json.NewDecoder(req.Body) 922 + rawReqBody := json.RawMessage{} 923 + if err := d.Decode(&rawReqBody); err != nil { 924 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 925 + return 926 + } 927 + reqContent := new(PurchasePetRequest) 928 + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 929 + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 930 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 931 + return 932 + } 933 + 934 + handler := s.PetStoreService.PurchasePet 935 + if s.interceptor != nil { 936 + handler = func(ctx context.Context, req *PurchasePetRequest) (*PurchasePetResponse, error) { 937 + resp, err := s.interceptor( 938 + func(ctx context.Context, req interface{}) (interface{}, error) { 939 + typedReq, ok := req.(*PurchasePetRequest) 940 + if !ok { 941 + return nil, twirp.InternalError("failed type assertion req.(*PurchasePetRequest) when calling interceptor") 942 + } 943 + return s.PetStoreService.PurchasePet(ctx, typedReq) 944 + }, 945 + )(ctx, req) 946 + if resp != nil { 947 + typedResp, ok := resp.(*PurchasePetResponse) 948 + if !ok { 949 + return nil, twirp.InternalError("failed type assertion resp.(*PurchasePetResponse) when calling interceptor") 950 + } 951 + return typedResp, err 952 + } 953 + return nil, err 954 + } 955 + } 956 + 957 + // Call service method 958 + var respContent *PurchasePetResponse 959 + func() { 960 + defer ensurePanicResponses(ctx, resp, s.hooks) 961 + respContent, err = handler(ctx, reqContent) 962 + }() 963 + 964 + if err != nil { 965 + s.writeError(ctx, resp, err) 966 + return 967 + } 968 + if respContent == nil { 969 + s.writeError(ctx, resp, twirp.InternalError("received a nil *PurchasePetResponse and nil error while calling PurchasePet. nil responses are not supported")) 970 + return 971 + } 972 + 973 + ctx = callResponsePrepared(ctx, s.hooks) 974 + 975 + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 976 + respBytes, err := marshaler.Marshal(respContent) 977 + if err != nil { 978 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 979 + return 980 + } 981 + 982 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 983 + resp.Header().Set("Content-Type", "application/json") 984 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 985 + resp.WriteHeader(http.StatusOK) 986 + 987 + if n, err := resp.Write(respBytes); err != nil { 988 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 989 + twerr := twirp.NewError(twirp.Unknown, msg) 990 + ctx = callError(ctx, s.hooks, twerr) 991 + } 992 + callResponseSent(ctx, s.hooks) 993 + } 994 + 995 + func (s *petStoreServiceServer) servePurchasePetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 996 + var err error 997 + ctx = ctxsetters.WithMethodName(ctx, "PurchasePet") 998 + ctx, err = callRequestRouted(ctx, s.hooks) 999 + if err != nil { 1000 + s.writeError(ctx, resp, err) 1001 + return 1002 + } 1003 + 1004 + buf, err := io.ReadAll(req.Body) 1005 + if err != nil { 1006 + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 1007 + return 1008 + } 1009 + reqContent := new(PurchasePetRequest) 1010 + if err = proto.Unmarshal(buf, reqContent); err != nil { 1011 + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 1012 + return 1013 + } 1014 + 1015 + handler := s.PetStoreService.PurchasePet 1016 + if s.interceptor != nil { 1017 + handler = func(ctx context.Context, req *PurchasePetRequest) (*PurchasePetResponse, error) { 1018 + resp, err := s.interceptor( 1019 + func(ctx context.Context, req interface{}) (interface{}, error) { 1020 + typedReq, ok := req.(*PurchasePetRequest) 1021 + if !ok { 1022 + return nil, twirp.InternalError("failed type assertion req.(*PurchasePetRequest) when calling interceptor") 1023 + } 1024 + return s.PetStoreService.PurchasePet(ctx, typedReq) 1025 + }, 1026 + )(ctx, req) 1027 + if resp != nil { 1028 + typedResp, ok := resp.(*PurchasePetResponse) 1029 + if !ok { 1030 + return nil, twirp.InternalError("failed type assertion resp.(*PurchasePetResponse) when calling interceptor") 1031 + } 1032 + return typedResp, err 1033 + } 1034 + return nil, err 1035 + } 1036 + } 1037 + 1038 + // Call service method 1039 + var respContent *PurchasePetResponse 1040 + func() { 1041 + defer ensurePanicResponses(ctx, resp, s.hooks) 1042 + respContent, err = handler(ctx, reqContent) 1043 + }() 1044 + 1045 + if err != nil { 1046 + s.writeError(ctx, resp, err) 1047 + return 1048 + } 1049 + if respContent == nil { 1050 + s.writeError(ctx, resp, twirp.InternalError("received a nil *PurchasePetResponse and nil error while calling PurchasePet. nil responses are not supported")) 1051 + return 1052 + } 1053 + 1054 + ctx = callResponsePrepared(ctx, s.hooks) 1055 + 1056 + respBytes, err := proto.Marshal(respContent) 1057 + if err != nil { 1058 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 1059 + return 1060 + } 1061 + 1062 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 1063 + resp.Header().Set("Content-Type", "application/protobuf") 1064 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 1065 + resp.WriteHeader(http.StatusOK) 1066 + if n, err := resp.Write(respBytes); err != nil { 1067 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1068 + twerr := twirp.NewError(twirp.Unknown, msg) 1069 + ctx = callError(ctx, s.hooks, twerr) 1070 + } 1071 + callResponseSent(ctx, s.hooks) 1072 + } 1073 + 1074 + func (s *petStoreServiceServer) ServiceDescriptor() ([]byte, int) { 1075 + return twirpFileDescriptor0, 0 1076 + } 1077 + 1078 + func (s *petStoreServiceServer) ProtocGenTwirpVersion() string { 1079 + return "v8.1.3" 1080 + } 1081 + 1082 + // PathPrefix returns the base service path, in the form: "/<prefix>/<package>.<Service>/" 1083 + // that is everything in a Twirp route except for the <Method>. This can be used for routing, 1084 + // for example to identify the requests that are targeted to this service in a mux. 1085 + func (s *petStoreServiceServer) PathPrefix() string { 1086 + return baseServicePath(s.pathPrefix, "pet.v1", "PetStoreService") 1087 + } 1088 + 1089 + // ===== 1090 + // Utils 1091 + // ===== 1092 + 1093 + // HTTPClient is the interface used by generated clients to send HTTP requests. 1094 + // It is fulfilled by *(net/http).Client, which is sufficient for most users. 1095 + // Users can provide their own implementation for special retry policies. 1096 + // 1097 + // HTTPClient implementations should not follow redirects. Redirects are 1098 + // automatically disabled if *(net/http).Client is passed to client 1099 + // constructors. See the withoutRedirects function in this file for more 1100 + // details. 1101 + type HTTPClient interface { 1102 + Do(req *http.Request) (*http.Response, error) 1103 + } 1104 + 1105 + // TwirpServer is the interface generated server structs will support: they're 1106 + // HTTP handlers with additional methods for accessing metadata about the 1107 + // service. Those accessors are a low-level API for building reflection tools. 1108 + // Most people can think of TwirpServers as just http.Handlers. 1109 + type TwirpServer interface { 1110 + http.Handler 1111 + 1112 + // ServiceDescriptor returns gzipped bytes describing the .proto file that 1113 + // this service was generated from. Once unzipped, the bytes can be 1114 + // unmarshalled as a 1115 + // google.golang.org/protobuf/types/descriptorpb.FileDescriptorProto. 1116 + // 1117 + // The returned integer is the index of this particular service within that 1118 + // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a 1119 + // low-level field, expected to be used for reflection. 1120 + ServiceDescriptor() ([]byte, int) 1121 + 1122 + // ProtocGenTwirpVersion is the semantic version string of the version of 1123 + // twirp used to generate this file. 1124 + ProtocGenTwirpVersion() string 1125 + 1126 + // PathPrefix returns the HTTP URL path prefix for all methods handled by this 1127 + // service. This can be used with an HTTP mux to route Twirp requests. 1128 + // The path prefix is in the form: "/<prefix>/<package>.<Service>/" 1129 + // that is, everything in a Twirp route except for the <Method> at the end. 1130 + PathPrefix() string 1131 + } 1132 + 1133 + func newServerOpts(opts []interface{}) *twirp.ServerOptions { 1134 + serverOpts := &twirp.ServerOptions{} 1135 + for _, opt := range opts { 1136 + switch o := opt.(type) { 1137 + case twirp.ServerOption: 1138 + o(serverOpts) 1139 + case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument 1140 + twirp.WithServerHooks(o)(serverOpts) 1141 + case nil: // backwards compatibility, allow nil value for the argument 1142 + continue 1143 + default: 1144 + panic(fmt.Sprintf("Invalid option type %T, please use a twirp.ServerOption", o)) 1145 + } 1146 + } 1147 + return serverOpts 1148 + } 1149 + 1150 + // WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta). 1151 + // Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks. 1152 + // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) 1153 + func WriteError(resp http.ResponseWriter, err error) { 1154 + writeError(context.Background(), resp, err, nil) 1155 + } 1156 + 1157 + // writeError writes Twirp errors in the response and triggers hooks. 1158 + func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { 1159 + // Convert to a twirp.Error. Non-twirp errors are converted to internal errors. 1160 + var twerr twirp.Error 1161 + if !errors.As(err, &twerr) { 1162 + twerr = twirp.InternalErrorWith(err) 1163 + } 1164 + 1165 + statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) 1166 + ctx = ctxsetters.WithStatusCode(ctx, statusCode) 1167 + ctx = callError(ctx, hooks, twerr) 1168 + 1169 + respBody := marshalErrorToJSON(twerr) 1170 + 1171 + resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON 1172 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBody))) 1173 + resp.WriteHeader(statusCode) // set HTTP status code and send response 1174 + 1175 + _, writeErr := resp.Write(respBody) 1176 + if writeErr != nil { 1177 + // We have three options here. We could log the error, call the Error 1178 + // hook, or just silently ignore the error. 1179 + // 1180 + // Logging is unacceptable because we don't have a user-controlled 1181 + // logger; writing out to stderr without permission is too rude. 1182 + // 1183 + // Calling the Error hook would confuse users: it would mean the Error 1184 + // hook got called twice for one request, which is likely to lead to 1185 + // duplicated log messages and metrics, no matter how well we document 1186 + // the behavior. 1187 + // 1188 + // Silently ignoring the error is our least-bad option. It's highly 1189 + // likely that the connection is broken and the original 'err' says 1190 + // so anyway. 1191 + _ = writeErr 1192 + } 1193 + 1194 + callResponseSent(ctx, hooks) 1195 + } 1196 + 1197 + // sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed. 1198 + // If the URL is unparsable, the baseURL is returned unchanged. 1199 + func sanitizeBaseURL(baseURL string) string { 1200 + u, err := url.Parse(baseURL) 1201 + if err != nil { 1202 + return baseURL // invalid URL will fail later when making requests 1203 + } 1204 + if u.Scheme == "" { 1205 + u.Scheme = "http" 1206 + } 1207 + return u.String() 1208 + } 1209 + 1210 + // baseServicePath composes the path prefix for the service (without <Method>). 1211 + // e.g.: baseServicePath("/twirp", "my.pkg", "MyService") 1212 + // 1213 + // returns => "/twirp/my.pkg.MyService/" 1214 + // 1215 + // e.g.: baseServicePath("", "", "MyService") 1216 + // 1217 + // returns => "/MyService/" 1218 + func baseServicePath(prefix, pkg, service string) string { 1219 + fullServiceName := service 1220 + if pkg != "" { 1221 + fullServiceName = pkg + "." + service 1222 + } 1223 + return path.Join("/", prefix, fullServiceName) + "/" 1224 + } 1225 + 1226 + // parseTwirpPath extracts path components form a valid Twirp route. 1227 + // Expected format: "[<prefix>]/<package>.<Service>/<Method>" 1228 + // e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat") 1229 + func parseTwirpPath(path string) (string, string, string) { 1230 + parts := strings.Split(path, "/") 1231 + if len(parts) < 2 { 1232 + return "", "", "" 1233 + } 1234 + method := parts[len(parts)-1] 1235 + pkgService := parts[len(parts)-2] 1236 + prefix := strings.Join(parts[0:len(parts)-2], "/") 1237 + return prefix, pkgService, method 1238 + } 1239 + 1240 + // getCustomHTTPReqHeaders retrieves a copy of any headers that are set in 1241 + // a context through the twirp.WithHTTPRequestHeaders function. 1242 + // If there are no headers set, or if they have the wrong type, nil is returned. 1243 + func getCustomHTTPReqHeaders(ctx context.Context) http.Header { 1244 + header, ok := twirp.HTTPRequestHeaders(ctx) 1245 + if !ok || header == nil { 1246 + return nil 1247 + } 1248 + copied := make(http.Header) 1249 + for k, vv := range header { 1250 + if vv == nil { 1251 + copied[k] = nil 1252 + continue 1253 + } 1254 + copied[k] = make([]string, len(vv)) 1255 + copy(copied[k], vv) 1256 + } 1257 + return copied 1258 + } 1259 + 1260 + // newRequest makes an http.Request from a client, adding common headers. 1261 + func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { 1262 + req, err := http.NewRequest("POST", url, reqBody) 1263 + if err != nil { 1264 + return nil, err 1265 + } 1266 + req = req.WithContext(ctx) 1267 + if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { 1268 + req.Header = customHeader 1269 + } 1270 + req.Header.Set("Accept", contentType) 1271 + req.Header.Set("Content-Type", contentType) 1272 + req.Header.Set("Twirp-Version", "v8.1.3") 1273 + return req, nil 1274 + } 1275 + 1276 + // JSON serialization for errors 1277 + type twerrJSON struct { 1278 + Code string `json:"code"` 1279 + Msg string `json:"msg"` 1280 + Meta map[string]string `json:"meta,omitempty"` 1281 + } 1282 + 1283 + // marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. 1284 + // If serialization fails, it will use a descriptive Internal error instead. 1285 + func marshalErrorToJSON(twerr twirp.Error) []byte { 1286 + // make sure that msg is not too large 1287 + msg := twerr.Msg() 1288 + if len(msg) > 1e6 { 1289 + msg = msg[:1e6] 1290 + } 1291 + 1292 + tj := twerrJSON{ 1293 + Code: string(twerr.Code()), 1294 + Msg: msg, 1295 + Meta: twerr.MetaMap(), 1296 + } 1297 + 1298 + buf, err := json.Marshal(&tj) 1299 + if err != nil { 1300 + buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback 1301 + } 1302 + 1303 + return buf 1304 + } 1305 + 1306 + // errorFromResponse builds a twirp.Error from a non-200 HTTP response. 1307 + // If the response has a valid serialized Twirp error, then it's returned. 1308 + // If not, the response status code is used to generate a similar twirp 1309 + // error. See twirpErrorFromIntermediary for more info on intermediary errors. 1310 + func errorFromResponse(resp *http.Response) twirp.Error { 1311 + statusCode := resp.StatusCode 1312 + statusText := http.StatusText(statusCode) 1313 + 1314 + if isHTTPRedirect(statusCode) { 1315 + // Unexpected redirect: it must be an error from an intermediary. 1316 + // Twirp clients don't follow redirects automatically, Twirp only handles 1317 + // POST requests, redirects should only happen on GET and HEAD requests. 1318 + location := resp.Header.Get("Location") 1319 + msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) 1320 + return twirpErrorFromIntermediary(statusCode, msg, location) 1321 + } 1322 + 1323 + respBodyBytes, err := io.ReadAll(resp.Body) 1324 + if err != nil { 1325 + return wrapInternal(err, "failed to read server error response body") 1326 + } 1327 + 1328 + var tj twerrJSON 1329 + dec := json.NewDecoder(bytes.NewReader(respBodyBytes)) 1330 + dec.DisallowUnknownFields() 1331 + if err := dec.Decode(&tj); err != nil || tj.Code == "" { 1332 + // Invalid JSON response; it must be an error from an intermediary. 1333 + msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) 1334 + return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) 1335 + } 1336 + 1337 + errorCode := twirp.ErrorCode(tj.Code) 1338 + if !twirp.IsValidErrorCode(errorCode) { 1339 + msg := "invalid type returned from server error response: " + tj.Code 1340 + return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes)) 1341 + } 1342 + 1343 + twerr := twirp.NewError(errorCode, tj.Msg) 1344 + for k, v := range tj.Meta { 1345 + twerr = twerr.WithMeta(k, v) 1346 + } 1347 + return twerr 1348 + } 1349 + 1350 + // twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. 1351 + // The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. 1352 + // Returned twirp Errors have some additional metadata for inspection. 1353 + func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { 1354 + var code twirp.ErrorCode 1355 + if isHTTPRedirect(status) { // 3xx 1356 + code = twirp.Internal 1357 + } else { 1358 + switch status { 1359 + case 400: // Bad Request 1360 + code = twirp.Internal 1361 + case 401: // Unauthorized 1362 + code = twirp.Unauthenticated 1363 + case 403: // Forbidden 1364 + code = twirp.PermissionDenied 1365 + case 404: // Not Found 1366 + code = twirp.BadRoute 1367 + case 429: // Too Many Requests 1368 + code = twirp.ResourceExhausted 1369 + case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout 1370 + code = twirp.Unavailable 1371 + default: // All other codes 1372 + code = twirp.Unknown 1373 + } 1374 + } 1375 + 1376 + twerr := twirp.NewError(code, msg) 1377 + twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary 1378 + twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) 1379 + if isHTTPRedirect(status) { 1380 + twerr = twerr.WithMeta("location", bodyOrLocation) 1381 + } else { 1382 + twerr = twerr.WithMeta("body", bodyOrLocation) 1383 + } 1384 + return twerr 1385 + } 1386 + 1387 + func isHTTPRedirect(status int) bool { 1388 + return status >= 300 && status <= 399 1389 + } 1390 + 1391 + // wrapInternal wraps an error with a prefix as an Internal error. 1392 + // The original error cause is accessible by github.com/pkg/errors.Cause. 1393 + func wrapInternal(err error, prefix string) twirp.Error { 1394 + return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err}) 1395 + } 1396 + 1397 + type wrappedError struct { 1398 + prefix string 1399 + cause error 1400 + } 1401 + 1402 + func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() } 1403 + func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As 1404 + func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors 1405 + 1406 + // ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal 1407 + // error response (status 500), and error hooks are properly called with the panic wrapped as an error. 1408 + // The panic is re-raised so it can be handled normally with middleware. 1409 + func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) { 1410 + if r := recover(); r != nil { 1411 + // Wrap the panic as an error so it can be passed to error hooks. 1412 + // The original error is accessible from error hooks, but not visible in the response. 1413 + err := errFromPanic(r) 1414 + twerr := &internalWithCause{msg: "Internal service panic", cause: err} 1415 + // Actually write the error 1416 + writeError(ctx, resp, twerr, hooks) 1417 + // If possible, flush the error to the wire. 1418 + f, ok := resp.(http.Flusher) 1419 + if ok { 1420 + f.Flush() 1421 + } 1422 + 1423 + panic(r) 1424 + } 1425 + } 1426 + 1427 + // errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error. 1428 + func errFromPanic(p interface{}) error { 1429 + if err, ok := p.(error); ok { 1430 + return err 1431 + } 1432 + return fmt.Errorf("panic: %v", p) 1433 + } 1434 + 1435 + // internalWithCause is a Twirp Internal error wrapping an original error cause, 1436 + // but the original error message is not exposed on Msg(). The original error 1437 + // can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap 1438 + type internalWithCause struct { 1439 + msg string 1440 + cause error 1441 + } 1442 + 1443 + func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As 1444 + func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors 1445 + func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() } 1446 + func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal } 1447 + func (e *internalWithCause) Msg() string { return e.msg } 1448 + func (e *internalWithCause) Meta(key string) string { return "" } 1449 + func (e *internalWithCause) MetaMap() map[string]string { return nil } 1450 + func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e } 1451 + 1452 + // malformedRequestError is used when the twirp server cannot unmarshal a request 1453 + func malformedRequestError(msg string) twirp.Error { 1454 + return twirp.NewError(twirp.Malformed, msg) 1455 + } 1456 + 1457 + // badRouteError is used when the twirp server cannot route a request 1458 + func badRouteError(msg string, method, url string) twirp.Error { 1459 + err := twirp.NewError(twirp.BadRoute, msg) 1460 + err = err.WithMeta("twirp_invalid_route", method+" "+url) 1461 + return err 1462 + } 1463 + 1464 + // withoutRedirects makes sure that the POST request can not be redirected. 1465 + // The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or 1466 + // 303 response, and also 301s in go1.8. It redirects by making a second request, changing the 1467 + // method to GET and removing the body. This produces very confusing error messages, so instead we 1468 + // set a redirect policy that always errors. This stops Go from executing the redirect. 1469 + // 1470 + // We have to be a little careful in case the user-provided http.Client has its own CheckRedirect 1471 + // policy - if so, we'll run through that policy first. 1472 + // 1473 + // Because this requires modifying the http.Client, we make a new copy of the client and return it. 1474 + func withoutRedirects(in *http.Client) *http.Client { 1475 + copy := *in 1476 + copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { 1477 + if in.CheckRedirect != nil { 1478 + // Run the input's redirect if it exists, in case it has side effects, but ignore any error it 1479 + // returns, since we want to use ErrUseLastResponse. 1480 + err := in.CheckRedirect(req, via) 1481 + _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. 1482 + } 1483 + return http.ErrUseLastResponse 1484 + } 1485 + return &copy 1486 + } 1487 + 1488 + // doProtobufRequest makes a Protobuf request to the remote Twirp service. 1489 + func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { 1490 + reqBodyBytes, err := proto.Marshal(in) 1491 + if err != nil { 1492 + return ctx, wrapInternal(err, "failed to marshal proto request") 1493 + } 1494 + reqBody := bytes.NewBuffer(reqBodyBytes) 1495 + if err = ctx.Err(); err != nil { 1496 + return ctx, wrapInternal(err, "aborted because context was done") 1497 + } 1498 + 1499 + req, err := newRequest(ctx, url, reqBody, "application/protobuf") 1500 + if err != nil { 1501 + return ctx, wrapInternal(err, "could not build request") 1502 + } 1503 + ctx, err = callClientRequestPrepared(ctx, hooks, req) 1504 + if err != nil { 1505 + return ctx, err 1506 + } 1507 + 1508 + req = req.WithContext(ctx) 1509 + resp, err := client.Do(req) 1510 + if err != nil { 1511 + return ctx, wrapInternal(err, "failed to do request") 1512 + } 1513 + defer func() { _ = resp.Body.Close() }() 1514 + 1515 + if err = ctx.Err(); err != nil { 1516 + return ctx, wrapInternal(err, "aborted because context was done") 1517 + } 1518 + 1519 + if resp.StatusCode != 200 { 1520 + return ctx, errorFromResponse(resp) 1521 + } 1522 + 1523 + respBodyBytes, err := io.ReadAll(resp.Body) 1524 + if err != nil { 1525 + return ctx, wrapInternal(err, "failed to read response body") 1526 + } 1527 + if err = ctx.Err(); err != nil { 1528 + return ctx, wrapInternal(err, "aborted because context was done") 1529 + } 1530 + 1531 + if err = proto.Unmarshal(respBodyBytes, out); err != nil { 1532 + return ctx, wrapInternal(err, "failed to unmarshal proto response") 1533 + } 1534 + return ctx, nil 1535 + } 1536 + 1537 + // doJSONRequest makes a JSON request to the remote Twirp service. 1538 + func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) { 1539 + marshaler := &protojson.MarshalOptions{UseProtoNames: true} 1540 + reqBytes, err := marshaler.Marshal(in) 1541 + if err != nil { 1542 + return ctx, wrapInternal(err, "failed to marshal json request") 1543 + } 1544 + if err = ctx.Err(); err != nil { 1545 + return ctx, wrapInternal(err, "aborted because context was done") 1546 + } 1547 + 1548 + req, err := newRequest(ctx, url, bytes.NewReader(reqBytes), "application/json") 1549 + if err != nil { 1550 + return ctx, wrapInternal(err, "could not build request") 1551 + } 1552 + ctx, err = callClientRequestPrepared(ctx, hooks, req) 1553 + if err != nil { 1554 + return ctx, err 1555 + } 1556 + 1557 + req = req.WithContext(ctx) 1558 + resp, err := client.Do(req) 1559 + if err != nil { 1560 + return ctx, wrapInternal(err, "failed to do request") 1561 + } 1562 + 1563 + defer func() { 1564 + cerr := resp.Body.Close() 1565 + if err == nil && cerr != nil { 1566 + err = wrapInternal(cerr, "failed to close response body") 1567 + } 1568 + }() 1569 + 1570 + if err = ctx.Err(); err != nil { 1571 + return ctx, wrapInternal(err, "aborted because context was done") 1572 + } 1573 + 1574 + if resp.StatusCode != 200 { 1575 + return ctx, errorFromResponse(resp) 1576 + } 1577 + 1578 + d := json.NewDecoder(resp.Body) 1579 + rawRespBody := json.RawMessage{} 1580 + if err := d.Decode(&rawRespBody); err != nil { 1581 + return ctx, wrapInternal(err, "failed to unmarshal json response") 1582 + } 1583 + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 1584 + if err = unmarshaler.Unmarshal(rawRespBody, out); err != nil { 1585 + return ctx, wrapInternal(err, "failed to unmarshal json response") 1586 + } 1587 + if err = ctx.Err(); err != nil { 1588 + return ctx, wrapInternal(err, "aborted because context was done") 1589 + } 1590 + return ctx, nil 1591 + } 1592 + 1593 + // Call twirp.ServerHooks.RequestReceived if the hook is available 1594 + func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { 1595 + if h == nil || h.RequestReceived == nil { 1596 + return ctx, nil 1597 + } 1598 + return h.RequestReceived(ctx) 1599 + } 1600 + 1601 + // Call twirp.ServerHooks.RequestRouted if the hook is available 1602 + func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { 1603 + if h == nil || h.RequestRouted == nil { 1604 + return ctx, nil 1605 + } 1606 + return h.RequestRouted(ctx) 1607 + } 1608 + 1609 + // Call twirp.ServerHooks.ResponsePrepared if the hook is available 1610 + func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { 1611 + if h == nil || h.ResponsePrepared == nil { 1612 + return ctx 1613 + } 1614 + return h.ResponsePrepared(ctx) 1615 + } 1616 + 1617 + // Call twirp.ServerHooks.ResponseSent if the hook is available 1618 + func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { 1619 + if h == nil || h.ResponseSent == nil { 1620 + return 1621 + } 1622 + h.ResponseSent(ctx) 1623 + } 1624 + 1625 + // Call twirp.ServerHooks.Error if the hook is available 1626 + func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { 1627 + if h == nil || h.Error == nil { 1628 + return ctx 1629 + } 1630 + return h.Error(ctx, err) 1631 + } 1632 + 1633 + func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) { 1634 + if h == nil || h.ResponseReceived == nil { 1635 + return 1636 + } 1637 + h.ResponseReceived(ctx) 1638 + } 1639 + 1640 + func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) { 1641 + if h == nil || h.RequestPrepared == nil { 1642 + return ctx, nil 1643 + } 1644 + return h.RequestPrepared(ctx, req) 1645 + } 1646 + 1647 + func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) { 1648 + if h == nil || h.Error == nil { 1649 + return 1650 + } 1651 + h.Error(ctx, err) 1652 + } 1653 + 1654 + var twirpFileDescriptor0 = []byte{ 1655 + // 781 bytes of a gzipped FileDescriptorProto 1656 + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xdb, 0x46, 1657 + 0x10, 0xb6, 0x44, 0x5b, 0xb6, 0x47, 0x8d, 0x4d, 0x6c, 0x6d, 0x97, 0x61, 0xda, 0xc0, 0x51, 0x81, 1658 + 0x22, 0x0d, 0x6a, 0x12, 0x52, 0x7b, 0x69, 0x2e, 0x85, 0x1c, 0xa9, 0x8e, 0xd1, 0x24, 0x26, 0x28, 1659 + 0x55, 0x48, 0x83, 0x00, 0xc2, 0x4a, 0x9a, 0x50, 0x44, 0x29, 0xee, 0x66, 0x39, 0x54, 0xa0, 0xd7, 1660 + 0x29, 0x7a, 0xea, 0xa3, 0xf4, 0x05, 0x7a, 0xef, 0xad, 0x7d, 0x8a, 0x62, 0xf9, 0xa3, 0xe8, 0x27, 1661 + 0x80, 0x9b, 0xdb, 0x68, 0xbe, 0x6f, 0x47, 0xdf, 0x7c, 0x33, 0x43, 0x30, 0x25, 0x92, 0x3b, 0x6f, 1662 + 0xba, 0x12, 0xc9, 0x91, 0x4a, 0x90, 0x60, 0x35, 0x1d, 0xce, 0x9b, 0xf6, 0x7d, 0xc9, 0x17, 0x33, 1663 + 0x8c, 0x35, 0xca, 0x23, 0x39, 0xe5, 0x4d, 0xb7, 0x48, 0xe4, 0x3c, 0xdb, 0x0e, 0x84, 0x08, 0x22, 1664 + 0x74, 0x69, 0x21, 0xd1, 0x9d, 0x70, 0x42, 0x0a, 0x67, 0x58, 0x60, 0xf7, 0x0a, 0x2c, 0xfb, 0x35, 1665 + 0x4a, 0xdf, 0xb8, 0x38, 0x93, 0xb4, 0x28, 0xc0, 0xbb, 0x9b, 0x20, 0x8f, 0x4b, 0xe8, 0xfe, 0x26, 1666 + 0xf4, 0x4e, 0x71, 0x29, 0x51, 0x25, 0x05, 0xfe, 0xf9, 0x26, 0x9e, 0x90, 0x4a, 0xc7, 0x85, 0xa2, 1667 + 0xc6, 0x57, 0x70, 0xe7, 0x0a, 0xc9, 0x43, 0xf2, 0xf1, 0x6d, 0x8a, 0x09, 0xb1, 0x53, 0xd0, 0xcd, 1668 + 0x0c, 0xc3, 0x89, 0x55, 0x39, 0xaf, 0x3c, 0x3c, 0xf4, 0xf7, 0x24, 0xd2, 0xf5, 0xa4, 0xe1, 0xc2, 1669 + 0x51, 0xc9, 0x4b, 0xa4, 0x88, 0x13, 0x64, 0x5f, 0x80, 0x21, 0x91, 0x32, 0x56, 0xbd, 0x55, 0x77, 1670 + 0x72, 0x07, 0x1c, 0xcd, 0xd0, 0xf9, 0xc6, 0x0d, 0xdc, 0xf1, 0xd2, 0xd5, 0xc2, 0x8f, 0xe0, 0x40, 1671 + 0x17, 0xd6, 0xad, 0x67, 0x8f, 0x8e, 0x5a, 0xc7, 0x2b, 0x8f, 0xfa, 0x0b, 0x89, 0xfe, 0xbe, 0xcc, 1672 + 0x03, 0xc6, 0x60, 0x37, 0xe6, 0x33, 0xb4, 0xaa, 0x99, 0x84, 0x2c, 0xd6, 0x0a, 0xca, 0x82, 0xff, 1673 + 0x4f, 0xc1, 0xd7, 0x60, 0x76, 0x30, 0x42, 0xc2, 0xdb, 0xbb, 0x7b, 0x05, 0xcc, 0x4b, 0xd5, 0x78, 1674 + 0xca, 0x93, 0xdb, 0xc9, 0xec, 0x02, 0xf6, 0x84, 0x9a, 0xa0, 0xca, 0xd4, 0xd5, 0x5b, 0x9f, 0x39, 1675 + 0xe5, 0x8c, 0xcb, 0xa1, 0x3b, 0x37, 0x1a, 0xf6, 0x73, 0x56, 0xe3, 0x14, 0x3e, 0x5d, 0xab, 0x9d, 1676 + 0x8b, 0x6f, 0xfc, 0x63, 0x80, 0xe1, 0xe1, 0xc7, 0xd9, 0xf2, 0x0d, 0x1c, 0x96, 0xdc, 0xc4, 0xaa, 1677 + 0x9e, 0x1b, 0x1f, 0x22, 0x1f, 0x14, 0xe4, 0x84, 0x3d, 0x03, 0xb3, 0x50, 0x36, 0x94, 0x4a, 0xcc, 1678 + 0x43, 0x2d, 0xf9, 0x2c, 0xfb, 0x87, 0x07, 0xdb, 0x92, 0xbd, 0x3c, 0xe1, 0x15, 0x44, 0xff, 0x58, 1679 + 0xae, 0x27, 0x56, 0xcc, 0x30, 0x56, 0xcd, 0x28, 0x27, 0xb5, 0xfb, 0x7e, 0x52, 0xec, 0x3b, 0x80, 1680 + 0xb1, 0x42, 0x4e, 0x38, 0x19, 0x72, 0xb2, 0xf6, 0x32, 0x97, 0x4e, 0x9d, 0x7c, 0x0d, 0x1d, 0x2d, 1681 + 0xde, 0xe9, 0x70, 0xc2, 0x7e, 0x38, 0x43, 0xff, 0xb0, 0x20, 0xb6, 0x89, 0x39, 0xb0, 0x3f, 0x41, 1682 + 0xe2, 0x61, 0x94, 0x58, 0xb5, 0x73, 0xe3, 0x61, 0xbd, 0x75, 0x52, 0x3e, 0x29, 0x37, 0xd7, 0x69, 1683 + 0xc7, 0x0b, 0xbf, 0x24, 0xb1, 0x07, 0x60, 0xcc, 0x91, 0xac, 0xfd, 0xac, 0xfc, 0xaa, 0x0d, 0xce, 1684 + 0x40, 0x6f, 0xc0, 0x1c, 0x89, 0x7d, 0x09, 0xbb, 0x73, 0xa4, 0xc4, 0x3a, 0xc8, 0xea, 0x6d, 0x71, 1685 + 0x32, 0x90, 0xb5, 0xa0, 0x16, 0xf1, 0x11, 0x46, 0x89, 0x85, 0x59, 0x29, 0x7b, 0xeb, 0x6f, 0x9f, 1686 + 0x85, 0x09, 0x0d, 0x78, 0x94, 0xa2, 0x5f, 0x30, 0x75, 0xd7, 0xc4, 0x83, 0xc4, 0x7a, 0x73, 0x6e, 1687 + 0xe8, 0xae, 0x75, 0x6c, 0xdf, 0x05, 0x63, 0x80, 0xb4, 0x34, 0xa4, 0xf2, 0xde, 0x90, 0x47, 0x6f, 1688 + 0x61, 0xbf, 0x18, 0x0f, 0xb3, 0xe0, 0xc4, 0xeb, 0xf6, 0x87, 0xfd, 0x5f, 0xbc, 0xee, 0xf0, 0xe7, 1689 + 0x17, 0x3d, 0xaf, 0xfb, 0xe4, 0xfa, 0xc7, 0xeb, 0x6e, 0xc7, 0xdc, 0x61, 0x26, 0x7c, 0xb2, 0x44, 1690 + 0x9e, 0xb4, 0xfb, 0x66, 0x65, 0x2d, 0xd3, 0xb9, 0xb9, 0x32, 0xab, 0x8c, 0xc1, 0xd1, 0x32, 0xd3, 1691 + 0x7b, 0xd1, 0xfe, 0xa9, 0x6b, 0x1a, 0xec, 0x04, 0xcc, 0x65, 0xee, 0x69, 0xfb, 0x79, 0xaf, 0xdf, 1692 + 0xf5, 0xcd, 0xdd, 0xd6, 0x5f, 0x15, 0x38, 0xf6, 0x90, 0x7a, 0x24, 0x14, 0xf6, 0x50, 0xcd, 0xc3, 1693 + 0x31, 0xb2, 0xef, 0xa1, 0x96, 0xdf, 0x30, 0x3b, 0x2d, 0xad, 0x58, 0xbb, 0x7d, 0xfb, 0x6c, 0x33, 1694 + 0x5d, 0xec, 0xea, 0x0e, 0xfb, 0x01, 0x0e, 0x97, 0xb7, 0xc4, 0xac, 0x92, 0xb6, 0x79, 0x5e, 0xf6, 1695 + 0xd9, 0x96, 0x77, 0x5d, 0xfd, 0x11, 0x6b, 0xec, 0xb0, 0xa7, 0x50, 0x5f, 0xb9, 0x02, 0x66, 0x2f, 1696 + 0x67, 0xb1, 0x75, 0x76, 0xf6, 0xbd, 0x0f, 0x62, 0xa5, 0x94, 0xcb, 0xdf, 0x2b, 0x00, 0x63, 0x31, 1697 + 0x2b, 0x48, 0x97, 0x07, 0x1e, 0xea, 0x25, 0x25, 0xe1, 0x55, 0x5e, 0xf5, 0x82, 0x90, 0xa6, 0xe9, 1698 + 0xc8, 0x19, 0x8b, 0x99, 0x3b, 0x8a, 0xc4, 0xf8, 0x57, 0x9a, 0x2a, 0x91, 0x06, 0x53, 0x97, 0xde, 1699 + 0x85, 0x4a, 0x5e, 0x08, 0x89, 0x31, 0x97, 0xe1, 0x45, 0x80, 0xb1, 0x1b, 0xc6, 0x84, 0x2a, 0xe6, 1700 + 0x91, 0x1b, 0x60, 0x8c, 0x8a, 0x93, 0x50, 0x2e, 0x61, 0x42, 0x13, 0x4e, 0x5c, 0xa7, 0xdc, 0x40, 1701 + 0xb8, 0xf9, 0x57, 0xfe, 0xb7, 0xaa, 0xe1, 0xbd, 0x7c, 0xf9, 0x47, 0xb5, 0x96, 0x6d, 0x4d, 0xf3, 1702 + 0xcf, 0x2c, 0x78, 0x3d, 0x68, 0xfe, 0x5d, 0x65, 0x79, 0xf0, 0xfa, 0xca, 0xbb, 0x7c, 0x8e, 0xc4, 1703 + 0xf5, 0xd3, 0x7f, 0xab, 0x7a, 0xd0, 0x8f, 0x1f, 0x0f, 0x9a, 0xa3, 0x5a, 0x66, 0xc1, 0xb7, 0xff, 1704 + 0x05, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xd6, 0xd6, 0x2e, 0x2d, 0x06, 0x00, 0x00, 1705 + }
+24
cmd/twirp-openapi-gen/internal/generator/testdata/paymentapis/payment/v1alpha1/payment.proto
··· 1 + syntax = "proto3"; 2 + 3 + package payment.v1alpha1; 4 + 5 + option go_package = "xeiaso.net/v4/cmd/twirp-openapi-gen/internal/generator/testdata/gen/go/payment/v1alpha1"; 6 + 7 + import "google/type/money.proto"; 8 + 9 + // PaymentProvider represents the supported set 10 + // of payment providers. 11 + enum PaymentProvider { 12 + PAYMENT_PROVIDER_UNSPECIFIED = 0; 13 + PAYMENT_PROVIDER_STRIPE = 1; 14 + PAYMENT_PROVIDER_PAYPAL = 2; 15 + PAYMENT_PROVIDER_APPLE = 3; 16 + } 17 + 18 + // Order represents a monetary order. 19 + message Order { 20 + string order_id = 1; 21 + string recipient_id = 2; 22 + google.type.Money amount = 3; 23 + PaymentProvider payment_provider = 4; 24 + }
+321
cmd/twirp-openapi-gen/internal/generator/testdata/pet-api-doc.json
··· 1 + { 2 + "components": { 3 + "schemas": { 4 + "google.protobuf.Any": { 5 + "description": "\nThe JSON representation of an Any value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field @type which contains the type URL. Example:\n\n\tpackage google.profile;\n\tmessage Person {\n\t string first_name = 1;\n\t string last_name = 2;\n\t}\n\n\t{\n\t \"@type\": \"type.googleapis.com/google.profile.Person\",\n\t \"firstName\": \u003cstring\u003e,\n\t \"lastName\": \u003cstring\u003e\n\t}\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\nvalue which holds the custom JSON in addition to the @type\nfield. Example (for message [google.protobuf.Duration][]):\n\n\t{\n\t \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n\t \"value\": \"1.212s\"\n\t}\n", 6 + "properties": { 7 + "@type": { 8 + "type": "string" 9 + } 10 + }, 11 + "type": "object" 12 + }, 13 + "google.protobuf.ListValue": { 14 + "description": "\nListValue is a wrapper around a repeated field of values.\nThe JSON representation for ListValue is JSON array.\n", 15 + "items": { 16 + "oneOf": [ 17 + { 18 + "type": "string" 19 + }, 20 + { 21 + "type": "number" 22 + }, 23 + { 24 + "type": "integer" 25 + }, 26 + { 27 + "type": "boolean" 28 + }, 29 + { 30 + "type": "array" 31 + }, 32 + { 33 + "type": "object" 34 + } 35 + ] 36 + }, 37 + "type": "array" 38 + }, 39 + "google.type.Money": { 40 + "description": "Represents an amount of money with its currency type", 41 + "properties": { 42 + "currency_code": { 43 + "description": "The 3-letter currency code defined in ISO 4217.", 44 + "type": "string" 45 + }, 46 + "nanos": { 47 + "description": "Number of nano (10^-9) units of the amount.\nThe value must be between -999,999,999 and +999,999,999 inclusive.\nIf `units` is positive, `nanos` must be positive or zero.\nIf `units` is zero, `nanos` can be positive, zero, or negative.\nIf `units` is negative, `nanos` must be negative or zero.\nFor example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.", 48 + "format": "int32", 49 + "type": "integer" 50 + }, 51 + "units": { 52 + "description": "The whole units of the amount.\nFor example if `currencyCode` is `\"USD\"`, then 1 unit is one US dollar.", 53 + "format": "int64", 54 + "type": "integer" 55 + } 56 + }, 57 + "type": "object" 58 + }, 59 + "payment.v1alpha1.Order": { 60 + "description": "Order represents a monetary order.", 61 + "properties": { 62 + "amount": { 63 + "$ref": "#/components/schemas/google.type.Money" 64 + }, 65 + "order_id": { 66 + "type": "string" 67 + }, 68 + "payment_provider": { 69 + "$ref": "#/components/schemas/payment.v1alpha1.PaymentProvider" 70 + }, 71 + "recipient_id": { 72 + "type": "string" 73 + } 74 + }, 75 + "type": "object" 76 + }, 77 + "payment.v1alpha1.PaymentProvider": { 78 + "description": "PaymentProvider represents the supported set\nof payment providers.", 79 + "enum": [ 80 + "PAYMENT_PROVIDER_UNSPECIFIED", 81 + "PAYMENT_PROVIDER_STRIPE", 82 + "PAYMENT_PROVIDER_PAYPAL", 83 + "PAYMENT_PROVIDER_APPLE" 84 + ], 85 + "type": "string" 86 + }, 87 + "pet.v1.DeletePetRequest": { 88 + "properties": { 89 + "pet_id": { 90 + "type": "string" 91 + } 92 + }, 93 + "type": "object" 94 + }, 95 + "pet.v1.GetPetRequest": { 96 + "description": "GetPetRequest is the request object for GetPet\nThe message accepts a pet id as an input", 97 + "properties": { 98 + "pet_id": { 99 + "type": "string" 100 + } 101 + }, 102 + "type": "object" 103 + }, 104 + "pet.v1.GetPetResponse": { 105 + "properties": { 106 + "pet": { 107 + "$ref": "#/components/schemas/pet.v1.Pet" 108 + } 109 + }, 110 + "type": "object" 111 + }, 112 + "pet.v1.Pet": { 113 + "description": "Pet represents a pet in the pet store.", 114 + "properties": { 115 + "created_at": { 116 + "format": "date-time", 117 + "type": "string" 118 + }, 119 + "details": { 120 + "items": { 121 + "$ref": "#/components/schemas/google.protobuf.Any" 122 + }, 123 + "type": "array" 124 + }, 125 + "labels": { 126 + "$ref": "#/components/schemas/google.protobuf.ListValue" 127 + }, 128 + "name": { 129 + "type": "string" 130 + }, 131 + "payment_provider": { 132 + "$ref": "#/components/schemas/payment.v1alpha1.PaymentProvider" 133 + }, 134 + "pet_id": { 135 + "description": "pet_id is an auto-generated id for the pet\nthe id uniquely identifies a pet in the system", 136 + "type": "string" 137 + }, 138 + "pet_type": { 139 + "$ref": "#/components/schemas/pet.v1.PetType" 140 + }, 141 + "pet_types": { 142 + "items": { 143 + "$ref": "#/components/schemas/pet.v1.PetType" 144 + }, 145 + "type": "array" 146 + }, 147 + "tags": { 148 + "items": { 149 + "type": "string" 150 + }, 151 + "type": "array" 152 + }, 153 + "vet": { 154 + "$ref": "#/components/schemas/pet.v1.Vet" 155 + }, 156 + "vets": { 157 + "items": { 158 + "$ref": "#/components/schemas/pet.v1.Vet" 159 + }, 160 + "type": "array" 161 + } 162 + }, 163 + "type": "object" 164 + }, 165 + "pet.v1.PetType": { 166 + "description": "PetType represents the different types of pets in the pet store.", 167 + "enum": [ 168 + "PET_TYPE_UNSPECIFIED", 169 + "PET_TYPE_CAT", 170 + "PET_TYPE_DOG", 171 + "PET_TYPE_SNAKE", 172 + "PET_TYPE_HAMSTER" 173 + ], 174 + "type": "string" 175 + }, 176 + "pet.v1.PurchasePetRequest": { 177 + "properties": { 178 + "order": { 179 + "$ref": "#/components/schemas/payment.v1alpha1.Order" 180 + }, 181 + "pet_id": { 182 + "type": "string" 183 + } 184 + }, 185 + "type": "object" 186 + }, 187 + "pet.v1.PurchasePetResponse": { 188 + "type": "object" 189 + }, 190 + "pet.v1.PutPetRequest": { 191 + "properties": { 192 + "name": { 193 + "type": "string" 194 + }, 195 + "pet_type": { 196 + "$ref": "#/components/schemas/pet.v1.PetType" 197 + } 198 + }, 199 + "type": "object" 200 + }, 201 + "pet.v1.PutPetResponse": { 202 + "properties": { 203 + "pet": { 204 + "$ref": "#/components/schemas/pet.v1.Pet" 205 + } 206 + }, 207 + "type": "object" 208 + }, 209 + "pet.v1.Vet": { 210 + "properties": { 211 + "name": { 212 + "type": "string" 213 + } 214 + }, 215 + "type": "object" 216 + } 217 + } 218 + }, 219 + "info": { 220 + "title": "Pet API", 221 + "version": "1.0" 222 + }, 223 + "openapi": "3.0.0", 224 + "paths": { 225 + "/pet.v1.PetStoreService/DeletePet": { 226 + "post": { 227 + "requestBody": { 228 + "content": { 229 + "application/json": { 230 + "schema": { 231 + "$ref": "#/components/schemas/pet.v1.DeletePetRequest" 232 + } 233 + } 234 + } 235 + }, 236 + "responses": { 237 + "200": { 238 + "content": { 239 + "application/json": {} 240 + }, 241 + "description": "Success" 242 + } 243 + }, 244 + "summary": "DeletePet" 245 + } 246 + }, 247 + "/pet.v1.PetStoreService/GetPet": { 248 + "post": { 249 + "description": "\nGetPet returns details about a pet\nIt accepts a pet id as an input and returns back the matching pet object", 250 + "requestBody": { 251 + "content": { 252 + "application/json": { 253 + "example": { 254 + "example 0": { 255 + "pet_id": "123" 256 + }, 257 + "example 1": { 258 + "pet_id": "456" 259 + } 260 + }, 261 + "schema": { 262 + "$ref": "#/components/schemas/pet.v1.GetPetRequest" 263 + } 264 + } 265 + } 266 + }, 267 + "responses": { 268 + "200": { 269 + "content": { 270 + "application/json": { 271 + "example": { 272 + "example 0": { 273 + "pet": { 274 + "name": "toby" 275 + } 276 + } 277 + }, 278 + "schema": { 279 + "$ref": "#/components/schemas/pet.v1.GetPetResponse" 280 + } 281 + } 282 + }, 283 + "description": "Success" 284 + } 285 + }, 286 + "summary": "GetPet" 287 + } 288 + }, 289 + "/pet.v1.PetStoreService/PurchasePet": { 290 + "post": { 291 + "requestBody": { 292 + "content": { 293 + "application/json": { 294 + "schema": { 295 + "$ref": "#/components/schemas/pet.v1.PurchasePetRequest" 296 + } 297 + } 298 + } 299 + }, 300 + "responses": { 301 + "200": { 302 + "content": { 303 + "application/json": { 304 + "schema": { 305 + "$ref": "#/components/schemas/pet.v1.PurchasePetResponse" 306 + } 307 + } 308 + }, 309 + "description": "Success" 310 + } 311 + }, 312 + "summary": "PurchasePet" 313 + } 314 + } 315 + }, 316 + "servers": [ 317 + { 318 + "url": "https://petapi.example.com" 319 + } 320 + ] 321 + }
+297
cmd/twirp-openapi-gen/internal/generator/testdata/pet-api-doc.yaml
··· 1 + components: 2 + schemas: 3 + google.protobuf.Any: 4 + description: |4 5 + The JSON representation of an Any value uses the regular 6 + representation of the deserialized, embedded message, with an 7 + additional field @type which contains the type URL. Example: 8 + 9 + package google.profile; 10 + message Person { 11 + string first_name = 1; 12 + string last_name = 2; 13 + } 14 + 15 + { 16 + "@type": "type.googleapis.com/google.profile.Person", 17 + "firstName": <string>, 18 + "lastName": <string> 19 + } 20 + 21 + If the embedded message type is well-known and has a custom JSON 22 + representation, that representation will be embedded adding a field 23 + value which holds the custom JSON in addition to the @type 24 + field. Example (for message [google.protobuf.Duration][]): 25 + 26 + { 27 + "@type": "type.googleapis.com/google.protobuf.Duration", 28 + "value": "1.212s" 29 + } 30 + properties: 31 + '@type': 32 + type: string 33 + type: object 34 + google.protobuf.ListValue: 35 + description: |4 36 + ListValue is a wrapper around a repeated field of values. 37 + The JSON representation for ListValue is JSON array. 38 + items: 39 + oneOf: 40 + - type: string 41 + - type: number 42 + - type: integer 43 + - type: boolean 44 + - type: array 45 + - type: object 46 + type: array 47 + google.protobuf.Struct: 48 + description: "\nStruct represents a structured data value, consisting of fields\nwhich map to dynamically typed values. In some languages, \nStruct might be supported by a native representation. For example,\nin scripting languages like JS a struct is represented as\nan object. The details of that representation are described\ntogether with the proto support for the language.\n\nThe JSON representation for Struct is JSON object.\n" 49 + properties: 50 + fields: 51 + additionalProperties: 52 + $ref: '#/components/schemas/google.protobuf.Value' 53 + description: Unordered map of dynamically typed values. 54 + type: object 55 + type: object 56 + google.protobuf.Value: 57 + description: |4 58 + Value represents a dynamically typed value which can be either 59 + null, a number, a string, a boolean, a recursive struct value, or a 60 + list of values. A producer of value is expected to set one of that 61 + variants, absence of any variant indicates an error. 62 + 63 + The JSON representation for Value is JSON value. 64 + oneOf: 65 + - type: string 66 + - type: number 67 + - type: integer 68 + - type: boolean 69 + - $ref: '#/components/schemas/google.protobuf.Struct' 70 + - $ref: '#/components/schemas/google.protobuf.ListValue' 71 + google.type.Money: 72 + description: Represents an amount of money with its currency type 73 + properties: 74 + currency_code: 75 + description: The 3-letter currency code defined in ISO 4217. 76 + type: string 77 + nanos: 78 + description: |- 79 + Number of nano (10^-9) units of the amount. 80 + The value must be between -999,999,999 and +999,999,999 inclusive. 81 + If `units` is positive, `nanos` must be positive or zero. 82 + If `units` is zero, `nanos` can be positive, zero, or negative. 83 + If `units` is negative, `nanos` must be negative or zero. 84 + For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. 85 + format: int32 86 + type: integer 87 + units: 88 + description: |- 89 + The whole units of the amount. 90 + For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. 91 + format: int64 92 + type: integer 93 + type: object 94 + payment.v1alpha1.Order: 95 + description: Order represents a monetary order. 96 + properties: 97 + amount: 98 + $ref: '#/components/schemas/google.type.Money' 99 + order_id: 100 + type: string 101 + payment_provider: 102 + $ref: '#/components/schemas/payment.v1alpha1.PaymentProvider' 103 + recipient_id: 104 + type: string 105 + type: object 106 + payment.v1alpha1.PaymentProvider: 107 + description: |- 108 + PaymentProvider represents the supported set 109 + of payment providers. 110 + enum: 111 + - PAYMENT_PROVIDER_UNSPECIFIED 112 + - PAYMENT_PROVIDER_STRIPE 113 + - PAYMENT_PROVIDER_PAYPAL 114 + - PAYMENT_PROVIDER_APPLE 115 + type: string 116 + pet.v1.DeletePetRequest: 117 + properties: 118 + pet_id: 119 + type: string 120 + type: object 121 + pet.v1.GetPetRequest: 122 + description: |- 123 + GetPetRequest is the request object for GetPet 124 + The message accepts a pet id as an input 125 + properties: 126 + pet_id: 127 + type: string 128 + type: object 129 + pet.v1.GetPetResponse: 130 + properties: 131 + pet: 132 + $ref: '#/components/schemas/pet.v1.Pet' 133 + type: object 134 + pet.v1.Pet: 135 + description: Pet represents a pet in the pet store. 136 + properties: 137 + created_at: 138 + format: date-time 139 + type: string 140 + details: 141 + items: 142 + $ref: '#/components/schemas/google.protobuf.Any' 143 + type: array 144 + labels: 145 + $ref: '#/components/schemas/google.protobuf.ListValue' 146 + metadata: 147 + $ref: '#/components/schemas/google.protobuf.Struct' 148 + name: 149 + type: string 150 + payment_provider: 151 + $ref: '#/components/schemas/payment.v1alpha1.PaymentProvider' 152 + pet_id: 153 + description: |- 154 + pet_id is an auto-generated id for the pet 155 + the id uniquely identifies a pet in the system 156 + type: string 157 + pet_type: 158 + $ref: '#/components/schemas/pet.v1.PetType' 159 + pet_types: 160 + items: 161 + $ref: '#/components/schemas/pet.v1.PetType' 162 + type: array 163 + tags: 164 + items: 165 + type: string 166 + type: array 167 + vet: 168 + $ref: '#/components/schemas/pet.v1.Vet' 169 + vets: 170 + items: 171 + $ref: '#/components/schemas/pet.v1.Vet' 172 + type: array 173 + type: object 174 + pet.v1.PetType: 175 + description: PetType represents the different types of pets in the pet store. 176 + enum: 177 + - PET_TYPE_UNSPECIFIED 178 + - PET_TYPE_CAT 179 + - PET_TYPE_DOG 180 + - PET_TYPE_SNAKE 181 + - PET_TYPE_HAMSTER 182 + type: string 183 + pet.v1.PurchasePetRequest: 184 + properties: 185 + order: 186 + $ref: '#/components/schemas/payment.v1alpha1.Order' 187 + pet_id: 188 + type: string 189 + type: object 190 + pet.v1.PurchasePetResponse: 191 + type: object 192 + pet.v1.PutPetRequest: 193 + properties: 194 + name: 195 + type: string 196 + pet_type: 197 + $ref: '#/components/schemas/pet.v1.PetType' 198 + type: object 199 + pet.v1.PutPetResponse: 200 + properties: 201 + pet: 202 + $ref: '#/components/schemas/pet.v1.Pet' 203 + type: object 204 + pet.v1.UpdatePetRequest: 205 + properties: 206 + metadata: 207 + $ref: '#/components/schemas/google.protobuf.Struct' 208 + pet_id: 209 + type: string 210 + type: object 211 + pet.v1.UpdatePetResponse: 212 + properties: 213 + pet: 214 + $ref: '#/components/schemas/pet.v1.Pet' 215 + type: object 216 + pet.v1.Vet: 217 + properties: 218 + name: 219 + type: string 220 + type: object 221 + info: 222 + title: Pet API 223 + version: "1.0" 224 + openapi: 3.0.0 225 + paths: 226 + /pet.v1.PetStoreService/DeletePet: 227 + post: 228 + requestBody: 229 + content: 230 + application/json: 231 + schema: 232 + $ref: '#/components/schemas/pet.v1.DeletePetRequest' 233 + responses: 234 + "200": 235 + content: 236 + application/json: {} 237 + description: Success 238 + summary: DeletePet 239 + /pet.v1.PetStoreService/GetPet: 240 + post: 241 + description: |4- 242 + GetPet returns details about a pet 243 + It accepts a pet id as an input and returns back the matching pet object 244 + requestBody: 245 + content: 246 + application/json: 247 + example: 248 + example 0: 249 + pet_id: "123" 250 + example 1: 251 + pet_id: "456" 252 + schema: 253 + $ref: '#/components/schemas/pet.v1.GetPetRequest' 254 + responses: 255 + "200": 256 + content: 257 + application/json: 258 + example: 259 + example 0: 260 + pet: 261 + name: toby 262 + schema: 263 + $ref: '#/components/schemas/pet.v1.GetPetResponse' 264 + description: Success 265 + summary: GetPet 266 + /pet.v1.PetStoreService/PurchasePet: 267 + post: 268 + requestBody: 269 + content: 270 + application/json: 271 + schema: 272 + $ref: '#/components/schemas/pet.v1.PurchasePetRequest' 273 + responses: 274 + "200": 275 + content: 276 + application/json: 277 + schema: 278 + $ref: '#/components/schemas/pet.v1.PurchasePetResponse' 279 + description: Success 280 + summary: PurchasePet 281 + /pet.v1.PetStoreService/UpdatePet: 282 + post: 283 + requestBody: 284 + content: 285 + application/json: 286 + schema: 287 + $ref: '#/components/schemas/pet.v1.UpdatePetRequest' 288 + responses: 289 + "200": 290 + content: 291 + application/json: 292 + schema: 293 + $ref: '#/components/schemas/pet.v1.UpdatePetResponse' 294 + description: Success 295 + summary: UpdatePet 296 + servers: 297 + - url: https://petapi.example.com
+115
cmd/twirp-openapi-gen/internal/generator/testdata/petapis/pet/v1/pet.proto
··· 1 + syntax = "proto3"; 2 + 3 + package pet.v1; 4 + 5 + option go_package = "xeiaso.net/v4/cmd/twirp-openapi-gen/internal/generator/testdata/gen/go/pet/v1"; 6 + 7 + import "payment/v1alpha1/payment.proto"; 8 + import "google/type/datetime.proto"; 9 + import "google/protobuf/empty.proto"; 10 + import "google/protobuf/any.proto"; 11 + import "google/protobuf/wrappers.proto"; 12 + import "google/protobuf/struct.proto"; 13 + 14 + // PetType represents the different types of pets in the pet store. 15 + enum PetType { 16 + PET_TYPE_UNSPECIFIED = 0; 17 + PET_TYPE_CAT = 1; 18 + PET_TYPE_DOG = 2; 19 + PET_TYPE_SNAKE = 3; 20 + PET_TYPE_HAMSTER = 4; 21 + } 22 + 23 + service PetStoreService { 24 + // GetPet returns details about a pet 25 + // It accepts a pet id as an input and returns back the matching pet object 26 + // req-example: { "pet_id": "123" } 27 + // req-example: { "pet_id": "456" } 28 + // res-example: { "pet": {"name": "toby"} } 29 + rpc GetPet(GetPetRequest) returns (GetPetResponse) {} 30 + 31 + rpc DeletePet(DeletePetRequest) returns (google.protobuf.Empty) {} 32 + rpc PurchasePet(PurchasePetRequest) returns (PurchasePetResponse) {} 33 + rpc UpdatePet(UpdatePetRequest) returns (UpdatePetResponse) {} 34 + } 35 + 36 + // GetPetRequest is the request object for GetPet 37 + // The message accepts a pet id as an input 38 + message GetPetRequest { 39 + string pet_id = 1; 40 + } 41 + 42 + message GetPetResponse { 43 + Pet pet = 1; 44 + } 45 + 46 + message PutPetRequest { 47 + PetType pet_type = 1; 48 + string name = 2; 49 + } 50 + 51 + message PutPetResponse { 52 + Pet pet = 1; 53 + } 54 + 55 + message DeletePetRequest { 56 + string pet_id = 1; 57 + } 58 + 59 + message PurchasePetRequest { 60 + string pet_id = 1; 61 + payment.v1alpha1.Order order = 2; 62 + } 63 + 64 + message UpdatePetRequest { 65 + string pet_id = 1; 66 + google.protobuf.Struct metadata = 2; 67 + } 68 + 69 + message UpdatePetResponse { 70 + Pet pet = 1; 71 + } 72 + 73 + message PurchasePetResponse {} 74 + 75 + // Pet represents a pet in the pet store. 76 + message Pet { 77 + PetType pet_type = 1; 78 + 79 + repeated PetType pet_types = 2; 80 + 81 + payment.v1alpha1.PaymentProvider payment_provider = 22; 82 + 83 + // pet_id is an auto-generated id for the pet 84 + // the id uniquely identifies a pet in the system 85 + string pet_id = 3; 86 + 87 + string name = 4; 88 + 89 + google.type.DateTime created_at = 5; 90 + 91 + repeated google.protobuf.Any details = 6; 92 + 93 + message Vet { 94 + string name = 1; 95 + } 96 + 97 + Vet vet = 7; 98 + repeated Vet vets = 8; 99 + 100 + google.protobuf.ListValue labels = 101; 101 + 102 + repeated string tags = 102; 103 + 104 + google.protobuf.Struct metadata = 103; 105 + 106 + // TODO(dm): add support for oneof 107 + // oneof test_oneof { 108 + // string p1 = 7; 109 + // int32 p2 = 8; 110 + // } 111 + 112 + // TODO(dm): add support for maps 113 + // map<string, Medication> medications = 9; 114 + // map<string, int32> vet_visits_by_month = 10; 115 + }
+90
cmd/twirp-openapi-gen/main.go
··· 1 + package main 2 + 3 + import ( 4 + "flag" 5 + "fmt" 6 + "log/slog" 7 + "os" 8 + "strings" 9 + 10 + "xeiaso.net/v4/cmd/twirp-openapi-gen/internal/generator" 11 + ) 12 + 13 + type arrayFlags []string 14 + 15 + var ( 16 + version = "DEV" 17 + ) 18 + 19 + func main() { 20 + if err := run(os.Args); err != nil { 21 + fmt.Fprintf(os.Stderr, "%s\n", err) 22 + os.Exit(1) 23 + } 24 + } 25 + 26 + func run(args []string) error { 27 + flags := flag.NewFlagSet(args[0], flag.ExitOnError) 28 + 29 + in := arrayFlags{} 30 + protoPaths := arrayFlags{} 31 + servers := arrayFlags{} 32 + flags.Var(&in, "in", "Input source .proto files. May be specified multiple times.") 33 + flags.Var(&protoPaths, "proto-path", "Specify the directory in which to search for imports. May be specified multiple times; directories will be searched in order. If not given, the current working directory is used.") 34 + flags.Var(&servers, "servers", "Server object URL. May be specified multiple times.") 35 + title := flags.String("title", "open-api-v3-docs", "Document title") 36 + docVersion := flags.String("doc-version", "0.1", "API Document version") 37 + format := flags.String("format", "json", "Document format; json or yaml") 38 + out := flags.String("out", "./openapi-doc.json", "Output document file") 39 + pathPrefix := flags.String("path-prefix", "/twirp", "Twirp server path prefix") 40 + verbose := flags.Bool("verbose", false, "Log debug output") 41 + printVersion := flags.Bool("version", false, "Print version") 42 + 43 + var h slog.Handler 44 + h = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 45 + AddSource: true, 46 + Level: slog.LevelInfo, 47 + }) 48 + if *verbose { 49 + h = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 50 + AddSource: true, 51 + Level: slog.LevelDebug, 52 + }) 53 + } 54 + slog.SetDefault(slog.New(h)) 55 + 56 + if err := flags.Parse(args[1:]); err != nil { 57 + return err 58 + } 59 + 60 + if *printVersion { 61 + fmt.Println(version) 62 + return nil 63 + } 64 + 65 + opts := []generator.Option{ 66 + generator.ProtoPaths(protoPaths), 67 + generator.Servers(servers), 68 + generator.Title(*title), 69 + generator.DocVersion(*docVersion), 70 + generator.PathPrefix(*pathPrefix), 71 + generator.Format(*format), 72 + } 73 + gen, err := generator.NewGenerator(in, opts...) 74 + if err != nil { 75 + return err 76 + } 77 + if err := gen.Generate(*out); err != nil { 78 + return err 79 + } 80 + return nil 81 + } 82 + 83 + func (i *arrayFlags) String() string { 84 + return strings.Join(*i, ",") 85 + } 86 + 87 + func (i *arrayFlags) Set(value string) error { 88 + *i = append(*i, value) 89 + return nil 90 + }
+8 -8
go.mod
··· 1 1 module xeiaso.net/v4 2 2 3 - go 1.21.1 3 + go 1.22 4 4 5 - toolchain go1.21.5 5 + toolchain go1.22.0 6 6 7 7 require ( 8 8 github.com/bep/debounce v1.2.1 9 9 github.com/donatj/hmacsig v1.1.0 10 + github.com/emicklei/proto v1.11.2 11 + github.com/esceer/todo/swagger-ui v0.0.0-20230925141326-8c4d7abce8d9 10 12 github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 13 + github.com/getkin/kin-openapi v0.120.0 11 14 github.com/go-faker/faker/v4 v4.3.0 12 15 github.com/go-git/go-git/v5 v5.11.0 16 + github.com/invopop/yaml v0.2.0 13 17 github.com/joho/godotenv v1.5.1 14 18 github.com/twitchtv/twirp v8.1.3+incompatible 15 19 golang.org/x/oauth2 v0.17.0 ··· 24 28 dario.cat/mergo v1.0.0 // indirect 25 29 github.com/Microsoft/go-winio v0.6.1 // indirect 26 30 github.com/ProtonMail/go-crypto v1.0.0 // indirect 27 - github.com/blockthrough/twirp-openapi-gen v0.0.0-20231107141308-45a52eb7d856 // indirect 28 31 github.com/cloudflare/circl v1.3.7 // indirect 29 32 github.com/cyphar/filepath-securejoin v0.2.4 // indirect 30 - github.com/emicklei/proto v1.11.2 // indirect 31 33 github.com/emirpasic/gods v1.18.1 // indirect 32 34 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect 33 35 github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect 34 - github.com/getkin/kin-openapi v0.120.0 // indirect 35 36 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 36 37 github.com/go-git/go-billy/v5 v5.5.0 // indirect 37 - github.com/go-openapi/jsonpointer v0.20.0 // indirect 38 - github.com/go-openapi/swag v0.22.4 // indirect 38 + github.com/go-openapi/jsonpointer v0.20.2 // indirect 39 + github.com/go-openapi/swag v0.22.9 // indirect 39 40 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 40 41 github.com/golang/protobuf v1.5.3 // indirect 41 - github.com/invopop/yaml v0.2.0 // indirect 42 42 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 43 43 github.com/josharian/intern v1.0.0 // indirect 44 44 github.com/kevinburke/ssh_config v1.2.0 // indirect
+10 -6
go.sum
··· 11 11 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 12 12 github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= 13 13 github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= 14 - github.com/blockthrough/twirp-openapi-gen v0.0.0-20231107141308-45a52eb7d856 h1:7m7Q6EErVZWPIp1UeVSb5zUTFzV6m7ZUcUIpbWffBfs= 15 - github.com/blockthrough/twirp-openapi-gen v0.0.0-20231107141308-45a52eb7d856/go.mod h1:UN1OFH6gXrM9qKvXPcs4dOAAMB1tC4kw/eUzdA/bmI8= 16 14 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 17 15 github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 18 16 github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= ··· 30 28 github.com/emicklei/proto v1.11.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= 31 29 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 32 30 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 31 + github.com/esceer/todo/swagger-ui v0.0.0-20230925141326-8c4d7abce8d9 h1:+6HArmVv5pnIBTBr1oM/l6RAVP5c4FSVlkdlomPq/KY= 32 + github.com/esceer/todo/swagger-ui v0.0.0-20230925141326-8c4d7abce8d9/go.mod h1:IhN3RCmQTSWyC/Y25zd4ztmDG7M21yzoz88UGkGFp/8= 33 33 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= 34 34 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= 35 35 github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:CkmB2l68uhvRlwOTPrwnuitSxi/S3Cg4L5QYOcL9MBc= ··· 54 54 github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 55 55 github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= 56 56 github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= 57 - github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= 58 - github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= 59 - github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= 60 - github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 57 + github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= 58 + github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= 59 + github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= 60 + github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= 61 + github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= 62 + github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 61 63 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 62 64 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 63 65 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= ··· 112 114 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 113 115 github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= 114 116 github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= 117 + github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= 118 + github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 115 119 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 116 120 github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 117 121 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+7 -7
gomod2nix.toml
··· 13 13 [mod."github.com/bep/debounce"] 14 14 version = "v1.2.1" 15 15 hash = "sha256-7qHOp4vB0ifEseXXBuSH6W5YNImVcb8PTWSJJAMaGcU=" 16 - [mod."github.com/blockthrough/twirp-openapi-gen"] 17 - version = "v0.0.0-20231107141308-45a52eb7d856" 18 - hash = "sha256-i0K4rM/A36eTIG86zibtaA40WoT2M+ptReRCDy+EYGs=" 19 16 [mod."github.com/cloudflare/circl"] 20 17 version = "v1.3.7" 21 18 hash = "sha256-AkOpcZ+evLxLJStvvr01+TLeWDqcLxY3e/AhGggzh40=" ··· 31 28 [mod."github.com/emirpasic/gods"] 32 29 version = "v1.18.1" 33 30 hash = "sha256-hGDKddjLj+5dn2woHtXKUdd49/3xdsqnhx7VEdCu1m4=" 31 + [mod."github.com/esceer/todo/swagger-ui"] 32 + version = "v0.0.0-20230925141326-8c4d7abce8d9" 33 + hash = "sha256-lj4BQx1kc6HODBHbSmxgvTiuDH2Xw7CFS9mReRh7SKE=" 34 34 [mod."github.com/facebookgo/ensure"] 35 35 version = "v0.0.0-20200202191622-63f1cf65ac4c" 36 36 hash = "sha256-ZvQLMu0LBtRB4lMgY4DlsKxiRUzmh8W4KvGYJF4icRc=" ··· 56 56 version = "v5.11.0" 57 57 hash = "sha256-2yUM/FlV+nYxacVynJCnDZeMub4Iu8JL2WBHmlnwOkE=" 58 58 [mod."github.com/go-openapi/jsonpointer"] 59 - version = "v0.20.0" 60 - hash = "sha256-31YT6a8u7adrtEOV62066BrrNfPT5kg5uUUo9ULW0Qk=" 59 + version = "v0.20.2" 60 + hash = "sha256-z9IZxP+JvJ1WvrHE7qbAZQqJ3XMx1uD0S611shTMna8=" 61 61 [mod."github.com/go-openapi/swag"] 62 - version = "v0.22.4" 63 - hash = "sha256-a3iJF2d9pJVTBMNpxWIOA8S1+qTbQQ+ToFcXypQ5Tec=" 62 + version = "v0.22.9" 63 + hash = "sha256-rH8EMuDAAKhyNjo+CpQmlmqTc9aPzEFkRptYCenw+xE=" 64 64 [mod."github.com/golang/groupcache"] 65 65 version = "v0.0.0-20210331224755-41bb18bfe9da" 66 66 hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0="
-7
tools.go
··· 1 - //go:build ignore 2 - 3 - package xeiaso 4 - 5 - import ( 6 - _ "github.com/blockthrough/twirp-openapi-gen/cmd/twirp-openapi-gen" 7 - )