diff --git a/frameworks-grpc-gateway/.gitignore b/frameworks-grpc-gateway/.gitignore new file mode 100644 index 00000000..3b735ec4 --- /dev/null +++ b/frameworks-grpc-gateway/.gitignore @@ -0,0 +1,21 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work diff --git a/frameworks-grpc-gateway/LICENSE b/frameworks-grpc-gateway/LICENSE new file mode 100644 index 00000000..aa7acc7f --- /dev/null +++ b/frameworks-grpc-gateway/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/frameworks-grpc-gateway/README.md b/frameworks-grpc-gateway/README.md new file mode 100644 index 00000000..d498d128 --- /dev/null +++ b/frameworks-grpc-gateway/README.md @@ -0,0 +1,134 @@ +
+ + Speakeasy + +
+
+
+ Docs Quickstart  //  Join us on Slack +
+
+ +
+ +
+

Speakeasy gRPC Gateway OpenAPI Example

+ + +
+ +This example gRPC Gateway app demonstrates Speakeasy-recommended practices for +generating OpenAPI and SDKs from protobuf. + +## Installation + +To install the application on your local machine: + +Clone the repository: +```bash +git clone https://github.com/speakeasy/examples.git +``` + +Navigate into the directory: +```bash +cd frameworks-grpc-gateway +``` + +### Install Go + +On macOS, install Go by running: + +```bash +brew install go +``` + +Alternatively follow the [Go installation +instructions](https://go.dev/doc/install) for your platform. + +### Install Buf + +On macOS, install Buf by running: + +```bash +brew install bufbuild/buf/buf +``` + +Alternatively, follow the [Buf CLI installation +instructions](https://buf.build/docs/installation) for your platform. + +### Install Buf Modules + +We'll use Buf to manage our dependencies. + +```bash +cd proto +buf dep update +cd .. +``` + +### Install Go module dependencies + +Run the following in the terminal from the project's root: + +```bash +go mod tidy +``` + +`protoc-gen-go`, `protoc-gen-go-grpc`, and `protoc-gen-grpc-gateway` are tracked +as [Go tool dependencies](https://go.dev/doc/modules/managing-dependencies#tools) +in `go.mod`, so `buf generate` runs them with `go tool` and no separate install +step is required for these three. + +### Install protoc-gen-openapiv3 + +`protoc-gen-openapiv3` hasn't shipped in a tagged grpc-gateway release yet, so +it can't be pinned as a Go tool dependency. Install it separately from the +`main` branch: + +```bash +go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3@main +``` + +This installs an unreleased, moving target, so expect occasional breaking +changes until the plugin ships in a tagged release. Make sure `$HOME/go/bin` is +in `$PATH`: + +```bash +export PATH="$PATH:$HOME/go/bin" +``` + +### Install Speakeasy CLI + +To create an SDK, you'll also need the Speakeasy CLI installed, or use the +Speakeasy dashboard. + +[Install Speakeasy CLI](https://github.com/speakeasy-api/speakeasy#installation): +```bash +brew install speakeasy-api/homebrew-tap/speakeasy +``` + +## Generate OpenAPI Document and Create SDK + +The `protoc-gen-openapiv3` plugin emits OpenAPI v3.1 JSON directly from the +source code, using HTTP bindings, leading comments, and +`google.api.field_behavior` annotations. Unlike `protoc-gen-openapiv2`, it +doesn't rely on annotations spread throughout the codebase for fine-grained +metadata. Instead, extra OpenAPI metadata such as operation IDs/tags, server +information, and vendor extensions is applied through +[Overlays](https://spec.openapis.org/overlay/v1.1.0.html). + +```bash +buf generate +speakeasy overlay apply \ + -s openapi/speakeasy/v1/speakeasy.openapi.json \ + -o openapi/train-travel.overlay.yaml \ + --out openapi/speakeasy/v1/speakeasy.patched.openapi.json +speakeasy generate sdk \ + --schema openapi/speakeasy/v1/speakeasy.patched.openapi.json \ + --lang typescript \ + --out ./sdk +``` + +## License + +This project is licensed under the terms of the Apache 2.0 license. diff --git a/frameworks-grpc-gateway/buf.gen.yaml b/frameworks-grpc-gateway/buf.gen.yaml new file mode 100644 index 00000000..d31979be --- /dev/null +++ b/frameworks-grpc-gateway/buf.gen.yaml @@ -0,0 +1,23 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: github.com/speakeasy-api/speakeasy-grpc-gateway-example/proto +plugins: + - local: ["go", "tool", "protoc-gen-go"] + out: proto + opt: + - paths=source_relative + - local: ["go", "tool", "protoc-gen-go-grpc"] + out: proto + opt: + - paths=source_relative + - require_unimplemented_servers=false + - local: ["go", "tool", "protoc-gen-grpc-gateway"] + out: proto + opt: + - paths=source_relative + - generate_unbound_methods=true + - local: protoc-gen-openapiv3 + out: openapi diff --git a/frameworks-grpc-gateway/buf.lock b/frameworks-grpc-gateway/buf.lock new file mode 100644 index 00000000..84475891 --- /dev/null +++ b/frameworks-grpc-gateway/buf.lock @@ -0,0 +1,6 @@ +# Generated by buf. DO NOT EDIT. +version: v2 +deps: + - name: buf.build/googleapis/googleapis + commit: c17df5b2beca46928cc87d5656bd5343 + digest: b5:648a01e0170d4512dea7d564016165decd1ed6e34bef79fe54753e51ad7e27545709ad9157d7551270147d551155c595a2fb0bf5bb33b1c83040ddbce915c604 diff --git a/frameworks-grpc-gateway/buf.yaml b/frameworks-grpc-gateway/buf.yaml new file mode 100644 index 00000000..ddfadf0c --- /dev/null +++ b/frameworks-grpc-gateway/buf.yaml @@ -0,0 +1,15 @@ +version: v2 +modules: + - path: proto + name: buf.build/speakeasy-api/speakeasy-grpc-gateway-example +deps: + - buf.build/googleapis/googleapis +lint: + except: + - FIELD_NOT_REQUIRED + - PACKAGE_NO_IMPORT_CYCLE + disallow_comment_ignores: true +breaking: + except: + - EXTENSION_NO_DELETE + - FIELD_SAME_DEFAULT diff --git a/frameworks-grpc-gateway/go.mod b/frameworks-grpc-gateway/go.mod new file mode 100644 index 00000000..dcf48b42 --- /dev/null +++ b/frameworks-grpc-gateway/go.mod @@ -0,0 +1,25 @@ +module github.com/speakeasy-api/speakeasy-grpc-gateway-example + +go 1.26.0 + +require ( + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 + google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324 + google.golang.org/grpc v1.81.1 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 + google.golang.org/protobuf v1.36.11 +) + +require ( + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.38.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 // indirect +) + +tool ( + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway + google.golang.org/grpc/cmd/protoc-gen-go-grpc + google.golang.org/protobuf/cmd/protoc-gen-go +) diff --git a/frameworks-grpc-gateway/go.sum b/frameworks-grpc-gateway/go.sum new file mode 100644 index 00000000..7ba53fc3 --- /dev/null +++ b/frameworks-grpc-gateway/go.sum @@ -0,0 +1,47 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324 h1:g0RAkxK/smSu/iRwC/KIX1mwUoVJtk2OjbgaeS4DmUM= +google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324/go.mod h1:Z4WJ5pJOYWFWcHEQUelD5QaZDknIQkpIL/+fyJOT9+A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 h1:9HZDLIdYBJXAnaFOr9WHrKVycfpY+75s9HGadC0305A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/frameworks-grpc-gateway/go.work.sum b/frameworks-grpc-gateway/go.work.sum new file mode 100644 index 00000000..b2d3165e --- /dev/null +++ b/frameworks-grpc-gateway/go.work.sum @@ -0,0 +1,11 @@ +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/frameworks-grpc-gateway/insecure/insecure.go b/frameworks-grpc-gateway/insecure/insecure.go new file mode 100644 index 00000000..e1ac7ee2 --- /dev/null +++ b/frameworks-grpc-gateway/insecure/insecure.go @@ -0,0 +1,79 @@ +package insecure + +import ( + "crypto/tls" + "crypto/x509" + "log" +) + +const certPEM = `-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIQIj4BuOtQRWxvUA4CUaL+WjANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMCAXDTE4MDIyMjEzNDA1NFoYDzIxMzIwMzIzMDU0 +MDU0WjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA0f+rxvg+P/YxJ9Rnj4qPypexre9OAwHfYIfDtBwPffSNhmaJa6Ir +JmPDAfrkGmAu8E+1EJMRge7R4js+y7lj/nxSTHQW4ixXWYNaHrXB8T2ty+dW2T+t +TWagtBkgdZqC+t3AloRtDJBIFKXcd6yHA9q9vj/KRtnafTPjDYD+m4obR5vhkFYm +5oJJoLkcuZ8hGr3MdzHFMIPOJ5Bm5YBY3z4TLqGnmDqhL3pqNHW0xHP7wGEJOTal +I/3OqRthAkLLMwUCHQcpLt1j2jTbavodUSr4ibNXTn5L1ynRGtozb2iE+4bZlRQZ +oR0Q32XxPQ+vkKtatgXS7E6yiq/vUc88hQIDAQABo1owWDAOBgNVHQ8BAf8EBAMC +AqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAgBgNVHREE +GTAXgglsb2NhbGhvc3SHBAAAAACHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAJgo +hrLJDKN9VXh6EXYtaeMxRVEINt+swrXLoxNcNmRXZb5vX11yX9uHWCcIaOHZM4c6 ++ZZe6gtdTGswrzl7vB5RJ5ZJEypj0MhAvH/PN0J9W0gXYbxzI839RQ2DqNXDjU7I +bEDlKBSSmFb0TjXTuXhHKyviLETAbf143Zb7M1i9L+U5fiPaq2Zt07NX6d2SYeMd +7udXyv/WhWfXKYj2Hoa8sKfcNr2e68IkbD6i1j9zXSbOMfvs1JZgryGqNIoGDOPz ++M3QhvvuiYJCSoOhDph0pNoVeH4NtaVwqPe7qMPnim11CGQSfjzxmZMFqsoJIsRe +lig/ubNJZbC6oA1X+t4= +-----END CERTIFICATE----- +` + +const keyPEM = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0f+rxvg+P/YxJ9Rnj4qPypexre9OAwHfYIfDtBwPffSNhmaJ +a6IrJmPDAfrkGmAu8E+1EJMRge7R4js+y7lj/nxSTHQW4ixXWYNaHrXB8T2ty+dW +2T+tTWagtBkgdZqC+t3AloRtDJBIFKXcd6yHA9q9vj/KRtnafTPjDYD+m4obR5vh +kFYm5oJJoLkcuZ8hGr3MdzHFMIPOJ5Bm5YBY3z4TLqGnmDqhL3pqNHW0xHP7wGEJ +OTalI/3OqRthAkLLMwUCHQcpLt1j2jTbavodUSr4ibNXTn5L1ynRGtozb2iE+4bZ +lRQZoR0Q32XxPQ+vkKtatgXS7E6yiq/vUc88hQIDAQABAoIBAENHu7xaqm8JrIQL +TEaz6Q/KfBWy9vaFshCWTsA0wU3kfTdTQLHoWhTJn5/RxyUgLcm2b9dolxJe9oWZ +36ubsQrGwANYqkA6Xp4CNGxkZUeqMDWE39Fo0lhbCh/JcRncsBw50csnkFyXGVDs +Nu9sqjE08teyTldW0uaMKjGYY3pnNrx2o4rjC40zukuaKJx2cUE7lm05lrYNxUWT +mNA+ByG8kaK7dL03fT8g9qihbrNTbTG8LC6Bfr8Tl/+PAi+uudQsTW3m2+0mXBOR +iP7R/j0PEVuDjVRSWcV1oXH/XBVWozUZgBNxTdW1VDyPU7HLM907zzLMZRVId+5w +/Hxz/70CgYEA0hDZA0YTlawDySqQrrdyeSd/OWTO4fbuq5uDVP2VwgM/KQLsr9N0 +Sa4g8ByeeXdOPKTxSA4a/ACXunKQaGSSjTUQ9L01J7nQQ723Ji7YU+0pM3LG+0Ga +PGWxwmTVM/XcOChOdv0u8H5mSJw14UAwzbguRH/Sw7yTCZdXjE7MCdsCgYEA/+sR +PWUpPRyClIOD20ee422Cib8/Utx6FrEnUKYnSi90hC3dIq1Ti+NAHqTtkFIVcG3H +PzcobEFsjVdERYc7QGhEkxJY84V7nulGufLuF0k9w0DtM+clzLSR7jYKSdOdiIJa +U3zlnuAt1wdRLGToeC69h5FK30ZkQ3axakwfkR8CgYEAmEsgme//GN6pq/lRBWn3 +8wAAi4KbPlVAuWc4crCaFxs1ei0lnV9HCnfUZ1/IQLWPIgZO6vdW9uYTGlgee6CW +Ywta7KQT2mYrKEFte6Aws7/Xw/XtbpEkGa98jTt/Gnmfm5MVN8zcb/yjePbPVSut +dieWW5D0I3Yef7CaBx3FbUsCgYEAhk99RcaAxSTgV0dKfVvyRJPlrZtkhX1WyfAB +nS8GccXEFdboNtnWfhUvQqX2VAbwX4gNyNyO53nSmb9SAld9vki6rKE1c+D7RyRQ +zSh00l3K/11k4BeQ3AVsjSNpdOONyuX2t9hVvnMTO8YIUQ9IfkKxj6OuMs4DsvBp +HkuDSasCgYAJMKJLDzVHEVTokQIYge8ZK4/TtbD+OHt176Q3cWNbRGQPsLnmWqA2 +9wtvOiilMPPhMUcIJDkuiRk8Ee2Tn/BfY5+sVa/ciwH+LvWOJ9GU2HCFTysgz0dE +nGJMtCztdc2DqxoFcBThVTmZ8F9XIRmBLxmcHlUYjwwTSp0Wo4ppwA== +-----END RSA PRIVATE KEY----- +` + +var ( + // Cert is a self signed certificate + Cert tls.Certificate + // CertPool contains the self signed certificate + CertPool *x509.CertPool +) + +func init() { + var err error + Cert, err = tls.X509KeyPair([]byte(certPEM), []byte(keyPEM)) + if err != nil { + log.Fatalln("Failed to parse key pair:", err) + } + Cert.Leaf, err = x509.ParseCertificate(Cert.Certificate[0]) + if err != nil { + log.Fatalln("Failed to parse certificate:", err) + } + + CertPool = x509.NewCertPool() + CertPool.AddCert(Cert.Leaf) +} \ No newline at end of file diff --git a/frameworks-grpc-gateway/main.go b/frameworks-grpc-gateway/main.go new file mode 100644 index 00000000..798e71ae --- /dev/null +++ b/frameworks-grpc-gateway/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "io" + "net" + "os" + + "github.com/speakeasy-api/speakeasy-grpc-gateway-example/insecure" + "github.com/speakeasy-api/speakeasy-grpc-gateway-example/server" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/grpclog" + + speakeasyv1 "github.com/speakeasy-api/speakeasy-grpc-gateway-example/proto/speakeasy/v1" +) + +func main() { + // Adds gRPC internal logs. This is quite verbose, so adjust as desired! + log := grpclog.NewLoggerV2(os.Stdout, io.Discard, io.Discard) + grpclog.SetLoggerV2(log) + + addr := "127.0.0.1:8080" + lis, err := net.Listen("tcp", addr) + if err != nil { + log.Fatalln("Failed to listen:", err) + } + + s := grpc.NewServer( + // TODO: Replace with your own certificate! + grpc.Creds(credentials.NewServerTLSFromCert(&insecure.Cert)), + ) + speakeasyv1.RegisterTrainTravelServiceServer(s, server.New()) + + // Serve gRPC Server + log.Info("Serving gRPC on https://", addr) + log.Fatal(s.Serve(lis)) +} \ No newline at end of file diff --git a/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.openapi.json b/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.openapi.json new file mode 100644 index 00000000..fd82fe5a --- /dev/null +++ b/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.openapi.json @@ -0,0 +1,467 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "speakeasy", + "version": "1.0.0" + }, + "paths": { + "/v1/stations": { + "get": { + "tags": [ + "TrainTravelService" + ], + "summary": "Get a list of train stations", + "description": "Returns a paginated and searchable list of all train stations.", + "operationId": "TrainTravelService_ListStations", + "parameters": [ + { + "name": "empty", + "in": "query", + "schema": { + "type": "object" + } + } + ], + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.ListStationsResponse" + } + } + } + } + } + } + }, + "/v1/trips": { + "get": { + "tags": [ + "TrainTravelService" + ], + "summary": "Get available train trips", + "description": "Returns a list of available train trips between the specified origin and\ndestination stations on the given date.", + "operationId": "TrainTravelService_GetTrips", + "parameters": [ + { + "name": "origin", + "in": "query", + "description": "The ID of the origin station.", + "schema": { + "type": "string" + } + }, + { + "name": "destination", + "in": "query", + "description": "The ID of the destination station.", + "schema": { + "type": "string" + } + }, + { + "name": "date", + "in": "query", + "description": "The date and time of the trip in ISO 8601 format.", + "schema": { + "type": "string" + } + }, + { + "name": "bicycles", + "in": "query", + "description": "Only return trips where bicycles are allowed.", + "schema": { + "type": "boolean" + } + }, + { + "name": "dogs", + "in": "query", + "description": "Only return trips where dogs are allowed.", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.GetTripsResponse" + } + } + } + } + } + } + }, + "/v1/bookings": { + "get": { + "tags": [ + "TrainTravelService" + ], + "summary": "List existing bookings", + "description": "Returns a list of all trip bookings by the authenticated user.", + "operationId": "TrainTravelService_ListBookings", + "parameters": [ + { + "name": "empty", + "in": "query", + "schema": { + "type": "object" + } + } + ], + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.ListBookingsResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "TrainTravelService" + ], + "summary": "Create a booking", + "description": "A booking is a temporary hold on a trip. It is not confirmed until the\npayment is processed.", + "operationId": "TrainTravelService_CreateBooking", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.Booking" + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A booking for a train trip.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.Booking" + } + } + } + } + } + } + }, + "/v1/bookings/{booking_id}": { + "get": { + "tags": [ + "TrainTravelService" + ], + "summary": "Get a booking", + "description": "Returns the details of a specific booking.", + "operationId": "TrainTravelService_GetBooking", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "The ID of the booking to retrieve.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A booking for a train trip.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.Booking" + } + } + } + } + } + }, + "delete": { + "tags": [ + "TrainTravelService" + ], + "summary": "Delete a booking", + "description": "Deletes a booking, cancelling the hold on the trip.", + "operationId": "TrainTravelService_DeleteBooking", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "The ID of the booking to delete.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/google.rpc.Status" + } + } + } + }, + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/speakeasy.DeleteBookingResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "google.rpc.Status": { + "type": "object", + "description": "The Status type defines a logical error model suitable for different programming environments.", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "description": "The status code, which should be an enum value of google.rpc.Code." + }, + "details": { + "type": "array", + "description": "A list of messages that carry the error details.", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": true + } + }, + "message": { + "type": "string", + "description": "A developer-facing error message." + } + } + }, + "speakeasy.Booking": { + "type": "object", + "description": "A booking for a train trip.", + "properties": { + "has_bicycle": { + "type": "boolean", + "description": "Indicates whether the passenger has a bicycle." + }, + "has_dog": { + "type": "boolean", + "description": "Indicates whether the passenger has a dog." + }, + "id": { + "type": "string", + "description": "Unique identifier for the booking." + }, + "passenger_name": { + "type": "string", + "description": "Name of the passenger." + }, + "trip_id": { + "type": "string", + "description": "Identifier of the booked trip." + } + }, + "required": [ + "trip_id", + "passenger_name" + ] + }, + "speakeasy.DeleteBookingResponse": { + "type": "object" + }, + "speakeasy.GetTripsResponse": { + "type": "object", + "properties": { + "trips": { + "type": "array", + "items": { + "$ref": "#/components/schemas/speakeasy.Trip" + } + } + } + }, + "speakeasy.ListBookingsResponse": { + "type": "object", + "properties": { + "bookings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/speakeasy.Booking" + } + } + } + }, + "speakeasy.ListStationsResponse": { + "type": "object", + "properties": { + "stations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/speakeasy.Station" + } + } + } + }, + "speakeasy.Station": { + "type": "object", + "description": "A train station.", + "properties": { + "address": { + "type": "string", + "description": "The address of the station." + }, + "country_code": { + "type": "string", + "description": "The country code of the station." + }, + "id": { + "type": "string", + "description": "Unique identifier for the station." + }, + "name": { + "type": "string", + "description": "The name of the station." + }, + "timezone": { + "type": "string", + "description": "The timezone of the station in IANA format." + } + }, + "required": [ + "id", + "name", + "address", + "country_code" + ] + }, + "speakeasy.Trip": { + "type": "object", + "description": "A train trip between two stations.", + "properties": { + "arrival_time": { + "type": "string", + "description": "The arrival time in ISO 8601 format." + }, + "bicycles_allowed": { + "type": "boolean", + "description": "Indicates whether bicycles are allowed on the trip." + }, + "departure_time": { + "type": "string", + "description": "The departure time in ISO 8601 format." + }, + "destination": { + "type": "string", + "description": "The ID of the destination station." + }, + "dogs_allowed": { + "type": "boolean", + "description": "Indicates whether dogs are allowed on the trip." + }, + "id": { + "type": "string", + "description": "Unique identifier for the trip." + }, + "operator": { + "type": "string", + "description": "The name of the operator of the trip." + }, + "origin": { + "type": "string", + "description": "The ID of the origin station." + }, + "price": { + "type": "number", + "format": "double", + "description": "The cost of the trip." + } + } + } + } + }, + "tags": [ + { + "name": "TrainTravelService" + } + ] +} \ No newline at end of file diff --git a/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.patched.openapi.yaml b/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.patched.openapi.yaml new file mode 100644 index 00000000..d258c496 --- /dev/null +++ b/frameworks-grpc-gateway/openapi/speakeasy/v1/speakeasy.patched.openapi.yaml @@ -0,0 +1,346 @@ +openapi: "3.1.0" +info: + title: "Train Travel API" + version: "1.2.1" + description: API for finding and booking train trips across Europe. + contact: + name: Train Support + url: https://example.com/support + email: support@example.com + license: + name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + url: https://creativecommons.org/licenses/by-nc-sa/4.0/ +paths: + /v1/stations: + get: + tags: + - "TrainTravelService" + - Stations + summary: "Get a list of train stations" + description: "Returns a paginated and searchable list of all train stations." + operationId: "get-stations" + parameters: + - name: "empty" + in: "query" + schema: + type: "object" + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A successful response." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.ListStationsResponse" + /v1/trips: + get: + tags: + - "TrainTravelService" + - Trips + summary: "Get available train trips" + description: "Returns a list of available train trips between the specified origin and\ndestination stations on the given date." + operationId: "get-trips" + parameters: + - name: "origin" + in: "query" + description: "The ID of the origin station." + schema: + type: "string" + - name: "destination" + in: "query" + description: "The ID of the destination station." + schema: + type: "string" + - name: "date" + in: "query" + description: "The date and time of the trip in ISO 8601 format." + schema: + type: "string" + - name: "bicycles" + in: "query" + description: "Only return trips where bicycles are allowed." + schema: + type: "boolean" + - name: "dogs" + in: "query" + description: "Only return trips where dogs are allowed." + schema: + type: "boolean" + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A successful response." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.GetTripsResponse" + /v1/bookings: + get: + tags: + - "TrainTravelService" + - Bookings + summary: "List existing bookings" + description: "Returns a list of all trip bookings by the authenticated user." + operationId: "get-bookings" + parameters: + - name: "empty" + in: "query" + schema: + type: "object" + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A successful response." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.ListBookingsResponse" + post: + tags: + - "TrainTravelService" + - Bookings + summary: "Create a booking" + description: "A booking is a temporary hold on a trip. It is not confirmed until the\npayment is processed." + operationId: "create-booking" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.Booking" + required: true + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A booking for a train trip." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.Booking" + /v1/bookings/{booking_id}: + get: + tags: + - "TrainTravelService" + - Bookings + summary: "Get a booking" + description: "Returns the details of a specific booking." + operationId: "get-booking" + parameters: + - name: "booking_id" + in: "path" + description: "The ID of the booking to retrieve." + required: true + schema: + type: "string" + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A booking for a train trip." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.Booking" + x-speakeasy-retries: + strategy: backoff + backoff: + initialInterval: 500 + maxInterval: 60000 + maxElapsedTime: 3600000 + exponent: 1.5 + statusCodes: + - 5XX + retryConnectionErrors: true + delete: + tags: + - "TrainTravelService" + - Bookings + summary: "Delete a booking" + description: "Deletes a booking, cancelling the hold on the trip." + operationId: "delete-booking" + parameters: + - name: "booking_id" + in: "path" + description: "The ID of the booking to delete." + required: true + schema: + type: "string" + responses: + default: + description: "An unexpected error response." + content: + application/json: + schema: + $ref: "#/components/schemas/google.rpc.Status" + "200": + description: "A successful response." + content: + application/json: + schema: + $ref: "#/components/schemas/speakeasy.DeleteBookingResponse" +components: + schemas: + google.rpc.Status: + type: "object" + description: "The Status type defines a logical error model suitable for different programming environments." + properties: + code: + type: "integer" + format: "int32" + description: "The status code, which should be an enum value of google.rpc.Code." + details: + type: "array" + description: "A list of messages that carry the error details." + items: + type: "object" + properties: + '@type': + type: "string" + additionalProperties: true + message: + type: "string" + description: "A developer-facing error message." + speakeasy.Booking: + type: "object" + description: "A booking for a train trip." + properties: + has_bicycle: + type: "boolean" + description: "Indicates whether the passenger has a bicycle." + has_dog: + type: "boolean" + description: "Indicates whether the passenger has a dog." + id: + type: "string" + description: "Unique identifier for the booking." + passenger_name: + type: "string" + description: "Name of the passenger." + trip_id: + type: "string" + description: "Identifier of the booked trip." + required: + - "trip_id" + - "passenger_name" + speakeasy.DeleteBookingResponse: + type: "object" + speakeasy.GetTripsResponse: + type: "object" + properties: + trips: + type: "array" + items: + $ref: "#/components/schemas/speakeasy.Trip" + speakeasy.ListBookingsResponse: + type: "object" + properties: + bookings: + type: "array" + items: + $ref: "#/components/schemas/speakeasy.Booking" + speakeasy.ListStationsResponse: + type: "object" + properties: + stations: + type: "array" + items: + $ref: "#/components/schemas/speakeasy.Station" + speakeasy.Station: + type: "object" + description: "A train station." + properties: + address: + type: "string" + description: "The address of the station." + country_code: + type: "string" + description: "The country code of the station." + id: + type: "string" + description: "Unique identifier for the station." + name: + type: "string" + description: "The name of the station." + timezone: + type: "string" + description: "The timezone of the station in IANA format." + required: + - "id" + - "name" + - "address" + - "country_code" + speakeasy.Trip: + type: "object" + description: "A train trip between two stations." + properties: + arrival_time: + type: "string" + description: "The arrival time in ISO 8601 format." + bicycles_allowed: + type: "boolean" + description: "Indicates whether bicycles are allowed on the trip." + departure_time: + type: "string" + description: "The departure time in ISO 8601 format." + destination: + type: "string" + description: "The ID of the destination station." + dogs_allowed: + type: "boolean" + description: "Indicates whether dogs are allowed on the trip." + id: + type: "string" + description: "Unique identifier for the trip." + operator: + type: "string" + description: "The name of the operator of the trip." + origin: + type: "string" + description: "The ID of the origin station." + price: + type: "number" + format: "double" + description: "The cost of the trip." +tags: + - name: "TrainTravelService" + - name: Stations + description: Find and filter train stations across Europe, including their location and local timezone. + - name: Trips + description: Timetables and routes for train trips between stations, including pricing and availability. + - name: Bookings + description: Create and manage bookings for train trips, including passenger details and optional extras. + - name: Payments + description: "Pay for bookings using a card or bank account, and view payment\nstatus and history.\n\n> warn\n> Bookings usually expire within 1 hour so you'll need to make your payment\n> before the expiry date \n" +x-speakeasy-retries: + strategy: backoff + backoff: + initialInterval: 500 + maxInterval: 60000 + maxElapsedTime: 3600000 + exponent: 1.5 + statusCodes: + - 5XX + retryConnectionErrors: true diff --git a/frameworks-grpc-gateway/openapi/train-travel.overlay.yaml b/frameworks-grpc-gateway/openapi/train-travel.overlay.yaml new file mode 100644 index 00000000..ee3a1fec --- /dev/null +++ b/frameworks-grpc-gateway/openapi/train-travel.overlay.yaml @@ -0,0 +1,107 @@ +overlay: 1.1.0 +info: + title: Train Travel OAS3.1 Customizations + version: 1.0.0 + description: >- + Applies deterministic API metadata, operation metadata, and x-speakeasy + extension fields to the OpenAPI document generated by protoc-gen-openapiv3. +actions: + - target: $.info + update: + title: Train Travel API + version: 1.2.1 + description: API for finding and booking train trips across Europe. + contact: + name: Train Support + url: https://example.com/support + email: support@example.com + license: + name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + url: https://creativecommons.org/licenses/by-nc-sa/4.0/ + + - target: $.servers + update: + - url: https://api.example.com/ + description: Production + + - target: $.tags + update: + - name: Stations + description: Find and filter train stations across Europe, including their location and local timezone. + - name: Trips + description: Timetables and routes for train trips between stations, including pricing and availability. + - name: Bookings + description: Create and manage bookings for train trips, including passenger details and optional extras. + - name: Payments + description: | + Pay for bookings using a card or bank account, and view payment + status and history. + + > warn + > Bookings usually expire within 1 hour so you'll need to make your payment + > before the expiry date + + - target: $.paths['/v1/stations'].get + update: + operationId: get-stations + summary: Get a list of train stations + tags: + - Stations + + - target: $.paths['/v1/trips'].get + update: + operationId: get-trips + summary: Get available train trips + tags: + - Trips + + - target: $.paths['/v1/bookings'].get + update: + operationId: get-bookings + summary: List existing bookings + tags: + - Bookings + + - target: $.paths['/v1/bookings'].post + update: + operationId: create-booking + summary: Create a booking + tags: + - Bookings + + - target: $.paths['/v1/bookings/{booking_id}'].get + update: + operationId: get-booking + summary: Get a booking + tags: + - Bookings + x-speakeasy-retries: + strategy: backoff + backoff: + initialInterval: 500 + maxInterval: 60000 + maxElapsedTime: 3600000 + exponent: 1.5 + statusCodes: + - 5XX + retryConnectionErrors: true + + - target: $.paths['/v1/bookings/{booking_id}'].delete + update: + operationId: delete-booking + summary: Delete a booking + tags: + - Bookings + + - target: $ + update: + x-speakeasy-retries: + strategy: backoff + backoff: + initialInterval: 500 + maxInterval: 60000 + maxElapsedTime: 3600000 + exponent: 1.5 + statusCodes: + - 5XX + retryConnectionErrors: true diff --git a/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.go b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.go new file mode 100644 index 00000000..30ea9da3 --- /dev/null +++ b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.go @@ -0,0 +1,920 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: speakeasy/v1/speakeasy.proto + +package v1 + +import ( + _ "github.com/speakeasy-api/speakeasy-grpc-gateway-example/proto/google/api" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// A train station. +type Station struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique identifier for the station. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The name of the station. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // The address of the station. + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + // The country code of the station. + CountryCode string `protobuf:"bytes,4,opt,name=country_code,proto3" json:"country_code,omitempty"` + // The timezone of the station in IANA format. + Timezone string `protobuf:"bytes,5,opt,name=timezone,proto3" json:"timezone,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Station) Reset() { + *x = Station{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Station) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Station) ProtoMessage() {} + +func (x *Station) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Station.ProtoReflect.Descriptor instead. +func (*Station) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{0} +} + +func (x *Station) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Station) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Station) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Station) GetCountryCode() string { + if x != nil { + return x.CountryCode + } + return "" +} + +func (x *Station) GetTimezone() string { + if x != nil { + return x.Timezone + } + return "" +} + +// A train trip between two stations. +type Trip struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique identifier for the trip. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The ID of the origin station. + Origin string `protobuf:"bytes,2,opt,name=origin,proto3" json:"origin,omitempty"` + // The ID of the destination station. + Destination string `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` + // The departure time in ISO 8601 format. + DepartureTime string `protobuf:"bytes,4,opt,name=departure_time,proto3" json:"departure_time,omitempty"` + // The arrival time in ISO 8601 format. + ArrivalTime string `protobuf:"bytes,5,opt,name=arrival_time,proto3" json:"arrival_time,omitempty"` + // The cost of the trip. + Price float64 `protobuf:"fixed64,6,opt,name=price,proto3" json:"price,omitempty"` + // The name of the operator of the trip. + Operator string `protobuf:"bytes,7,opt,name=operator,proto3" json:"operator,omitempty"` + // Indicates whether bicycles are allowed on the trip. + BicyclesAllowed bool `protobuf:"varint,8,opt,name=bicycles_allowed,proto3" json:"bicycles_allowed,omitempty"` + // Indicates whether dogs are allowed on the trip. + DogsAllowed bool `protobuf:"varint,9,opt,name=dogs_allowed,proto3" json:"dogs_allowed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Trip) Reset() { + *x = Trip{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Trip) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Trip) ProtoMessage() {} + +func (x *Trip) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Trip.ProtoReflect.Descriptor instead. +func (*Trip) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{1} +} + +func (x *Trip) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Trip) GetOrigin() string { + if x != nil { + return x.Origin + } + return "" +} + +func (x *Trip) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *Trip) GetDepartureTime() string { + if x != nil { + return x.DepartureTime + } + return "" +} + +func (x *Trip) GetArrivalTime() string { + if x != nil { + return x.ArrivalTime + } + return "" +} + +func (x *Trip) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *Trip) GetOperator() string { + if x != nil { + return x.Operator + } + return "" +} + +func (x *Trip) GetBicyclesAllowed() bool { + if x != nil { + return x.BicyclesAllowed + } + return false +} + +func (x *Trip) GetDogsAllowed() bool { + if x != nil { + return x.DogsAllowed + } + return false +} + +// A booking for a train trip. +type Booking struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Unique identifier for the booking. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Identifier of the booked trip. + TripId string `protobuf:"bytes,2,opt,name=trip_id,proto3" json:"trip_id,omitempty"` + // Name of the passenger. + PassengerName string `protobuf:"bytes,3,opt,name=passenger_name,proto3" json:"passenger_name,omitempty"` + // Indicates whether the passenger has a bicycle. + HasBicycle bool `protobuf:"varint,4,opt,name=has_bicycle,proto3" json:"has_bicycle,omitempty"` + // Indicates whether the passenger has a dog. + HasDog bool `protobuf:"varint,5,opt,name=has_dog,proto3" json:"has_dog,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Booking) Reset() { + *x = Booking{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Booking) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Booking) ProtoMessage() {} + +func (x *Booking) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Booking.ProtoReflect.Descriptor instead. +func (*Booking) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{2} +} + +func (x *Booking) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Booking) GetTripId() string { + if x != nil { + return x.TripId + } + return "" +} + +func (x *Booking) GetPassengerName() string { + if x != nil { + return x.PassengerName + } + return "" +} + +func (x *Booking) GetHasBicycle() bool { + if x != nil { + return x.HasBicycle + } + return false +} + +func (x *Booking) GetHasDog() bool { + if x != nil { + return x.HasDog + } + return false +} + +type ListStationsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Empty *emptypb.Empty `protobuf:"bytes,1,opt,name=empty,proto3" json:"empty,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStationsRequest) Reset() { + *x = ListStationsRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStationsRequest) ProtoMessage() {} + +func (x *ListStationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListStationsRequest.ProtoReflect.Descriptor instead. +func (*ListStationsRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{3} +} + +func (x *ListStationsRequest) GetEmpty() *emptypb.Empty { + if x != nil { + return x.Empty + } + return nil +} + +type ListStationsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Stations []*Station `protobuf:"bytes,1,rep,name=stations,proto3" json:"stations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListStationsResponse) Reset() { + *x = ListStationsResponse{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListStationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStationsResponse) ProtoMessage() {} + +func (x *ListStationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListStationsResponse.ProtoReflect.Descriptor instead. +func (*ListStationsResponse) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{4} +} + +func (x *ListStationsResponse) GetStations() []*Station { + if x != nil { + return x.Stations + } + return nil +} + +type GetTripsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The ID of the origin station. + Origin string `protobuf:"bytes,1,opt,name=origin,proto3" json:"origin,omitempty"` + // The ID of the destination station. + Destination string `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` + // The date and time of the trip in ISO 8601 format. + Date string `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"` + // Only return trips where bicycles are allowed. + Bicycles bool `protobuf:"varint,4,opt,name=bicycles,proto3" json:"bicycles,omitempty"` + // Only return trips where dogs are allowed. + Dogs bool `protobuf:"varint,5,opt,name=dogs,proto3" json:"dogs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTripsRequest) Reset() { + *x = GetTripsRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripsRequest) ProtoMessage() {} + +func (x *GetTripsRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripsRequest.ProtoReflect.Descriptor instead. +func (*GetTripsRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{5} +} + +func (x *GetTripsRequest) GetOrigin() string { + if x != nil { + return x.Origin + } + return "" +} + +func (x *GetTripsRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *GetTripsRequest) GetDate() string { + if x != nil { + return x.Date + } + return "" +} + +func (x *GetTripsRequest) GetBicycles() bool { + if x != nil { + return x.Bicycles + } + return false +} + +func (x *GetTripsRequest) GetDogs() bool { + if x != nil { + return x.Dogs + } + return false +} + +type GetTripsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Trips []*Trip `protobuf:"bytes,1,rep,name=trips,proto3" json:"trips,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTripsResponse) Reset() { + *x = GetTripsResponse{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripsResponse) ProtoMessage() {} + +func (x *GetTripsResponse) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripsResponse.ProtoReflect.Descriptor instead. +func (*GetTripsResponse) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{6} +} + +func (x *GetTripsResponse) GetTrips() []*Trip { + if x != nil { + return x.Trips + } + return nil +} + +type ListBookingsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Empty *emptypb.Empty `protobuf:"bytes,1,opt,name=empty,proto3" json:"empty,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListBookingsRequest) Reset() { + *x = ListBookingsRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListBookingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBookingsRequest) ProtoMessage() {} + +func (x *ListBookingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBookingsRequest.ProtoReflect.Descriptor instead. +func (*ListBookingsRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{7} +} + +func (x *ListBookingsRequest) GetEmpty() *emptypb.Empty { + if x != nil { + return x.Empty + } + return nil +} + +type ListBookingsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Bookings []*Booking `protobuf:"bytes,1,rep,name=bookings,proto3" json:"bookings,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListBookingsResponse) Reset() { + *x = ListBookingsResponse{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListBookingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListBookingsResponse) ProtoMessage() {} + +func (x *ListBookingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListBookingsResponse.ProtoReflect.Descriptor instead. +func (*ListBookingsResponse) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{8} +} + +func (x *ListBookingsResponse) GetBookings() []*Booking { + if x != nil { + return x.Bookings + } + return nil +} + +type CreateBookingRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Booking *Booking `protobuf:"bytes,1,opt,name=booking,proto3" json:"booking,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateBookingRequest) Reset() { + *x = CreateBookingRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateBookingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateBookingRequest) ProtoMessage() {} + +func (x *CreateBookingRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateBookingRequest.ProtoReflect.Descriptor instead. +func (*CreateBookingRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{9} +} + +func (x *CreateBookingRequest) GetBooking() *Booking { + if x != nil { + return x.Booking + } + return nil +} + +type GetBookingRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The ID of the booking to retrieve. + BookingId string `protobuf:"bytes,1,opt,name=booking_id,proto3" json:"booking_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetBookingRequest) Reset() { + *x = GetBookingRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetBookingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBookingRequest) ProtoMessage() {} + +func (x *GetBookingRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBookingRequest.ProtoReflect.Descriptor instead. +func (*GetBookingRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{10} +} + +func (x *GetBookingRequest) GetBookingId() string { + if x != nil { + return x.BookingId + } + return "" +} + +type DeleteBookingRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The ID of the booking to delete. + BookingId string `protobuf:"bytes,1,opt,name=booking_id,proto3" json:"booking_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteBookingRequest) Reset() { + *x = DeleteBookingRequest{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteBookingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteBookingRequest) ProtoMessage() {} + +func (x *DeleteBookingRequest) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteBookingRequest.ProtoReflect.Descriptor instead. +func (*DeleteBookingRequest) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{11} +} + +func (x *DeleteBookingRequest) GetBookingId() string { + if x != nil { + return x.BookingId + } + return "" +} + +type DeleteBookingResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteBookingResponse) Reset() { + *x = DeleteBookingResponse{} + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteBookingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteBookingResponse) ProtoMessage() {} + +func (x *DeleteBookingResponse) ProtoReflect() protoreflect.Message { + mi := &file_speakeasy_v1_speakeasy_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteBookingResponse.ProtoReflect.Descriptor instead. +func (*DeleteBookingResponse) Descriptor() ([]byte, []int) { + return file_speakeasy_v1_speakeasy_proto_rawDescGZIP(), []int{12} +} + +var File_speakeasy_v1_speakeasy_proto protoreflect.FileDescriptor + +const file_speakeasy_v1_speakeasy_proto_rawDesc = "" + + "\n" + + "\x1cspeakeasy/v1/speakeasy.proto\x12\tspeakeasy\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x1cgoogle/api/annotations.proto\"\x9b\x01\n" + + "\aStation\x12\x13\n" + + "\x02id\x18\x01 \x01(\tB\x03\xe0A\x02R\x02id\x12\x17\n" + + "\x04name\x18\x02 \x01(\tB\x03\xe0A\x02R\x04name\x12\x1d\n" + + "\aaddress\x18\x03 \x01(\tB\x03\xe0A\x02R\aaddress\x12'\n" + + "\fcountry_code\x18\x04 \x01(\tB\x03\xe0A\x02R\fcountry_code\x12\x1a\n" + + "\btimezone\x18\x05 \x01(\tR\btimezone\"\x9e\x02\n" + + "\x04Trip\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + + "\x06origin\x18\x02 \x01(\tR\x06origin\x12 \n" + + "\vdestination\x18\x03 \x01(\tR\vdestination\x12&\n" + + "\x0edeparture_time\x18\x04 \x01(\tR\x0edeparture_time\x12\"\n" + + "\farrival_time\x18\x05 \x01(\tR\farrival_time\x12\x14\n" + + "\x05price\x18\x06 \x01(\x01R\x05price\x12\x1a\n" + + "\boperator\x18\a \x01(\tR\boperator\x12*\n" + + "\x10bicycles_allowed\x18\b \x01(\bR\x10bicycles_allowed\x12\"\n" + + "\fdogs_allowed\x18\t \x01(\bR\fdogs_allowed\"\xa1\x01\n" + + "\aBooking\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" + + "\atrip_id\x18\x02 \x01(\tB\x03\xe0A\x02R\atrip_id\x12+\n" + + "\x0epassenger_name\x18\x03 \x01(\tB\x03\xe0A\x02R\x0epassenger_name\x12 \n" + + "\vhas_bicycle\x18\x04 \x01(\bR\vhas_bicycle\x12\x18\n" + + "\ahas_dog\x18\x05 \x01(\bR\ahas_dog\"C\n" + + "\x13ListStationsRequest\x12,\n" + + "\x05empty\x18\x01 \x01(\v2\x16.google.protobuf.EmptyR\x05empty\"F\n" + + "\x14ListStationsResponse\x12.\n" + + "\bstations\x18\x01 \x03(\v2\x12.speakeasy.StationR\bstations\"\x9e\x01\n" + + "\x0fGetTripsRequest\x12\x1b\n" + + "\x06origin\x18\x01 \x01(\tB\x03\xe0A\x02R\x06origin\x12%\n" + + "\vdestination\x18\x02 \x01(\tB\x03\xe0A\x02R\vdestination\x12\x17\n" + + "\x04date\x18\x03 \x01(\tB\x03\xe0A\x02R\x04date\x12\x1a\n" + + "\bbicycles\x18\x04 \x01(\bR\bbicycles\x12\x12\n" + + "\x04dogs\x18\x05 \x01(\bR\x04dogs\"9\n" + + "\x10GetTripsResponse\x12%\n" + + "\x05trips\x18\x01 \x03(\v2\x0f.speakeasy.TripR\x05trips\"C\n" + + "\x13ListBookingsRequest\x12,\n" + + "\x05empty\x18\x01 \x01(\v2\x16.google.protobuf.EmptyR\x05empty\"F\n" + + "\x14ListBookingsResponse\x12.\n" + + "\bbookings\x18\x01 \x03(\v2\x12.speakeasy.BookingR\bbookings\"D\n" + + "\x14CreateBookingRequest\x12,\n" + + "\abooking\x18\x01 \x01(\v2\x12.speakeasy.BookingR\abooking\"8\n" + + "\x11GetBookingRequest\x12#\n" + + "\n" + + "booking_id\x18\x01 \x01(\tB\x03\xe0A\x02R\n" + + "booking_id\";\n" + + "\x14DeleteBookingRequest\x12#\n" + + "\n" + + "booking_id\x18\x01 \x01(\tB\x03\xe0A\x02R\n" + + "booking_id\"\x17\n" + + "\x15DeleteBookingResponse2\x94\x05\n" + + "\x12TrainTravelService\x12o\n" + + "\fListStations\x12\x1e.speakeasy.ListStationsRequest\x1a\x1f.speakeasy.ListStationsResponse\"\x1e\x82\xd3\xe4\x93\x02\x18b\bstations\x12\f/v1/stations\x12]\n" + + "\bGetTrips\x12\x1a.speakeasy.GetTripsRequest\x1a\x1b.speakeasy.GetTripsResponse\"\x18\x82\xd3\xe4\x93\x02\x12b\x05trips\x12\t/v1/trips\x12o\n" + + "\fListBookings\x12\x1e.speakeasy.ListBookingsRequest\x1a\x1f.speakeasy.ListBookingsResponse\"\x1e\x82\xd3\xe4\x93\x02\x18b\bbookings\x12\f/v1/bookings\x12c\n" + + "\rCreateBooking\x12\x1f.speakeasy.CreateBookingRequest\x1a\x12.speakeasy.Booking\"\x1d\x82\xd3\xe4\x93\x02\x17:\abooking\"\f/v1/bookings\x12a\n" + + "\n" + + "GetBooking\x12\x1c.speakeasy.GetBookingRequest\x1a\x12.speakeasy.Booking\"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/v1/bookings/{booking_id}\x12u\n" + + "\rDeleteBooking\x12\x1f.speakeasy.DeleteBookingRequest\x1a .speakeasy.DeleteBookingResponse\"!\x82\xd3\xe4\x93\x02\x1b*\x19/v1/bookings/{booking_id}B\xaf\x01\n" + + "\rcom.speakeasyB\x0eSpeakeasyProtoP\x01ZJgithub.com/speakeasy-api/speakeasy-grpc-gateway-example/proto/speakeasy/v1\xa2\x02\x03SXX\xaa\x02\tSpeakeasy\xca\x02\tSpeakeasy\xe2\x02\x15Speakeasy\\GPBMetadata\xea\x02\tSpeakeasyb\x06proto3" + +var ( + file_speakeasy_v1_speakeasy_proto_rawDescOnce sync.Once + file_speakeasy_v1_speakeasy_proto_rawDescData []byte +) + +func file_speakeasy_v1_speakeasy_proto_rawDescGZIP() []byte { + file_speakeasy_v1_speakeasy_proto_rawDescOnce.Do(func() { + file_speakeasy_v1_speakeasy_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_speakeasy_v1_speakeasy_proto_rawDesc), len(file_speakeasy_v1_speakeasy_proto_rawDesc))) + }) + return file_speakeasy_v1_speakeasy_proto_rawDescData +} + +var file_speakeasy_v1_speakeasy_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_speakeasy_v1_speakeasy_proto_goTypes = []any{ + (*Station)(nil), // 0: speakeasy.Station + (*Trip)(nil), // 1: speakeasy.Trip + (*Booking)(nil), // 2: speakeasy.Booking + (*ListStationsRequest)(nil), // 3: speakeasy.ListStationsRequest + (*ListStationsResponse)(nil), // 4: speakeasy.ListStationsResponse + (*GetTripsRequest)(nil), // 5: speakeasy.GetTripsRequest + (*GetTripsResponse)(nil), // 6: speakeasy.GetTripsResponse + (*ListBookingsRequest)(nil), // 7: speakeasy.ListBookingsRequest + (*ListBookingsResponse)(nil), // 8: speakeasy.ListBookingsResponse + (*CreateBookingRequest)(nil), // 9: speakeasy.CreateBookingRequest + (*GetBookingRequest)(nil), // 10: speakeasy.GetBookingRequest + (*DeleteBookingRequest)(nil), // 11: speakeasy.DeleteBookingRequest + (*DeleteBookingResponse)(nil), // 12: speakeasy.DeleteBookingResponse + (*emptypb.Empty)(nil), // 13: google.protobuf.Empty +} +var file_speakeasy_v1_speakeasy_proto_depIdxs = []int32{ + 13, // 0: speakeasy.ListStationsRequest.empty:type_name -> google.protobuf.Empty + 0, // 1: speakeasy.ListStationsResponse.stations:type_name -> speakeasy.Station + 1, // 2: speakeasy.GetTripsResponse.trips:type_name -> speakeasy.Trip + 13, // 3: speakeasy.ListBookingsRequest.empty:type_name -> google.protobuf.Empty + 2, // 4: speakeasy.ListBookingsResponse.bookings:type_name -> speakeasy.Booking + 2, // 5: speakeasy.CreateBookingRequest.booking:type_name -> speakeasy.Booking + 3, // 6: speakeasy.TrainTravelService.ListStations:input_type -> speakeasy.ListStationsRequest + 5, // 7: speakeasy.TrainTravelService.GetTrips:input_type -> speakeasy.GetTripsRequest + 7, // 8: speakeasy.TrainTravelService.ListBookings:input_type -> speakeasy.ListBookingsRequest + 9, // 9: speakeasy.TrainTravelService.CreateBooking:input_type -> speakeasy.CreateBookingRequest + 10, // 10: speakeasy.TrainTravelService.GetBooking:input_type -> speakeasy.GetBookingRequest + 11, // 11: speakeasy.TrainTravelService.DeleteBooking:input_type -> speakeasy.DeleteBookingRequest + 4, // 12: speakeasy.TrainTravelService.ListStations:output_type -> speakeasy.ListStationsResponse + 6, // 13: speakeasy.TrainTravelService.GetTrips:output_type -> speakeasy.GetTripsResponse + 8, // 14: speakeasy.TrainTravelService.ListBookings:output_type -> speakeasy.ListBookingsResponse + 2, // 15: speakeasy.TrainTravelService.CreateBooking:output_type -> speakeasy.Booking + 2, // 16: speakeasy.TrainTravelService.GetBooking:output_type -> speakeasy.Booking + 12, // 17: speakeasy.TrainTravelService.DeleteBooking:output_type -> speakeasy.DeleteBookingResponse + 12, // [12:18] is the sub-list for method output_type + 6, // [6:12] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_speakeasy_v1_speakeasy_proto_init() } +func file_speakeasy_v1_speakeasy_proto_init() { + if File_speakeasy_v1_speakeasy_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_speakeasy_v1_speakeasy_proto_rawDesc), len(file_speakeasy_v1_speakeasy_proto_rawDesc)), + NumEnums: 0, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_speakeasy_v1_speakeasy_proto_goTypes, + DependencyIndexes: file_speakeasy_v1_speakeasy_proto_depIdxs, + MessageInfos: file_speakeasy_v1_speakeasy_proto_msgTypes, + }.Build() + File_speakeasy_v1_speakeasy_proto = out.File + file_speakeasy_v1_speakeasy_proto_goTypes = nil + file_speakeasy_v1_speakeasy_proto_depIdxs = nil +} diff --git a/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.gw.go b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.gw.go new file mode 100644 index 00000000..246bcabd --- /dev/null +++ b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.pb.gw.go @@ -0,0 +1,562 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: speakeasy/v1/speakeasy.proto + +/* +Package v1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package v1 + +import ( + "context" + "errors" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var ( + _ codes.Code + _ io.Reader + _ status.Status + _ = errors.New + _ = runtime.String + _ = utilities.NewDoubleArray + _ = metadata.Join +) + +var filter_TrainTravelService_ListStations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_TrainTravelService_ListStations_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListStationsRequest + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_ListStations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.ListStations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_ListStations_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListStationsRequest + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_ListStations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.ListStations(ctx, &protoReq) + return msg, metadata, err +} + +var filter_TrainTravelService_GetTrips_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_TrainTravelService_GetTrips_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetTripsRequest + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_GetTrips_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.GetTrips(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_GetTrips_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetTripsRequest + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_GetTrips_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.GetTrips(ctx, &protoReq) + return msg, metadata, err +} + +var filter_TrainTravelService_ListBookings_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_TrainTravelService_ListBookings_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListBookingsRequest + metadata runtime.ServerMetadata + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_ListBookings_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.ListBookings(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_ListBookings_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq ListBookingsRequest + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TrainTravelService_ListBookings_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.ListBookings(ctx, &protoReq) + return msg, metadata, err +} + +func request_TrainTravelService_CreateBooking_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateBookingRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Booking); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + msg, err := client.CreateBooking(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_CreateBooking_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq CreateBookingRequest + metadata runtime.ServerMetadata + ) + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Booking); err != nil && !errors.Is(err, io.EOF) { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.CreateBooking(ctx, &protoReq) + return msg, metadata, err +} + +func request_TrainTravelService_GetBooking_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetBookingRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["booking_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "booking_id") + } + protoReq.BookingId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "booking_id", err) + } + msg, err := client.GetBooking(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_GetBooking_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq GetBookingRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["booking_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "booking_id") + } + protoReq.BookingId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "booking_id", err) + } + msg, err := server.GetBooking(ctx, &protoReq) + return msg, metadata, err +} + +func request_TrainTravelService_DeleteBooking_0(ctx context.Context, marshaler runtime.Marshaler, client TrainTravelServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteBookingRequest + metadata runtime.ServerMetadata + err error + ) + if req.Body != nil { + _, _ = io.Copy(io.Discard, req.Body) + } + val, ok := pathParams["booking_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "booking_id") + } + protoReq.BookingId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "booking_id", err) + } + msg, err := client.DeleteBooking(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_TrainTravelService_DeleteBooking_0(ctx context.Context, marshaler runtime.Marshaler, server TrainTravelServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeleteBookingRequest + metadata runtime.ServerMetadata + err error + ) + val, ok := pathParams["booking_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "booking_id") + } + protoReq.BookingId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "booking_id", err) + } + msg, err := server.DeleteBooking(ctx, &protoReq) + return msg, metadata, err +} + +// RegisterTrainTravelServiceHandlerServer registers the http handlers for service TrainTravelService to "mux". +// UnaryRPC :call TrainTravelServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterTrainTravelServiceHandlerFromEndpoint instead. +// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. +func RegisterTrainTravelServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server TrainTravelServiceServer) error { + mux.Handle(http.MethodGet, pattern_TrainTravelService_ListStations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/ListStations", runtime.WithHTTPPathPattern("/v1/stations")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_ListStations_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_ListStations_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_ListStations_0{resp.(*ListStationsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_GetTrips_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/GetTrips", runtime.WithHTTPPathPattern("/v1/trips")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_GetTrips_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_GetTrips_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_GetTrips_0{resp.(*GetTripsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_ListBookings_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/ListBookings", runtime.WithHTTPPathPattern("/v1/bookings")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_ListBookings_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_ListBookings_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_ListBookings_0{resp.(*ListBookingsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_TrainTravelService_CreateBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/CreateBooking", runtime.WithHTTPPathPattern("/v1/bookings")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_CreateBooking_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_CreateBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_GetBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/GetBooking", runtime.WithHTTPPathPattern("/v1/bookings/{booking_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_GetBooking_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_GetBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_TrainTravelService_DeleteBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/speakeasy.TrainTravelService/DeleteBooking", runtime.WithHTTPPathPattern("/v1/bookings/{booking_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TrainTravelService_DeleteBooking_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_DeleteBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + return nil +} + +// RegisterTrainTravelServiceHandlerFromEndpoint is same as RegisterTrainTravelServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterTrainTravelServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.NewClient(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + return RegisterTrainTravelServiceHandler(ctx, mux, conn) +} + +// RegisterTrainTravelServiceHandler registers the http handlers for service TrainTravelService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterTrainTravelServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterTrainTravelServiceHandlerClient(ctx, mux, NewTrainTravelServiceClient(conn)) +} + +// RegisterTrainTravelServiceHandlerClient registers the http handlers for service TrainTravelService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "TrainTravelServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "TrainTravelServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "TrainTravelServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. +func RegisterTrainTravelServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client TrainTravelServiceClient) error { + mux.Handle(http.MethodGet, pattern_TrainTravelService_ListStations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/ListStations", runtime.WithHTTPPathPattern("/v1/stations")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_ListStations_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_ListStations_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_ListStations_0{resp.(*ListStationsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_GetTrips_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/GetTrips", runtime.WithHTTPPathPattern("/v1/trips")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_GetTrips_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_GetTrips_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_GetTrips_0{resp.(*GetTripsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_ListBookings_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/ListBookings", runtime.WithHTTPPathPattern("/v1/bookings")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_ListBookings_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_ListBookings_0(annotatedContext, mux, outboundMarshaler, w, req, response_TrainTravelService_ListBookings_0{resp.(*ListBookingsResponse)}, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodPost, pattern_TrainTravelService_CreateBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/CreateBooking", runtime.WithHTTPPathPattern("/v1/bookings")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_CreateBooking_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_CreateBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodGet, pattern_TrainTravelService_GetBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/GetBooking", runtime.WithHTTPPathPattern("/v1/bookings/{booking_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_GetBooking_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_GetBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle(http.MethodDelete, pattern_TrainTravelService_DeleteBooking_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/speakeasy.TrainTravelService/DeleteBooking", runtime.WithHTTPPathPattern("/v1/bookings/{booking_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TrainTravelService_DeleteBooking_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_TrainTravelService_DeleteBooking_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + return nil +} + +type response_TrainTravelService_ListStations_0 struct { + *ListStationsResponse +} + +func (m response_TrainTravelService_ListStations_0) XXX_ResponseBody() interface{} { + response := m.ListStationsResponse + return response.Stations +} + +type response_TrainTravelService_GetTrips_0 struct { + *GetTripsResponse +} + +func (m response_TrainTravelService_GetTrips_0) XXX_ResponseBody() interface{} { + response := m.GetTripsResponse + return response.Trips +} + +type response_TrainTravelService_ListBookings_0 struct { + *ListBookingsResponse +} + +func (m response_TrainTravelService_ListBookings_0) XXX_ResponseBody() interface{} { + response := m.ListBookingsResponse + return response.Bookings +} + +var ( + pattern_TrainTravelService_ListStations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "stations"}, "")) + pattern_TrainTravelService_GetTrips_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "trips"}, "")) + pattern_TrainTravelService_ListBookings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "bookings"}, "")) + pattern_TrainTravelService_CreateBooking_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "bookings"}, "")) + pattern_TrainTravelService_GetBooking_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "bookings", "booking_id"}, "")) + pattern_TrainTravelService_DeleteBooking_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "bookings", "booking_id"}, "")) +) + +var ( + forward_TrainTravelService_ListStations_0 = runtime.ForwardResponseMessage + forward_TrainTravelService_GetTrips_0 = runtime.ForwardResponseMessage + forward_TrainTravelService_ListBookings_0 = runtime.ForwardResponseMessage + forward_TrainTravelService_CreateBooking_0 = runtime.ForwardResponseMessage + forward_TrainTravelService_GetBooking_0 = runtime.ForwardResponseMessage + forward_TrainTravelService_DeleteBooking_0 = runtime.ForwardResponseMessage +) diff --git a/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.proto b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.proto new file mode 100644 index 00000000..7344b26a --- /dev/null +++ b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy.proto @@ -0,0 +1,203 @@ +syntax = "proto3"; + +package speakeasy; + +import "google/protobuf/empty.proto"; +import "google/api/field_behavior.proto"; +import "google/api/annotations.proto"; + +// A train station. +message Station { + // Unique identifier for the station. + string id = 1 [(google.api.field_behavior) = REQUIRED]; + + // The name of the station. + string name = 2 [(google.api.field_behavior) = REQUIRED]; + + // The address of the station. + string address = 3 [(google.api.field_behavior) = REQUIRED]; + + // The country code of the station. + string country_code = 4 [ + json_name = "country_code", + (google.api.field_behavior) = REQUIRED + ]; + + // The timezone of the station in IANA format. + string timezone = 5; +} + +// A train trip between two stations. +message Trip { + // Unique identifier for the trip. + string id = 1; + + // The ID of the origin station. + string origin = 2; + + // The ID of the destination station. + string destination = 3; + + // The departure time in ISO 8601 format. + string departure_time = 4 [json_name = "departure_time"]; + + // The arrival time in ISO 8601 format. + string arrival_time = 5 [json_name = "arrival_time"]; + + // The cost of the trip. + double price = 6; + + // The name of the operator of the trip. + string operator = 7; + + // Indicates whether bicycles are allowed on the trip. + bool bicycles_allowed = 8 [json_name = "bicycles_allowed"]; + + // Indicates whether dogs are allowed on the trip. + bool dogs_allowed = 9 [json_name = "dogs_allowed"]; +} + +// A booking for a train trip. +message Booking { + // Unique identifier for the booking. + string id = 1; + + // Identifier of the booked trip. + string trip_id = 2 [ + json_name = "trip_id", + (google.api.field_behavior) = REQUIRED + ]; + + // Name of the passenger. + string passenger_name = 3 [ + json_name = "passenger_name", + (google.api.field_behavior) = REQUIRED + ]; + + // Indicates whether the passenger has a bicycle. + bool has_bicycle = 4 [json_name = "has_bicycle"]; + + // Indicates whether the passenger has a dog. + bool has_dog = 5 [json_name = "has_dog"]; +} + +message ListStationsRequest { + google.protobuf.Empty empty = 1; +} + +message ListStationsResponse { + repeated Station stations = 1; +} + +message GetTripsRequest { + // The ID of the origin station. + string origin = 1 [(google.api.field_behavior) = REQUIRED]; + + // The ID of the destination station. + string destination = 2 [(google.api.field_behavior) = REQUIRED]; + + // The date and time of the trip in ISO 8601 format. + string date = 3 [(google.api.field_behavior) = REQUIRED]; + + // Only return trips where bicycles are allowed. + bool bicycles = 4; + + // Only return trips where dogs are allowed. + bool dogs = 5; +} + +message GetTripsResponse { + repeated Trip trips = 1; +} + +message ListBookingsRequest { + google.protobuf.Empty empty = 1; +} + +message ListBookingsResponse { + repeated Booking bookings = 1; +} + +message CreateBookingRequest { + Booking booking = 1; +} + +message GetBookingRequest { + // The ID of the booking to retrieve. + string booking_id = 1 [ + json_name = "booking_id", + (google.api.field_behavior) = REQUIRED + ]; +} + +message DeleteBookingRequest { + // The ID of the booking to delete. + string booking_id = 1 [ + json_name = "booking_id", + (google.api.field_behavior) = REQUIRED + ]; +} + +message DeleteBookingResponse {} + +service TrainTravelService { + // Get a list of train stations + // + // Returns a paginated and searchable list of all train stations. + rpc ListStations(ListStationsRequest) returns (ListStationsResponse) { + option (google.api.http) = { + get: "/v1/stations" + response_body: "stations" + }; + } + + // Get available train trips + // + // Returns a list of available train trips between the specified origin and + // destination stations on the given date. + rpc GetTrips(GetTripsRequest) returns (GetTripsResponse) { + option (google.api.http) = { + get: "/v1/trips" + response_body: "trips" + }; + } + + // List existing bookings + // + // Returns a list of all trip bookings by the authenticated user. + rpc ListBookings(ListBookingsRequest) returns (ListBookingsResponse) { + option (google.api.http) = { + get: "/v1/bookings" + response_body: "bookings" + }; + } + + // Create a booking + // + // A booking is a temporary hold on a trip. It is not confirmed until the + // payment is processed. + rpc CreateBooking(CreateBookingRequest) returns (Booking) { + option (google.api.http) = { + post: "/v1/bookings" + body: "booking" + }; + } + + // Get a booking + // + // Returns the details of a specific booking. + rpc GetBooking(GetBookingRequest) returns (Booking) { + option (google.api.http) = { + get: "/v1/bookings/{booking_id}" + }; + } + + // Delete a booking + // + // Deletes a booking, cancelling the hold on the trip. + rpc DeleteBooking(DeleteBookingRequest) returns (DeleteBookingResponse) { + option (google.api.http) = { + delete: "/v1/bookings/{booking_id}" + }; + } +} diff --git a/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy_grpc.pb.go b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy_grpc.pb.go new file mode 100644 index 00000000..97bd78e5 --- /dev/null +++ b/frameworks-grpc-gateway/proto/speakeasy/v1/speakeasy_grpc.pb.go @@ -0,0 +1,349 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.2 +// - protoc (unknown) +// source: speakeasy/v1/speakeasy.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + TrainTravelService_ListStations_FullMethodName = "/speakeasy.TrainTravelService/ListStations" + TrainTravelService_GetTrips_FullMethodName = "/speakeasy.TrainTravelService/GetTrips" + TrainTravelService_ListBookings_FullMethodName = "/speakeasy.TrainTravelService/ListBookings" + TrainTravelService_CreateBooking_FullMethodName = "/speakeasy.TrainTravelService/CreateBooking" + TrainTravelService_GetBooking_FullMethodName = "/speakeasy.TrainTravelService/GetBooking" + TrainTravelService_DeleteBooking_FullMethodName = "/speakeasy.TrainTravelService/DeleteBooking" +) + +// TrainTravelServiceClient is the client API for TrainTravelService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TrainTravelServiceClient interface { + // Get a list of train stations + // + // Returns a paginated and searchable list of all train stations. + ListStations(ctx context.Context, in *ListStationsRequest, opts ...grpc.CallOption) (*ListStationsResponse, error) + // Get available train trips + // + // Returns a list of available train trips between the specified origin and + // destination stations on the given date. + GetTrips(ctx context.Context, in *GetTripsRequest, opts ...grpc.CallOption) (*GetTripsResponse, error) + // List existing bookings + // + // Returns a list of all trip bookings by the authenticated user. + ListBookings(ctx context.Context, in *ListBookingsRequest, opts ...grpc.CallOption) (*ListBookingsResponse, error) + // Create a booking + // + // A booking is a temporary hold on a trip. It is not confirmed until the + // payment is processed. + CreateBooking(ctx context.Context, in *CreateBookingRequest, opts ...grpc.CallOption) (*Booking, error) + // Get a booking + // + // Returns the details of a specific booking. + GetBooking(ctx context.Context, in *GetBookingRequest, opts ...grpc.CallOption) (*Booking, error) + // Delete a booking + // + // Deletes a booking, cancelling the hold on the trip. + DeleteBooking(ctx context.Context, in *DeleteBookingRequest, opts ...grpc.CallOption) (*DeleteBookingResponse, error) +} + +type trainTravelServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTrainTravelServiceClient(cc grpc.ClientConnInterface) TrainTravelServiceClient { + return &trainTravelServiceClient{cc} +} + +func (c *trainTravelServiceClient) ListStations(ctx context.Context, in *ListStationsRequest, opts ...grpc.CallOption) (*ListStationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListStationsResponse) + err := c.cc.Invoke(ctx, TrainTravelService_ListStations_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trainTravelServiceClient) GetTrips(ctx context.Context, in *GetTripsRequest, opts ...grpc.CallOption) (*GetTripsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetTripsResponse) + err := c.cc.Invoke(ctx, TrainTravelService_GetTrips_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trainTravelServiceClient) ListBookings(ctx context.Context, in *ListBookingsRequest, opts ...grpc.CallOption) (*ListBookingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListBookingsResponse) + err := c.cc.Invoke(ctx, TrainTravelService_ListBookings_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trainTravelServiceClient) CreateBooking(ctx context.Context, in *CreateBookingRequest, opts ...grpc.CallOption) (*Booking, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Booking) + err := c.cc.Invoke(ctx, TrainTravelService_CreateBooking_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trainTravelServiceClient) GetBooking(ctx context.Context, in *GetBookingRequest, opts ...grpc.CallOption) (*Booking, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Booking) + err := c.cc.Invoke(ctx, TrainTravelService_GetBooking_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *trainTravelServiceClient) DeleteBooking(ctx context.Context, in *DeleteBookingRequest, opts ...grpc.CallOption) (*DeleteBookingResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteBookingResponse) + err := c.cc.Invoke(ctx, TrainTravelService_DeleteBooking_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TrainTravelServiceServer is the server API for TrainTravelService service. +// All implementations should embed UnimplementedTrainTravelServiceServer +// for forward compatibility. +type TrainTravelServiceServer interface { + // Get a list of train stations + // + // Returns a paginated and searchable list of all train stations. + ListStations(context.Context, *ListStationsRequest) (*ListStationsResponse, error) + // Get available train trips + // + // Returns a list of available train trips between the specified origin and + // destination stations on the given date. + GetTrips(context.Context, *GetTripsRequest) (*GetTripsResponse, error) + // List existing bookings + // + // Returns a list of all trip bookings by the authenticated user. + ListBookings(context.Context, *ListBookingsRequest) (*ListBookingsResponse, error) + // Create a booking + // + // A booking is a temporary hold on a trip. It is not confirmed until the + // payment is processed. + CreateBooking(context.Context, *CreateBookingRequest) (*Booking, error) + // Get a booking + // + // Returns the details of a specific booking. + GetBooking(context.Context, *GetBookingRequest) (*Booking, error) + // Delete a booking + // + // Deletes a booking, cancelling the hold on the trip. + DeleteBooking(context.Context, *DeleteBookingRequest) (*DeleteBookingResponse, error) +} + +// UnimplementedTrainTravelServiceServer should be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTrainTravelServiceServer struct{} + +func (UnimplementedTrainTravelServiceServer) ListStations(context.Context, *ListStationsRequest) (*ListStationsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListStations not implemented") +} +func (UnimplementedTrainTravelServiceServer) GetTrips(context.Context, *GetTripsRequest) (*GetTripsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetTrips not implemented") +} +func (UnimplementedTrainTravelServiceServer) ListBookings(context.Context, *ListBookingsRequest) (*ListBookingsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListBookings not implemented") +} +func (UnimplementedTrainTravelServiceServer) CreateBooking(context.Context, *CreateBookingRequest) (*Booking, error) { + return nil, status.Error(codes.Unimplemented, "method CreateBooking not implemented") +} +func (UnimplementedTrainTravelServiceServer) GetBooking(context.Context, *GetBookingRequest) (*Booking, error) { + return nil, status.Error(codes.Unimplemented, "method GetBooking not implemented") +} +func (UnimplementedTrainTravelServiceServer) DeleteBooking(context.Context, *DeleteBookingRequest) (*DeleteBookingResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteBooking not implemented") +} +func (UnimplementedTrainTravelServiceServer) testEmbeddedByValue() {} + +// UnsafeTrainTravelServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TrainTravelServiceServer will +// result in compilation errors. +type UnsafeTrainTravelServiceServer interface { + mustEmbedUnimplementedTrainTravelServiceServer() +} + +func RegisterTrainTravelServiceServer(s grpc.ServiceRegistrar, srv TrainTravelServiceServer) { + // If the following call panics, it indicates UnimplementedTrainTravelServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&TrainTravelService_ServiceDesc, srv) +} + +func _TrainTravelService_ListStations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListStationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).ListStations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_ListStations_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).ListStations(ctx, req.(*ListStationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrainTravelService_GetTrips_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTripsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).GetTrips(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_GetTrips_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).GetTrips(ctx, req.(*GetTripsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrainTravelService_ListBookings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListBookingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).ListBookings(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_ListBookings_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).ListBookings(ctx, req.(*ListBookingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrainTravelService_CreateBooking_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateBookingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).CreateBooking(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_CreateBooking_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).CreateBooking(ctx, req.(*CreateBookingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrainTravelService_GetBooking_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBookingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).GetBooking(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_GetBooking_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).GetBooking(ctx, req.(*GetBookingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TrainTravelService_DeleteBooking_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteBookingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TrainTravelServiceServer).DeleteBooking(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TrainTravelService_DeleteBooking_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TrainTravelServiceServer).DeleteBooking(ctx, req.(*DeleteBookingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TrainTravelService_ServiceDesc is the grpc.ServiceDesc for TrainTravelService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TrainTravelService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "speakeasy.TrainTravelService", + HandlerType: (*TrainTravelServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListStations", + Handler: _TrainTravelService_ListStations_Handler, + }, + { + MethodName: "GetTrips", + Handler: _TrainTravelService_GetTrips_Handler, + }, + { + MethodName: "ListBookings", + Handler: _TrainTravelService_ListBookings_Handler, + }, + { + MethodName: "CreateBooking", + Handler: _TrainTravelService_CreateBooking_Handler, + }, + { + MethodName: "GetBooking", + Handler: _TrainTravelService_GetBooking_Handler, + }, + { + MethodName: "DeleteBooking", + Handler: _TrainTravelService_DeleteBooking_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "speakeasy/v1/speakeasy.proto", +} diff --git a/frameworks-grpc-gateway/sdk/.gitattributes b/frameworks-grpc-gateway/sdk/.gitattributes new file mode 100644 index 00000000..113eead5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.gitattributes @@ -0,0 +1,2 @@ +# This allows generated code to be indexed correctly +*.ts linguist-generated=false \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/.gitignore b/frameworks-grpc-gateway/sdk/.gitignore new file mode 100644 index 00000000..f9a3f76b --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.gitignore @@ -0,0 +1,30 @@ +/models +/models/errors +/types +/node_modules +/examples/node_modules +/lib +/sdk +/funcs +/react-query +/mcp-server +/hooks +/index.* +/core.* +/bin +/cjs +/esm +/dist +/.tsbuildinfo +/.eslintcache +/.tshy +/.tshy-* +/__tests__ +.DS_Store +**/.speakeasy/temp/ +**/.speakeasy/logs/ +.DS_Store +/.speakeasy/reports +.env +.env.local +.env.*.local diff --git a/frameworks-grpc-gateway/sdk/.npmignore b/frameworks-grpc-gateway/sdk/.npmignore new file mode 100644 index 00000000..670ac0a6 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.npmignore @@ -0,0 +1,19 @@ +**/* +!/FUNCTIONS.md +!/RUNTIMES.md +!/REACT_QUERY.md +!/**/*.ts +!/**/*.js +!/**/*.mjs +!/package.json +!/jsr.json +!/dist/**/*.json +!/esm/**/*.json +!/**/*.map + +/eslint.config.mjs +/.oxlintrc.json +/cjs +/.tshy +/.tshy-* +/__tests__ diff --git a/frameworks-grpc-gateway/sdk/.oxlintrc.json b/frameworks-grpc-gateway/sdk/.oxlintrc.json new file mode 100644 index 00000000..6ee29210 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.oxlintrc.json @@ -0,0 +1,14 @@ +{ + "rules": { + "no-constant-condition": "off", + "no-useless-escape": "off", + "no-unused-private-class-members": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-object-type": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-useless-empty-export": "off", + "unicorn/no-thenable": "off", + "unicorn/no-empty-file": "off" + } +} diff --git a/frameworks-grpc-gateway/sdk/.speakeasy/gen.lock b/frameworks-grpc-gateway/sdk/.speakeasy/gen.lock new file mode 100644 index 00000000..d003e19d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.speakeasy/gen.lock @@ -0,0 +1,484 @@ +lockVersion: 2.0.0 +id: 6db435f6-9efb-42dd-bd6b-65c560bb7019 +management: + docChecksum: 8649ab00224172c421aef0795eddf1e1 + docVersion: 1.2.1 + speakeasyVersion: 1.771.1 + generationVersion: 2.893.0 + releaseVersion: 0.0.1 + configChecksum: 9245ac9755cd005e0bf57ea87d5683cb +persistentEdits: + generation_id: 7f6f65eb-be43-4895-a3b0-fee19265a09a + pristine_commit_hash: 776e5dc8f7dca88f243dc6d3058147f26b7c5d6f + pristine_tree_hash: afeaa4e699482b444997f05097482c61af0ae3e9 +features: + typescript: + additionalDependencies: 0.1.0 + additionalProperties: 0.1.3 + core: 3.27.0 + defaultEnabledRetries: 0.1.0 + envVarSecurityUsage: 0.1.2 + globalSecurityCallbacks: 0.1.0 + globalServerURLs: 2.83.1 + methodArguments: 0.1.2 + responseFormat: 0.3.0 + retries: 2.83.0 + sdkHooks: 0.4.0 +trackedFiles: + .gitattributes: + id: 24139dae6567 + last_write_checksum: sha1:26d16d8297b104945139da8c25bd38feeaad3e25 + pristine_git_object: 113eead5093c17d43a46159132885318f281a68a + .npmignore: + id: aa70c1f807c3 + last_write_checksum: sha1:6572da81f2e7a978ec29c22c2210bf461c5897b2 + pristine_git_object: 670ac0a62d7e568c9d44a14f5deafb8442a255d8 + .oxlintrc.json: + id: 8b9b571b1983 + last_write_checksum: sha1:f14bd09c455fa2de0d94641369b7888d23ee62f2 + pristine_git_object: 6ee2921010070a544e252596defc55d501e1c1ae + FUNCTIONS.md: + id: 21b9df02aaeb + last_write_checksum: sha1:e43d8532a181a31bee5dc4bb2c7a24974b0cf6f3 + pristine_git_object: 783e1882a29ace5a12da301ce86525d7df0c2683 + RUNTIMES.md: + id: 620c490847b6 + last_write_checksum: sha1:e45b854f02c357cbcfdb8c3663000e8339e16505 + pristine_git_object: 27731c3b5ace66bedc454ed5acbe15075aacd3dc + USAGE.md: + id: 3aed33ce6e6f + last_write_checksum: sha1:df3f7d9ce4bb5b40aa8e365d78c8c296eb4a541d + pristine_git_object: f6c0df27bf71c6129b6eb2d1c51a644846341039 + docs/lib/utils/retryconfig.md: + id: 0ce9707cb848 + last_write_checksum: sha1:bc4454e196fcd219f5a78da690375a884f5ed07b + pristine_git_object: 08f95f4552349360b2c0b01802aa71ec3a55d2c2 + docs/models/operations/create-booking-response.md: + id: 09d4e2d17e54 + last_write_checksum: sha1:7e44cc67be70dbae86fd96c4517bf4820a577ba8 + pristine_git_object: f2554ce5ed1cc3f1156fd50779f058309db90c54 + docs/models/operations/delete-booking-request.md: + id: 3a4272ec20ac + last_write_checksum: sha1:e084bd794c7bb0768e789e2caaa5a95beafd617f + pristine_git_object: 7ab12949047b09646a6c7eef0db6bc18a80cc84f + docs/models/operations/delete-booking-response.md: + id: 209fde674a77 + last_write_checksum: sha1:c8cbc2f1250d4e4cc2cacaef783a654f42567c28 + pristine_git_object: e6661f7d3bb39b0bdd6e6ed8d0e7cca3499a729a + docs/models/operations/get-booking-request.md: + id: d070a561ec0d + last_write_checksum: sha1:8dcf91de3c6d3241a2c643e98a7d6430ba5963cc + pristine_git_object: 6ab94380c89ba4bae8b92dc11da403202f8357d0 + docs/models/operations/get-booking-response.md: + id: a3fa76330eec + last_write_checksum: sha1:1cb46e93ec98b035a0a372696e2e9595ebbf3054 + pristine_git_object: 2a522586390195de971950f00da985401731397f + docs/models/operations/get-bookings-empty.md: + id: b7bbabdf8f32 + last_write_checksum: sha1:a7d301c350a2615a3059e6c6b8329b5ff86c6413 + pristine_git_object: c4a28c276ba2629a61227ca3d0490c73df6c5cc4 + docs/models/operations/get-bookings-request.md: + id: f03f42449dde + last_write_checksum: sha1:e77ee55cfee6fa5c380ae3659da83ed6e4a4f24d + pristine_git_object: 285dac14deeacb594a8ff05ae3af43562310e0d3 + docs/models/operations/get-bookings-response.md: + id: f9b6350a85c0 + last_write_checksum: sha1:d84cf1de6e63c44561cafe11cb2c189122061a19 + pristine_git_object: 2e113c7c3b3e3824a69241fb05a5945ef659549a + docs/models/operations/get-stations-empty.md: + id: 6d7f24cb2d1c + last_write_checksum: sha1:6d7c30eda59784636a7d10bf5fb07586c591573b + pristine_git_object: 7796864755a5cf4a63727f1a7475f433015c82cf + docs/models/operations/get-stations-request.md: + id: 7f3c9178d327 + last_write_checksum: sha1:f379639bb74cda5d7c8e99d630602a145b186d00 + pristine_git_object: 481b78bee0a843677f7436c5d7b24997cb714852 + docs/models/operations/get-stations-response.md: + id: fc7c312d7b9b + last_write_checksum: sha1:31bf758a6f2e9c34a78d122c1d3e86ae34017810 + pristine_git_object: bef25557eb3de36794b3808c4c2369c9f59fd057 + docs/models/operations/get-trips-request.md: + id: 386383fcf15a + last_write_checksum: sha1:ef36ecdfb1ac469a876009a5db4fe5ca36c8e018 + pristine_git_object: be89520500814c972b488675abc460aae12349da + docs/models/operations/get-trips-response.md: + id: c73a4a7d3456 + last_write_checksum: sha1:1212a8785bf1a45b8c2afc10c10a4fb8a43c8e87 + pristine_git_object: f2cde6924e3b708a45ec3aeef9fa370f0d94b4cf + docs/models/protobuf-any.md: + id: 93eaf007f5cf + last_write_checksum: sha1:421ea46266112e7e13c5468c5f59fabde506ba7e + pristine_git_object: 53e54e117180c89178aa19159ce24db1e0c62fbc + docs/models/rpc-status.md: + id: b207ee1530d1 + last_write_checksum: sha1:3a8750c2970a61692ad9d0e0f3b319ebc33d8fae + pristine_git_object: 0c49c8768c645c33b22970842aaf1c2a3c15c646 + docs/models/speakeasy-booking.md: + id: 34f196349da6 + last_write_checksum: sha1:f2105ae404a08fc57d417bb536e5687593b0a696 + pristine_git_object: e1cb671447ae83edc91e1fca33f0daa62bc011f9 + docs/models/speakeasy-delete-booking-response.md: + id: 5af7d72ab07f + last_write_checksum: sha1:1d19667ad43b2424711993a365f110480f03bdd1 + pristine_git_object: cb1a7a15afd1635a48a6f75c4fd3a2d9a0f527b0 + docs/models/speakeasy-station.md: + id: 421a8fed4364 + last_write_checksum: sha1:f05bca60b8801e5aa8a7b5f82e8e46fe2c66c5c2 + pristine_git_object: 99d3420b10ff32ef78bd6a19efdd62f618d7e177 + docs/models/speakeasy-trip.md: + id: 451e7d907caa + last_write_checksum: sha1:8dfadc82c009fc6647cd64715b577ddd58bd33cc + pristine_git_object: 0a6ee489524b4f009b07353bf3921d0a0ee86008 + docs/sdks/bookings/README.md: + id: e45a5ceed45e + last_write_checksum: sha1:f51cc0f7b0772cf49065c013ee8605543b8152dd + pristine_git_object: d5b30ba82439107fdc636a18f1164a3bdec7cdb9 + docs/sdks/stations/README.md: + id: 1299da0ccf94 + last_write_checksum: sha1:7361cbb6a767078949dc5acc2594b0c6c8a16803 + pristine_git_object: 572b1ab58ac8113e5572b17286c68136e076b4eb + docs/sdks/trips/README.md: + id: 2e6550a2a974 + last_write_checksum: sha1:a697266f4536ee883dac00597832c801d2737567 + pristine_git_object: 1502efacbf58c52c1c5426631b86b8e1c168452e + examples/.env.template: + id: 52cbe5afed92 + last_write_checksum: sha1:3b434186193b0d73d816d1f3e79cfee648902990 + pristine_git_object: 3f6a0be37a3294e170966fe5e0cf84da55860533 + examples/README.md: + id: 9d0f69d6e677 + last_write_checksum: sha1:0d1825825329479739f6bad07748193191d8fc8b + pristine_git_object: 9ed75dd928c899d5e42c20edb2df88dfdc4269ed + examples/bookingsGetBookings.example.ts: + id: a257f157db86 + last_write_checksum: sha1:e42f4a32b1cff673d563af458c809607275ea097 + pristine_git_object: b1cc6b6f6b1ebe052807fd654840858be026aade + examples/package.json: + id: c1d7b0ec8e7e + last_write_checksum: sha1:76d8594289593d7de70fdcf1b4a93fb2e7c2b872 + pristine_git_object: df551dc78fe1ee1914102051ff6c545538b24e3b + jsr.json: + id: 7f6ab7767282 + last_write_checksum: sha1:34d431f4fb2f1ae8586bfd82ad4f2d6aa35138d7 + pristine_git_object: 80dc5af47aa437c62d8ced03c581dc763a3dfb7b + package.json: + id: 7030d0b2f71b + last_write_checksum: sha1:03c6f25ac8212b8c232f318dc89b7d58f5f07128 + pristine_git_object: abdcf8be5f8acfd059071c00b0a4722adc9dc37e + src/core.ts: + id: f431fdbcd144 + last_write_checksum: sha1:ce840032c45608f5f4022b32bcce32cb69c5ed26 + pristine_git_object: c4486e4ac5f122d666243b52072232a064716999 + src/funcs/bookings-create-booking.ts: + id: 851ec179dbd4 + last_write_checksum: sha1:3b8b3de763a17653afa8de578613e5662fa33f9b + pristine_git_object: c41c6fc58d92d0d4b1db5cc256e617ccbeb63291 + src/funcs/bookings-delete-booking.ts: + id: e3eb13a0f749 + last_write_checksum: sha1:d88a8b9209abda5610258762407a187a5a7bdeac + pristine_git_object: 3e4d6108b9ddebd6c7ee0fed806cc4284c9c902a + src/funcs/bookings-get-booking.ts: + id: 2b4dc4f3f138 + last_write_checksum: sha1:5149679690eff78f07593fe5d1dee16c396bcc94 + pristine_git_object: 4a8b69394b51349372be2df91b16f7f674836a61 + src/funcs/bookings-get-bookings.ts: + id: b398fdf105e9 + last_write_checksum: sha1:f8c71a0aac31e66425e50fe459ac6c9604c5b8a4 + pristine_git_object: b16c8ac76a73d06f8fcf0ad7be586259dccf48d4 + src/funcs/stations-get-stations.ts: + id: 77dc4db7174a + last_write_checksum: sha1:e6e79f342797a80af631041af35048a50104b5a3 + pristine_git_object: 7571a1d16be901b0ea5325046eeb5dbcd3b0f1e6 + src/funcs/trips-get-trips.ts: + id: 62268bf2dc69 + last_write_checksum: sha1:ef407da59a01d1d24776cb583c09435d7a8b970b + pristine_git_object: 69ddcf6d7ff79a474a9965c487ef45a4c86e93f0 + src/hooks/hooks.ts: + id: a2463fc6f69b + last_write_checksum: sha1:3a90d88b4c6c07247db8e5f6441a79538232394e + pristine_git_object: 94ad0a323123b34dc638717c5d67a3c23c54d5c6 + src/hooks/index.ts: + id: 5f2dab62b520 + last_write_checksum: sha1:1a11c073a6cd792932218e1d4ed36b8567bad8ef + pristine_git_object: f60ec7acd76141a71d78008c5a362eb0236d8335 + src/hooks/types.ts: + id: 2a02d86ec24f + last_write_checksum: sha1:27a3444c4839bec58ae89820fd8c2483edafc3f4 + pristine_git_object: 9c36bf01332084f735909a71036c3544cc4c7e3a + src/index.ts: + id: c5fb850250c7 + last_write_checksum: sha1:35c9cde3ae14b18b3f893a1afe8b6c44f8760f24 + pristine_git_object: dbcba164a0082e3bb8c43cf21fa7f8ffb9d35dfc + src/lib/base64.ts: + id: "598522066688" + last_write_checksum: sha1:8c94164f2f78e6041c853186fa01f659591f5913 + pristine_git_object: 44be0eae8246521b230e8e711a88eff738fc015d + src/lib/config.ts: + id: 320761608fb3 + last_write_checksum: sha1:56605cda5955ebdf8c8f3a7da25d126123824e48 + pristine_git_object: 00fefbc13ca09530d813b9a9f4163bc56db8c259 + src/lib/dlv.ts: + id: b1988214835a + last_write_checksum: sha1:1dd3e3fbb4550c4bf31f5ef997faff355d6f3250 + pristine_git_object: e81091f5e536f82739f71379b1fddc2d147c49e2 + src/lib/encodings.ts: + id: 3bd8ead98afd + last_write_checksum: sha1:3e6784ec96431e0d8f98f67b3eae38d7b368264c + pristine_git_object: caeae5469fc8bd37da5aa7fd46d928eec5dc4b3a + src/lib/files.ts: + id: e5efa54fcb95 + last_write_checksum: sha1:795c14026405d547bfc47012f7ad9666e530fa7f + pristine_git_object: 6ca6b37d35a12448da176ee2517cc036db12c81c + src/lib/http.ts: + id: 63a80782d37e + last_write_checksum: sha1:797cbf16d3c7c4d62d3ba0eedb08617524938457 + pristine_git_object: 13cf1fd7894a89f727b8d2db2ad24313a94f68c7 + src/lib/is-plain-object.ts: + id: b9862591096a + last_write_checksum: sha1:df687f25a9590b6fd95d39df41a652ae3fd4e517 + pristine_git_object: 61070d3d7c7e5e95b1ed7e7ea21869eb0767db77 + src/lib/logger.ts: + id: 7ee7741a02bf + last_write_checksum: sha1:0ec9d3a2a34c362746b767e4f9a746c055d57a7b + pristine_git_object: d181f2937d4128afbe9b0327e0f81d1fa9c877b7 + src/lib/matchers.ts: + id: d54b2253b719 + last_write_checksum: sha1:2fd91fd107b77cbdc569db60e4c973e793083dc9 + pristine_git_object: 29c742f1c675710b6f2e2774dc7d3929b92cf951 + src/lib/primitives.ts: + id: 74859f750b28 + last_write_checksum: sha1:987869fc54790b9c22e9182099103d6aa7c67016 + pristine_git_object: d21f1dc49affa4e6d324bdd60811b470fdfa7eb6 + src/lib/retries.ts: + id: b24a69d15639 + last_write_checksum: sha1:f3da74dffcbe2756c69393f8600205aa1dc06e68 + pristine_git_object: e3ce9aba72150fc3c18f3105d39b21aba809377b + src/lib/schemas.ts: + id: deb4b531fae1 + last_write_checksum: sha1:b4b5fd22340ebd5b2914ee7b177c797b28fba7ef + pristine_git_object: cf280491bf54dbd4d78e5c071c3b85cf5ca9e35e + src/lib/sdks.ts: + id: 8a6d91f1218d + last_write_checksum: sha1:d44b9fa5a6a0a9a5a4ab58b25a6ef151f6c1fe02 + pristine_git_object: c6777f2c93bfb20699a580bb75d559e624ebe720 + src/lib/security.ts: + id: 0502afa7922e + last_write_checksum: sha1:1d7359985fe9fbb1fdcf7422b3d13103a9f119ed + pristine_git_object: a9cfc4db61f7d7b2ad7cf4afd33c7b737bedbec3 + src/lib/url.ts: + id: b0057e24ed76 + last_write_checksum: sha1:3bceadd74bf0b31adf13eb06b7531077c3e07d65 + pristine_git_object: 79e7ce660b3732053e3adcbb5a4cdeb51496e8aa + src/models/errors/http-client-errors.ts: + id: 08390231aab8 + last_write_checksum: sha1:994ced121c54fecd0af038ccfb7855fbfd3868ec + pristine_git_object: b34f612124c797c2a1106b9735708f679a90b74f + src/models/errors/index.ts: + id: c4e22507cb83 + last_write_checksum: sha1:45487f6e0761693e168f85d5831b80eab0af2624 + pristine_git_object: f5808ce5e0696622850605a86048cfa278f083f2 + src/models/errors/response-validation-error.ts: + id: a6ce701a9571 + last_write_checksum: sha1:c49315a4fb74937fcaf57a6884e5d689bd820980 + pristine_git_object: 6dce5e64c1415bf74ed3712543e9f06c58e70c55 + src/models/errors/sdk-default-error.ts: + id: fec60946951b + last_write_checksum: sha1:8cd1fb6f041b4de96b40b91d74e57a55febd87ae + pristine_git_object: 8312be82bff61c04b55afc51fa8c926f9184bb39 + src/models/errors/sdk-error.ts: + id: 2c53a8a2befb + last_write_checksum: sha1:8699ba62e2be76f2bc78c2822a547a6456e38b74 + pristine_git_object: efcdf908437379b4143b6b2233bfba53800e2450 + src/models/errors/sdk-validation-error.ts: + id: 9b17e7ef247f + last_write_checksum: sha1:92771987c251f02fd86cfd824a3be709cd3c2837 + pristine_git_object: db00022e05d55600252f401eb0e6ab46278e0b07 + src/models/index.ts: + id: f93644b0f37e + last_write_checksum: sha1:718262a30d469f7c0958bf75c3c9dd9fcd0bfe3d + pristine_git_object: de597b2f92a382a0262cc0322d689d12cdef75e3 + src/models/operations/create-booking.ts: + id: 6e598a102c0b + last_write_checksum: sha1:5be7a5e4f71cbf28da84382c939400ccc7284b42 + pristine_git_object: 3a8aafe5a97cf805ee138ede5b73997cac4e7c27 + src/models/operations/delete-booking.ts: + id: 4534f0c414c1 + last_write_checksum: sha1:3e8e089d7b34718b9596534d51b58132ca05d336 + pristine_git_object: a5b81809536925c91bfe64d5c93224217d9af7e8 + src/models/operations/get-booking.ts: + id: 16ce32d9288b + last_write_checksum: sha1:59d0edd9f96b4e1d15441782db980fee5ae6e7ff + pristine_git_object: 4d68a56bbb47c357100db615d937f8a36b4d9264 + src/models/operations/get-bookings.ts: + id: a8ed7e78ca73 + last_write_checksum: sha1:18f8454d127efcc98db9726935ead18a62cca7f8 + pristine_git_object: 8ebc2375d545c959e12d4be3f2301b257e3dcd38 + src/models/operations/get-stations.ts: + id: ca82437c5755 + last_write_checksum: sha1:d9d7682d809b1e671425446bf1cb5e2f94e12117 + pristine_git_object: 204df6ee85d977cb0b5f1859cdbd7e4b0ba5af8c + src/models/operations/get-trips.ts: + id: 32e914faa10f + last_write_checksum: sha1:ba10bcee86e867f71cad54b00cafff75cd4e48dc + pristine_git_object: 445d1f77e8cf10417be5050f9542b49e0fc5035a + src/models/operations/index.ts: + id: 0d9ffaf774d2 + last_write_checksum: sha1:5f578ef427d089ab5690f3bfece2ff01c29c2e58 + pristine_git_object: 3f8f8639532c0d9039130d938f1f76d84b6f5e9c + src/models/protobuf-any.ts: + id: 01a26663bd0f + last_write_checksum: sha1:7c18c804ec45b016a7b5bab0be7bdee6b97c15ed + pristine_git_object: 3d2d80f3af993f21bbb1cf1a534f26532af23238 + src/models/rpc-status.ts: + id: a507005eecdd + last_write_checksum: sha1:a743f33f9b7e33659fad4e5e7e7b34ca2a7f9576 + pristine_git_object: 25d295857189761b38eafa0f4871adf92d02cff4 + src/models/speakeasy-booking.ts: + id: fde9d009fb37 + last_write_checksum: sha1:d5456c027c255074a7f4f01fee0d7deaa4dd658e + pristine_git_object: 4c64cfda53196a5e063e001dfd38324af6c7ace5 + src/models/speakeasy-delete-booking-response.ts: + id: 45fdd862afcc + last_write_checksum: sha1:82e3c7adc6538b0a75a61e25a2fa64d69ce0f893 + pristine_git_object: 70fdf62885faa53dbd10beeb0ae31158394209ed + src/models/speakeasy-station.ts: + id: 278a7caa225f + last_write_checksum: sha1:798c85eb804bbfe4fb9f3e8263a31b2e50b6039e + pristine_git_object: e78a9dcce80e771eb6842c6fb981bb52a6dfdb50 + src/models/speakeasy-trip.ts: + id: c06d32ee0238 + last_write_checksum: sha1:08ecd007ad8aeccbf5d8bfcfa7a0b46fcd43a4b6 + pristine_git_object: 54cef26e5d82c66c69f3f8199929e7a68cf4af94 + src/sdk/bookings.ts: + id: 49019c0d99de + last_write_checksum: sha1:5d75ef1c7eb58d117fab451c4095c87f12ec0fd3 + pristine_git_object: 23cf80e92fa65d18963f637ee9170cdfece3be04 + src/sdk/index.ts: + id: a857902a703f + last_write_checksum: sha1:ed6d64f2a6135349aa8498b8d8cff9ba85c7fb8f + pristine_git_object: ecac2264817bb369ff2dbf0f0e9029807e67ff77 + src/sdk/sdk.ts: + id: 784571af2f69 + last_write_checksum: sha1:1b5353dd0489ba0d351f362199357700d97a3121 + pristine_git_object: 914b268365002fc478257701ec4da0ad398603bf + src/sdk/stations.ts: + id: e0d10fffccfb + last_write_checksum: sha1:695604a4111be5503c205e23156e601edb17e3b9 + pristine_git_object: 591239a3dd569a84af7b754a6fb6875256a0773f + src/sdk/trips.ts: + id: 2e466ceaf03e + last_write_checksum: sha1:8f22ebf4b911649d57fe9465bb24179fb2527807 + pristine_git_object: d689f68348d2e228bdf14546163849aad6b5d566 + src/types/async.ts: + id: fac8da972f86 + last_write_checksum: sha1:675d04bb4b4081dc3482f18de98674f9b446e7b4 + pristine_git_object: 1543b95cf7be9b5a871d7395f259dbd65331dc21 + src/types/blobs.ts: + id: f8ab9b326c86 + last_write_checksum: sha1:fd681458b532138913c372b05b3e16407abaa7a1 + pristine_git_object: 3deb988d444f7d202eda723492d7c1d76c5c208d + src/types/const-date-time.ts: + id: b3487d7170f2 + last_write_checksum: sha1:ce05316b31b373401875d5e962d63d2422e09cf2 + pristine_git_object: 586ee46aa2273cdf680bb542b4ef5dd953a48a08 + src/types/default-to-zero-value.ts: + id: 6e0f763ae8c8 + last_write_checksum: sha1:2820c0bc8e71d475e79cc6b6947ca18d93f6310c + pristine_git_object: c4cad53db08687649a55c711c4f4b3a29f7b7d24 + src/types/enums.ts: + id: 4d03ddfe5100 + last_write_checksum: sha1:d7d0ffad0c128a11c5feb1f0ae8f3b507ee03c14 + pristine_git_object: a523bdde4b5b8144cf26414e32fc2319f2c466fa + src/types/fp.ts: + id: ae9f0fb8aab6 + last_write_checksum: sha1:d24994d58f37a1ca106847fcc6d2e976c6f1c093 + pristine_git_object: ccbe51eac69dfb0da5131f14fd753c4b47c1d527 + src/types/index.ts: + id: cd7a0a5ed005 + last_write_checksum: sha1:a591fec0ad2231615101ebb6a95cc4d043733a6c + pristine_git_object: d75cb43bf5811991f96203650c264af2b4c78b41 + src/types/operations.ts: + id: 314da01dca47 + last_write_checksum: sha1:c49e724cfb1259296381cb57bc0fcdd2eae45865 + pristine_git_object: beb81e10f0b9eee2ed398a853fe6f737b743f818 + src/types/primitives.ts: + id: 0cd7f4ac625d + last_write_checksum: sha1:a521a09fa18b1af857d29477d3aa7007b42583ed + pristine_git_object: 5c4e93d2030e8b820b6d8f060a275502a66c1a87 + src/types/rfcdate.ts: + id: 9eb64906dcd6 + last_write_checksum: sha1:8e5f382e02fe21c2f2e24deb1b7bbf8cddd3dd16 + pristine_git_object: c79b3f53a3dd922aa2ace5a4191c8652cf9a8e89 + src/types/smart-union.ts: + id: 2e3af2ad4ea7 + last_write_checksum: sha1:9928dbec2d8ad501da8518c10ec6df3b441eb5c9 + pristine_git_object: a9db55cfc1d1cbdcc480dfa5eeead3ee526d6953 + src/types/streams.ts: + id: f99be9c4bf14 + last_write_checksum: sha1:cc9ced2dd34a76c3a1bd7784ccda1cc3b37ab9a9 + pristine_git_object: a0163e7a99cea88f21d0ad4ce3af1c720180ee20 + src/types/unrecognized.ts: + id: 476605c40716 + last_write_checksum: sha1:d33ac686528492f4cdc0dbf81ed343b4afe020fe + pristine_git_object: b7a2a13f3dff50663429df24ae2f55b647ff1084 + tsconfig.json: + id: 61ebb9fd6e8c + last_write_checksum: sha1:4f9c4daf3281a9be06591b2814e5bbcf98b5c803 + pristine_git_object: e6cd950ad724815ee1ed48ef2e52389cbd3a9143 +examples: + get-bookings: + speakeasy-default-get-bookings: + responses: + "200": + application/json: [{"id": "1725ff48-ab45-4bb5-9d02-88745177dedb", "passengerName": "John Doe", "tripId": "ea399ba1-6d95-433f-92d1-83f67b775594"}] + default: + application/json: {} + create-booking: + speakeasy-default-create-booking: + requestBody: + application/json: {"id": "1725ff48-ab45-4bb5-9d02-88745177dedb", "passengerName": "John Doe", "tripId": "ea399ba1-6d95-433f-92d1-83f67b775594"} + responses: + "200": + application/json: {"id": "1725ff48-ab45-4bb5-9d02-88745177dedb", "passengerName": "John Doe", "tripId": "ea399ba1-6d95-433f-92d1-83f67b775594"} + default: + application/json: {} + delete-booking: + speakeasy-default-delete-booking: + parameters: + path: + bookingId: "48d54bad-a4c8-4f4b-93f8-4b87d6bb9985" + responses: + "200": + application/json: {} + default: + application/json: {} + get-booking: + speakeasy-default-get-booking: + parameters: + path: + bookingId: "2d020851-136a-4f7c-8968-d8dff0315c29" + responses: + "200": + application/json: {"id": "1725ff48-ab45-4bb5-9d02-88745177dedb", "passengerName": "John Doe", "tripId": "ea399ba1-6d95-433f-92d1-83f67b775594"} + default: + application/json: {} + get-stations: + speakeasy-default-get-stations: + responses: + "200": + application/json: [{"address": "Invalidenstrasse 10557 Berlin, Germany", "countryCode": "DE", "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", "name": "Berlin Hauptbahnhof", "timezone": "Europe/Berlin"}] + default: + application/json: {} + get-trips: + speakeasy-default-get-trips: + parameters: + query: + origin: "4c3b4398-6f36-47bf-b258-344f651bc875" + destination: "eccd3bf6-7737-47cd-b359-0596cf854de4" + date: "2026-07-10T20:57:33.258Z" + responses: + "200": + application/json: [{"destination": "b2e783e1-c824-4d63-b37a-d8d698862f1d", "id": "ea399ba1-6d95-433f-92d1-83f67b775594", "operator": "Deutsche Bahn", "origin": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", "price": 50}] + default: + application/json: {} +examplesVersion: 1.0.2 diff --git a/frameworks-grpc-gateway/sdk/.speakeasy/gen.yaml b/frameworks-grpc-gateway/sdk/.speakeasy/gen.yaml new file mode 100644 index 00000000..36c2abaa --- /dev/null +++ b/frameworks-grpc-gateway/sdk/.speakeasy/gen.yaml @@ -0,0 +1,92 @@ +configVersion: 2.0.0 +generation: + sdkClassName: SDK + maintainOpenAPIOrder: true + usageSnippets: + optionalPropertyRendering: withExample + sdkInitStyle: constructor + useClassNamesForArrayFields: true + fixes: + nameResolutionDec2023: true + nameResolutionFeb2025: true + parameterOrderingFeb2024: true + requestResponseComponentNamesFeb2024: true + securityFeb2025: true + sharedErrorComponentsApr2025: true + sharedNestedComponentsJan2026: true + nameOverrideFeb2026: true + auth: + oAuth2ClientCredentialsEnabled: false + oAuth2PasswordEnabled: false + hoistGlobalSecurity: true + inferSSEOverload: true + sdkHooksConfigAccess: true + schemas: + allOfMergeStrategy: shallowMerge + requestBodyFieldName: body + versioningStrategy: automatic + persistentEdits: {} + tests: + generateTests: false + generateNewTests: true + skipResponseBodyAssertions: false +typescript: + version: 0.0.1 + acceptHeaderEnum: false + additionalDependencies: + dependencies: {} + devDependencies: {} + peerDependencies: {} + additionalPackageJSON: {} + additionalScripts: {} + alwaysIncludeInboundAndOutbound: false + author: Speakeasy + baseErrorName: SDKError + clientServerStatusCodesAsErrors: true + constFieldsAlwaysOptional: false + defaultErrorName: SDKDefaultError + enableCustomCodeRegions: false + enableMCPServer: false + enableReactQuery: false + enumFormat: union + exportZodModelNamespace: false + fixEnumNameSanitization: false + flatAdditionalProperties: true + flattenGlobalSecurity: true + flatteningOrder: parameters-first + formStringArrayEncodeMode: array + forwardCompatibleEnumsByDefault: true + forwardCompatibleUnionsByDefault: tagged-only + generateExamples: true + imports: + option: openapi + paths: + callbacks: models/callbacks + errors: models/errors + operations: models/operations + shared: models + webhooks: models/webhooks + inferUnionDiscriminators: true + inputModelSuffix: input + jsonpath: rfc9535 + laxMode: lax + legacyFileNaming: false + maxMethodParams: 0 + methodArguments: infer-optional-args + modelPropertyCasing: camel + moduleFormat: esm + multipartArrayFormat: standard + outputModelSuffix: output + packageName: openapi + preApplyUnionDiscriminators: true + preserveModelFieldNames: false + privateIdentifierPrefix: '#' + responseFormat: flat + sseFlatResponse: false + templateVersion: v2 + unionStrategy: populated-fields + usageSDKInitImports: [] + useIndexModules: true + useOxlint: true + useTsgo: true + zodVersion: v4-mini diff --git a/frameworks-grpc-gateway/sdk/CONTRIBUTING.md b/frameworks-grpc-gateway/sdk/CONTRIBUTING.md new file mode 100644 index 00000000..d585717f --- /dev/null +++ b/frameworks-grpc-gateway/sdk/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing to This Repository + +Thank you for your interest in contributing to this repository. Please note that this repository contains generated code. As such, we do not accept direct changes or pull requests. Instead, we encourage you to follow the guidelines below to report issues and suggest improvements. + +## How to Report Issues + +If you encounter any bugs or have suggestions for improvements, please open an issue on GitHub. When reporting an issue, please provide as much detail as possible to help us reproduce the problem. This includes: + +- A clear and descriptive title +- Steps to reproduce the issue +- Expected and actual behavior +- Any relevant logs, screenshots, or error messages +- Information about your environment (e.g., operating system, software versions) + - For example can be collected using the `npx envinfo` command from your terminal if you have Node.js installed + +## Issue Triage and Upstream Fixes + +We will review and triage issues as quickly as possible. Our goal is to address bugs and incorporate improvements in the upstream source code. Fixes will be included in the next generation of the generated code. + +## Contact + +If you have any questions or need further assistance, please feel free to reach out by opening an issue. + +Thank you for your understanding and cooperation! + +The Maintainers diff --git a/frameworks-grpc-gateway/sdk/FUNCTIONS.md b/frameworks-grpc-gateway/sdk/FUNCTIONS.md new file mode 100644 index 00000000..783e1882 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/FUNCTIONS.md @@ -0,0 +1,85 @@ +# Standalone Functions + +> [!NOTE] +> This section is useful if you are using a bundler and targeting browsers and +> runtimes where the size of an application affects performance and load times. + +Every method in this SDK is also available as a standalone function. This +alternative API is suitable when targeting the browser or serverless runtimes +and using a bundler to build your application since all unused functionality +will be tree-shaken away. This includes code for unused methods, Zod schemas, +encoding helpers and response handlers. The result is dramatically smaller +impact on the application's final bundle size which grows very slowly as you use +more and more functionality from this SDK. + +Calling methods through the main SDK class remains a valid and generally more +more ergonomic option. Standalone functions represent an optimisation for a +specific category of applications. + +## Example + +```typescript +import { SDKCore } from "openapi/core.js"; +import { bookingsGetBookings } from "openapi/funcs/bookings-get-bookings.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await bookingsGetBookings(sdk); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("bookingsGetBookings failed:", res.error); + } +} + +run(); +``` + +## Result types + +Standalone functions differ from SDK methods in that they return a +`Result` type to capture _known errors_ and document them using +the type system. By avoiding throwing errors, application code maintains clear +control flow and error-handling become part of the regular flow of application +code. + +> We use the term "known errors" because standalone functions, and JavaScript +> code in general, can still throw unexpected errors such as `TypeError`s, +> `RangeError`s and `DOMException`s. Exhaustively catching all errors may be +> something this SDK addresses in the future. Nevertheless, there is still a lot +> of benefit from capturing most errors and turning them into values. + +The second reason for this style of programming is because these functions will +typically be used in front-end applications where exception throwing is +sometimes discouraged or considered unidiomatic. React and similar ecosystems +and libraries tend to promote this style of programming so that components +render useful content under all states (loading, success, error and so on). + +The general pattern when calling standalone functions looks like this: + +```typescript +import { Core } from ""; +import { fetchSomething } from "/funcs/fetchSomething.js"; + +const client = new Core(); + +async function run() { + const result = await fetchSomething(client, { id: "123" }); + if (!result.ok) { + // You can throw the error or handle it. It's your choice now. + throw result.error; + } + + console.log(result.value); +} + +run(); +``` + +Notably, `result.error` above will have an explicit type compared to a try-catch +variation where the error in the catch block can only be of type `unknown` (or +`any` depending on your TypeScript settings). \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/README.md b/frameworks-grpc-gateway/sdk/README.md new file mode 100644 index 00000000..addb5714 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/README.md @@ -0,0 +1,383 @@ +# openapi + +Developer-friendly & type-safe Typescript SDK specifically catered to leverage *openapi* API. + +[![Built by Speakeasy](https://img.shields.io/badge/Built_by-SPEAKEASY-374151?style=for-the-badge&labelColor=f3f4f6)](https://www.speakeasy.com/?utm_source=openapi&utm_campaign=typescript) +[![License: MIT](https://img.shields.io/badge/LICENSE_//_MIT-3b5bdb?style=for-the-badge&labelColor=eff6ff)](https://opensource.org/licenses/MIT) + + +

+> [!IMPORTANT] +> This SDK is not yet ready for production use. To complete setup please follow the steps outlined in your [workspace](https://app.speakeasy.com/org/phil-sturgeon/phil-sturgeon). Delete this section before > publishing to a package manager. + + +## Summary + +Train Travel API: API for finding and booking train trips across Europe. + + + +## Table of Contents + +* [openapi](#openapi) + * [SDK Installation](#sdk-installation) + * [Requirements](#requirements) + * [SDK Example Usage](#sdk-example-usage) + * [Available Resources and Operations](#available-resources-and-operations) + * [Standalone functions](#standalone-functions) + * [Retries](#retries) + * [Error Handling](#error-handling) + * [Server Selection](#server-selection) + * [Custom HTTP Client](#custom-http-client) + * [Debugging](#debugging) +* [Development](#development) + * [Maturity](#maturity) + * [Contributions](#contributions) + + + + +## SDK Installation + +> [!TIP] +> To finish publishing your SDK to npm and others you must [run your first generation action](https://www.speakeasy.com/docs/github-setup#step-by-step-guide). + + +The SDK can be installed with either [npm](https://www.npmjs.com/), [pnpm](https://pnpm.io/), [bun](https://bun.sh/) or [yarn](https://classic.yarnpkg.com/en/) package managers. + +### NPM + +```bash +npm add +``` + +### PNPM + +```bash +pnpm add +``` + +### Bun + +```bash +bun add +``` + +### Yarn + +```bash +yarn add +``` + +> [!NOTE] +> This package is published as an ES Module (ESM) only. For applications using +> CommonJS, use `await import()` to import and use this package. + + + +## Requirements + +For supported JavaScript runtimes, please consult [RUNTIMES.md](RUNTIMES.md). + + + +## SDK Example Usage + +### Example + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +run(); + +``` + + + +## Available Resources and Operations + +
+Available methods + +### [Bookings](docs/sdks/bookings/README.md) + +* [getBookings](docs/sdks/bookings/README.md#getbookings) - List existing bookings +* [createBooking](docs/sdks/bookings/README.md#createbooking) - Create a booking +* [deleteBooking](docs/sdks/bookings/README.md#deletebooking) - Delete a booking +* [getBooking](docs/sdks/bookings/README.md#getbooking) - Get a booking + +### [Stations](docs/sdks/stations/README.md) + +* [getStations](docs/sdks/stations/README.md#getstations) - Get a list of train stations + +### [Trips](docs/sdks/trips/README.md) + +* [getTrips](docs/sdks/trips/README.md#gettrips) - Get available train trips + +
+ + + +## Standalone functions + +All the methods listed above are available as standalone functions. These +functions are ideal for use in applications running in the browser, serverless +runtimes or other environments where application bundle size is a primary +concern. When using a bundler to build your application, all unused +functionality will be either excluded from the final bundle or tree-shaken away. + +To read more about standalone functions, check [FUNCTIONS.md](./FUNCTIONS.md). + +
+ +Available standalone functions + +- [`bookingsCreateBooking`](docs/sdks/bookings/README.md#createbooking) - Create a booking +- [`bookingsDeleteBooking`](docs/sdks/bookings/README.md#deletebooking) - Delete a booking +- [`bookingsGetBooking`](docs/sdks/bookings/README.md#getbooking) - Get a booking +- [`bookingsGetBookings`](docs/sdks/bookings/README.md#getbookings) - List existing bookings +- [`stationsGetStations`](docs/sdks/stations/README.md#getstations) - Get a list of train stations +- [`tripsGetTrips`](docs/sdks/trips/README.md#gettrips) - Get available train trips + +
+ + + +## Retries + +Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK. + +To change the default retry strategy for a single API call, simply provide a retryConfig object to the call: +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.getBookings(undefined, { + retries: { + strategy: "backoff", + backoff: { + initialInterval: 1, + maxInterval: 50, + exponent: 1.1, + maxElapsedTime: 100, + }, + retryConnectionErrors: false, + }, + }); + + console.log(result); +} + +run(); + +``` + +If you'd like to override the default retry strategy for all operations that support retries, you can provide a retryConfig at SDK initialization: +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK({ + retryConfig: { + strategy: "backoff", + backoff: { + initialInterval: 1, + maxInterval: 50, + exponent: 1.1, + maxElapsedTime: 100, + }, + retryConnectionErrors: false, + }, +}); + +async function run() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +run(); + +``` + + + +## Error Handling + +[`SDKError`](./src/models/errors/sdk-error.ts) is the base class for all HTTP error responses. It has the following properties: + +| Property | Type | Description | +| ------------------- | ---------- | ------------------------------------------------------ | +| `error.message` | `string` | Error message | +| `error.statusCode` | `number` | HTTP response status code eg `404` | +| `error.headers` | `Headers` | HTTP response headers | +| `error.body` | `string` | HTTP body. Can be empty string if no body is returned. | +| `error.rawResponse` | `Response` | Raw HTTP response | + +### Example +```typescript +import { SDK } from "openapi"; +import * as errors from "openapi/models/errors"; + +const sdk = new SDK(); + +async function run() { + try { + const result = await sdk.bookings.getBookings(); + + console.log(result); + } catch (error) { + if (error instanceof errors.SDKError) { + console.log(error.message); + console.log(error.statusCode); + console.log(error.body); + console.log(error.headers); + } + } +} + +run(); + +``` + +### Error Classes +**Primary error:** +* [`SDKError`](./src/models/errors/sdk-error.ts): The base class for HTTP error responses. + +
Less common errors (6) + +
+ +**Network errors:** +* [`ConnectionError`](./src/models/errors/http-client-errors.ts): HTTP client was unable to make a request to a server. +* [`RequestTimeoutError`](./src/models/errors/http-client-errors.ts): HTTP request timed out due to an AbortSignal signal. +* [`RequestAbortedError`](./src/models/errors/http-client-errors.ts): HTTP request was aborted by the client. +* [`InvalidRequestError`](./src/models/errors/http-client-errors.ts): Any input used to create a request is invalid. +* [`UnexpectedClientError`](./src/models/errors/http-client-errors.ts): Unrecognised or unexpected error. + + +**Inherit from [`SDKError`](./src/models/errors/sdk-error.ts)**: +* [`ResponseValidationError`](./src/models/errors/response-validation-error.ts): Type mismatch between the data returned from the server and the structure expected by the SDK. See `error.rawValue` for the raw value and `error.pretty()` for a nicely formatted multi-line string. + +
+ + + +## Server Selection + +### Override Server URL Per-Client + +The default server can be overridden globally by passing a URL to the `serverURL: string` optional parameter when initializing the SDK client instance. For example: +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK({ + serverURL: "https://api.example.com/", +}); + +async function run() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +run(); + +``` + + + +## Custom HTTP Client + +The TypeScript SDK makes API calls using an `HTTPClient` that wraps the native +[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). This +client is a thin wrapper around `fetch` and provides the ability to attach hooks +around the request lifecycle that can be used to modify the request or handle +errors and response. + +The `HTTPClient` constructor takes an optional `fetcher` argument that can be +used to integrate a third-party HTTP client or when writing tests to mock out +the HTTP client and feed in fixtures. + +The following example shows how to: +- route requests through a proxy server using [undici](https://www.npmjs.com/package/undici)'s ProxyAgent +- use the `"beforeRequest"` hook to add a custom header and a timeout to requests +- use the `"requestError"` hook to log errors + +```typescript +import { SDK } from "openapi"; +import { ProxyAgent } from "undici"; +import { HTTPClient } from "openapi/lib/http"; + +const dispatcher = new ProxyAgent("http://proxy.example.com:8080"); + +const httpClient = new HTTPClient({ + // 'fetcher' takes a function that has the same signature as native 'fetch'. + fetcher: (input, init) => + // 'dispatcher' is specific to undici and not part of the standard Fetch API. + fetch(input, { ...init, dispatcher } as RequestInit), +}); + +httpClient.addHook("beforeRequest", (request) => { + const nextRequest = new Request(request, { + signal: request.signal || AbortSignal.timeout(5000) + }); + + nextRequest.headers.set("x-custom-header", "custom value"); + + return nextRequest; +}); + +httpClient.addHook("requestError", (error, request) => { + console.group("Request Error"); + console.log("Reason:", `${error}`); + console.log("Endpoint:", `${request.method} ${request.url}`); + console.groupEnd(); +}); + +const sdk = new SDK({ httpClient: httpClient }); +``` + + + +## Debugging + +You can setup your SDK to emit debug logs for SDK requests and responses. + +You can pass a logger that matches `console`'s interface as an SDK option. + +> [!WARNING] +> Beware that debug logging will reveal secrets, like API tokens in headers, in log messages printed to a console or files. It's recommended to use this feature only during local development and not in production. + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK({ debugLogger: console }); +``` + + + + +# Development + +## Maturity + +This SDK is in beta, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning usage +to a specific package version. This way, you can install the same version each time without breaking changes unless you are intentionally +looking for the latest version. + +## Contributions + +While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. +We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release. + +### SDK Created by [Speakeasy](https://www.speakeasy.com/?utm_source=openapi&utm_campaign=typescript) diff --git a/frameworks-grpc-gateway/sdk/RUNTIMES.md b/frameworks-grpc-gateway/sdk/RUNTIMES.md new file mode 100644 index 00000000..27731c3b --- /dev/null +++ b/frameworks-grpc-gateway/sdk/RUNTIMES.md @@ -0,0 +1,48 @@ +# Supported JavaScript runtimes + +This SDK is intended to be used in JavaScript runtimes that support ECMAScript 2020 or newer. The SDK uses the following features: + +- [Web Fetch API][web-fetch] +- [Web Streams API][web-streams] and in particular `ReadableStream` +- [Async iterables][async-iter] using `Symbol.asyncIterator` + +[web-fetch]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API +[web-streams]: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API +[async-iter]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols + +Runtime environments that are explicitly supported are: + +- Evergreen browsers which include: Chrome, Safari, Edge, Firefox +- Node.js active and maintenance LTS releases + - Currently, this is v18 and v20 +- Bun v1 and above +- Deno v1.39 + - Note that Deno does not currently have native support for streaming file uploads backed by the filesystem ([issue link][deno-file-streaming]) + +[deno-file-streaming]: https://github.com/denoland/deno/issues/11018 + +## Recommended TypeScript compiler options + +The following `tsconfig.json` options are recommended for projects using this +SDK in order to get static type support for features like async iterables, +streams and `fetch`-related APIs ([`for await...of`][for-await-of], +[`AbortSignal`][abort-signal], [`Request`][request], [`Response`][response] and +so on): + +[for-await-of]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of +[abort-signal]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal +[request]: https://developer.mozilla.org/en-US/docs/Web/API/Request +[response]: https://developer.mozilla.org/en-US/docs/Web/API/Response + +```jsonc +{ + "compilerOptions": { + "target": "es2020", // or higher + "lib": ["es2020", "dom", "dom.iterable"] + } +} +``` + +While `target` can be set to older ECMAScript versions, it may result in extra, +unnecessary compatibility code being generated if you are not targeting old +runtimes. diff --git a/frameworks-grpc-gateway/sdk/USAGE.md b/frameworks-grpc-gateway/sdk/USAGE.md new file mode 100644 index 00000000..f6c0df27 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/USAGE.md @@ -0,0 +1,16 @@ + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +run(); + +``` + \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/lib/utils/retryconfig.md b/frameworks-grpc-gateway/sdk/docs/lib/utils/retryconfig.md new file mode 100644 index 00000000..08f95f45 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/lib/utils/retryconfig.md @@ -0,0 +1,24 @@ +# RetryConfig + +Allows customizing the default retry configuration. It is only permitted in methods that accept retry policies. + +## Fields + +| Name | Type | Description | Example | +| ------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------ | ----------- | +| `strategy` | `"backoff" | "none"` | The retry strategy to use. | `"backoff"` | +| `backoff` | [BackoffStrategy](#backoffstrategy) | When strategy is "backoff", this configurates for the backoff parameters. | | +| `retryConnectionErrors` | `*boolean*` | When strategy is "backoff", this determines whether or not to retry on connection errors. | `true` | + +## BackoffStrategy + +The backoff strategy allows retrying a request with an exponential backoff between each retry. + +### Fields + +| Name | Type | Description | Example | +| ------------------ | ------------ | ----------------------------------------- | -------- | +| `initialInterval` | `*number*` | The initial interval in milliseconds. | `500` | +| `maxInterval` | `*number*` | The maximum interval in milliseconds. | `60000` | +| `exponent` | `*number*` | The exponent to use for the backoff. | `1.5` | +| `maxElapsedTime` | `*number*` | The maximum elapsed time in milliseconds. | `300000` | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/create-booking-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/create-booking-response.md new file mode 100644 index 00000000..f2554ce5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/create-booking-response.md @@ -0,0 +1,21 @@ +# CreateBookingResponse + + +## Supported Types + +### `models.SpeakeasyBooking` + +```typescript +const value: models.SpeakeasyBooking = { + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", +}; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-request.md b/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-request.md new file mode 100644 index 00000000..7ab12949 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-request.md @@ -0,0 +1,17 @@ +# DeleteBookingRequest + +## Example Usage + +```typescript +import { DeleteBookingRequest } from "openapi/models/operations"; + +let value: DeleteBookingRequest = { + bookingId: "6ceb7f96-ac3f-475b-bd4e-575983fabb4a", +}; +``` + +## Fields + +| Field | Type | Required | Description | +| -------------------------------- | -------------------------------- | -------------------------------- | -------------------------------- | +| `bookingId` | *string* | :heavy_check_mark: | The ID of the booking to delete. | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-response.md new file mode 100644 index 00000000..e6661f7d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/delete-booking-response.md @@ -0,0 +1,17 @@ +# DeleteBookingResponse + + +## Supported Types + +### `models.SpeakeasyDeleteBookingResponse` + +```typescript +const value: models.SpeakeasyDeleteBookingResponse = {}; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-request.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-request.md new file mode 100644 index 00000000..6ab94380 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-request.md @@ -0,0 +1,17 @@ +# GetBookingRequest + +## Example Usage + +```typescript +import { GetBookingRequest } from "openapi/models/operations"; + +let value: GetBookingRequest = { + bookingId: "813f9443-5985-46eb-b9ae-5140aad4711d", +}; +``` + +## Fields + +| Field | Type | Required | Description | +| ---------------------------------- | ---------------------------------- | ---------------------------------- | ---------------------------------- | +| `bookingId` | *string* | :heavy_check_mark: | The ID of the booking to retrieve. | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-response.md new file mode 100644 index 00000000..2a522586 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-booking-response.md @@ -0,0 +1,21 @@ +# GetBookingResponse + + +## Supported Types + +### `models.SpeakeasyBooking` + +```typescript +const value: models.SpeakeasyBooking = { + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", +}; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-empty.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-empty.md new file mode 100644 index 00000000..c4a28c27 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-empty.md @@ -0,0 +1,14 @@ +# GetBookingsEmpty + +## Example Usage + +```typescript +import { GetBookingsEmpty } from "openapi/models/operations"; + +let value: GetBookingsEmpty = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-request.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-request.md new file mode 100644 index 00000000..285dac14 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-request.md @@ -0,0 +1,15 @@ +# GetBookingsRequest + +## Example Usage + +```typescript +import { GetBookingsRequest } from "openapi/models/operations"; + +let value: GetBookingsRequest = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `empty` | [operations.GetBookingsEmpty](../../models/operations/get-bookings-empty.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-response.md new file mode 100644 index 00000000..2e113c7c --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-bookings-response.md @@ -0,0 +1,23 @@ +# GetBookingsResponse + + +## Supported Types + +### `models.SpeakeasyBooking[]` + +```typescript +const value: models.SpeakeasyBooking[] = [ + { + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", + }, +]; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-empty.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-empty.md new file mode 100644 index 00000000..77968647 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-empty.md @@ -0,0 +1,14 @@ +# GetStationsEmpty + +## Example Usage + +```typescript +import { GetStationsEmpty } from "openapi/models/operations"; + +let value: GetStationsEmpty = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-request.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-request.md new file mode 100644 index 00000000..481b78be --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-request.md @@ -0,0 +1,15 @@ +# GetStationsRequest + +## Example Usage + +```typescript +import { GetStationsRequest } from "openapi/models/operations"; + +let value: GetStationsRequest = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `empty` | [operations.GetStationsEmpty](../../models/operations/get-stations-empty.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-response.md new file mode 100644 index 00000000..bef25557 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-stations-response.md @@ -0,0 +1,25 @@ +# GetStationsResponse + + +## Supported Types + +### `models.SpeakeasyStation[]` + +```typescript +const value: models.SpeakeasyStation[] = [ + { + address: "Invalidenstrasse 10557 Berlin, Germany", + countryCode: "DE", + id: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + name: "Berlin Hauptbahnhof", + timezone: "Europe/Berlin", + }, +]; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-request.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-request.md new file mode 100644 index 00000000..be895205 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-request.md @@ -0,0 +1,23 @@ +# GetTripsRequest + +## Example Usage + +```typescript +import { GetTripsRequest } from "openapi/models/operations"; + +let value: GetTripsRequest = { + origin: "5de1e3e2-5a23-40e8-afc8-45118d2096e1", + destination: "a961de35-51b3-4e0b-9161-2789983b1ae6", + date: new Date("2024-04-06T17:57:36.526Z"), +}; +``` + +## Fields + +| Field | Type | Required | Description | +| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| `origin` | *string* | :heavy_check_mark: | The ID of the origin station. | +| `destination` | *string* | :heavy_check_mark: | The ID of the destination station. | +| `date` | [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) | :heavy_check_mark: | The date and time of the trip in ISO 8601 format. | +| `bicycles` | *boolean* | :heavy_minus_sign: | Only return trips where bicycles are allowed. | +| `dogs` | *boolean* | :heavy_minus_sign: | Only return trips where dogs are allowed. | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-response.md b/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-response.md new file mode 100644 index 00000000..f2cde692 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/operations/get-trips-response.md @@ -0,0 +1,25 @@ +# GetTripsResponse + + +## Supported Types + +### `models.SpeakeasyTrip[]` + +```typescript +const value: models.SpeakeasyTrip[] = [ + { + destination: "b2e783e1-c824-4d63-b37a-d8d698862f1d", + id: "ea399ba1-6d95-433f-92d1-83f67b775594", + operator: "Deutsche Bahn", + origin: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + price: 50, + }, +]; +``` + +### `models.RpcStatus` + +```typescript +const value: models.RpcStatus = {}; +``` + diff --git a/frameworks-grpc-gateway/sdk/docs/models/protobuf-any.md b/frameworks-grpc-gateway/sdk/docs/models/protobuf-any.md new file mode 100644 index 00000000..53e54e11 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/protobuf-any.md @@ -0,0 +1,16 @@ +# ProtobufAny + +## Example Usage + +```typescript +import { ProtobufAny } from "openapi/models"; + +let value: ProtobufAny = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ---------------------- | ---------------------- | ---------------------- | ---------------------- | +| `atType` | *string* | :heavy_minus_sign: | N/A | +| `additionalProperties` | Record | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/rpc-status.md b/frameworks-grpc-gateway/sdk/docs/models/rpc-status.md new file mode 100644 index 00000000..0c49c876 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/rpc-status.md @@ -0,0 +1,17 @@ +# RpcStatus + +## Example Usage + +```typescript +import { RpcStatus } from "openapi/models"; + +let value: RpcStatus = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- | +| `code` | *number* | :heavy_minus_sign: | N/A | +| `details` | [models.ProtobufAny](../models/protobuf-any.md)[] | :heavy_minus_sign: | N/A | +| `message` | *string* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/speakeasy-booking.md b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-booking.md new file mode 100644 index 00000000..e1cb6714 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-booking.md @@ -0,0 +1,25 @@ +# SpeakeasyBooking + +A booking for a train trip. + +## Example Usage + +```typescript +import { SpeakeasyBooking } from "openapi/models"; + +let value: SpeakeasyBooking = { + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", +}; +``` + +## Fields + +| Field | Type | Required | Description | Example | +| ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | +| `hasBicycle` | *boolean* | :heavy_minus_sign: | Indicates whether the passenger has a bicycle. | | +| `hasDog` | *boolean* | :heavy_minus_sign: | Indicates whether the passenger has a dog. | | +| `id` | *string* | :heavy_minus_sign: | Unique identifier for the booking. | 1725ff48-ab45-4bb5-9d02-88745177dedb | +| `passengerName` | *string* | :heavy_check_mark: | Name of the passenger. | John Doe | +| `tripId` | *string* | :heavy_check_mark: | Identifier of the booked trip. | ea399ba1-6d95-433f-92d1-83f67b775594 | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/speakeasy-delete-booking-response.md b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-delete-booking-response.md new file mode 100644 index 00000000..cb1a7a15 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-delete-booking-response.md @@ -0,0 +1,14 @@ +# SpeakeasyDeleteBookingResponse + +## Example Usage + +```typescript +import { SpeakeasyDeleteBookingResponse } from "openapi/models"; + +let value: SpeakeasyDeleteBookingResponse = {}; +``` + +## Fields + +| Field | Type | Required | Description | +| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/speakeasy-station.md b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-station.md new file mode 100644 index 00000000..99d3420b --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-station.md @@ -0,0 +1,27 @@ +# SpeakeasyStation + +A train station. + +## Example Usage + +```typescript +import { SpeakeasyStation } from "openapi/models"; + +let value: SpeakeasyStation = { + address: "Invalidenstrasse 10557 Berlin, Germany", + countryCode: "DE", + id: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + name: "Berlin Hauptbahnhof", + timezone: "Europe/Berlin", +}; +``` + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | +| `address` | *string* | :heavy_check_mark: | The address of the station. | Invalidenstrasse 10557 Berlin, Germany | +| `countryCode` | *string* | :heavy_check_mark: | The country code of the station. | DE | +| `id` | *string* | :heavy_check_mark: | Unique identifier for the station. | efdbb9d1-02c2-4bc3-afb7-6788d8782b1e | +| `name` | *string* | :heavy_check_mark: | The name of the station. | Berlin Hauptbahnhof | +| `timezone` | *string* | :heavy_minus_sign: | The timezone of the station in IANA format. | Europe/Berlin | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/models/speakeasy-trip.md b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-trip.md new file mode 100644 index 00000000..0a6ee489 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/models/speakeasy-trip.md @@ -0,0 +1,31 @@ +# SpeakeasyTrip + +A train trip between two stations. + +## Example Usage + +```typescript +import { SpeakeasyTrip } from "openapi/models"; + +let value: SpeakeasyTrip = { + destination: "b2e783e1-c824-4d63-b37a-d8d698862f1d", + id: "ea399ba1-6d95-433f-92d1-83f67b775594", + operator: "Deutsche Bahn", + origin: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + price: 50, +}; +``` + +## Fields + +| Field | Type | Required | Description | Example | +| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | +| `arrivalTime` | [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) | :heavy_minus_sign: | The arrival time in ISO 8601 format. | 2024-02-01T16:00:00Z | +| `bicyclesAllowed` | *boolean* | :heavy_minus_sign: | Indicates whether bicycles are allowed on the trip. | | +| `departureTime` | [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) | :heavy_minus_sign: | The departure time in ISO 8601 format. | 2024-02-01T10:00:00Z | +| `destination` | *string* | :heavy_minus_sign: | The ID of the destination station. | b2e783e1-c824-4d63-b37a-d8d698862f1d | +| `dogsAllowed` | *boolean* | :heavy_minus_sign: | Indicates whether dogs are allowed on the trip. | | +| `id` | *string* | :heavy_minus_sign: | Unique identifier for the trip. | ea399ba1-6d95-433f-92d1-83f67b775594 | +| `operator` | *string* | :heavy_minus_sign: | The name of the operator of the trip. | Deutsche Bahn | +| `origin` | *string* | :heavy_minus_sign: | The ID of the origin station. | efdbb9d1-02c2-4bc3-afb7-6788d8782b1e | +| `price` | *number* | :heavy_minus_sign: | The cost of the trip. | 50 | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/sdks/bookings/README.md b/frameworks-grpc-gateway/sdk/docs/sdks/bookings/README.md new file mode 100644 index 00000000..d5b30ba8 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/sdks/bookings/README.md @@ -0,0 +1,288 @@ +# Bookings + +## Overview + +Create and manage bookings for train trips, including passenger details and optional extras. + +### Available Operations + +* [getBookings](#getbookings) - List existing bookings +* [createBooking](#createbooking) - Create a booking +* [deleteBooking](#deletebooking) - Delete a booking +* [getBooking](#getbooking) - Get a booking + +## getBookings + +Returns a list of all trip bookings by the authenticated user. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { bookingsGetBookings } from "openapi/funcs/bookings-get-bookings.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await bookingsGetBookings(sdk); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("bookingsGetBookings failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [operations.GetBookingsRequest](../../models/operations/get-bookings-request.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.GetBookingsResponse](../../models/operations/get-bookings-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | + +## createBooking + +A booking is a temporary hold on a trip. It is not confirmed until the payment is processed. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.createBooking({ + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", + }); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { bookingsCreateBooking } from "openapi/funcs/bookings-create-booking.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await bookingsCreateBooking(sdk, { + id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + passengerName: "John Doe", + tripId: "ea399ba1-6d95-433f-92d1-83f67b775594", + }); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("bookingsCreateBooking failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [models.SpeakeasyBooking](../../models/speakeasy-booking.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.CreateBookingResponse](../../models/operations/create-booking-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | + +## deleteBooking + +Deletes a booking, cancelling the hold on the trip. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.deleteBooking({ + bookingId: "48d54bad-a4c8-4f4b-93f8-4b87d6bb9985", + }); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { bookingsDeleteBooking } from "openapi/funcs/bookings-delete-booking.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await bookingsDeleteBooking(sdk, { + bookingId: "48d54bad-a4c8-4f4b-93f8-4b87d6bb9985", + }); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("bookingsDeleteBooking failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [operations.DeleteBookingRequest](../../models/operations/delete-booking-request.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.DeleteBookingResponse](../../models/operations/delete-booking-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | + +## getBooking + +Returns the details of a specific booking. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.bookings.getBooking({ + bookingId: "2d020851-136a-4f7c-8968-d8dff0315c29", + }); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { bookingsGetBooking } from "openapi/funcs/bookings-get-booking.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await bookingsGetBooking(sdk, { + bookingId: "2d020851-136a-4f7c-8968-d8dff0315c29", + }); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("bookingsGetBooking failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [operations.GetBookingRequest](../../models/operations/get-booking-request.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.GetBookingResponse](../../models/operations/get-booking-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/sdks/stations/README.md b/frameworks-grpc-gateway/sdk/docs/sdks/stations/README.md new file mode 100644 index 00000000..572b1ab5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/sdks/stations/README.md @@ -0,0 +1,74 @@ +# Stations + +## Overview + +Find and filter train stations across Europe, including their location and local timezone. + +### Available Operations + +* [getStations](#getstations) - Get a list of train stations + +## getStations + +Returns a paginated and searchable list of all train stations. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.stations.getStations(); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { stationsGetStations } from "openapi/funcs/stations-get-stations.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await stationsGetStations(sdk); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("stationsGetStations failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [operations.GetStationsRequest](../../models/operations/get-stations-request.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.GetStationsResponse](../../models/operations/get-stations-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/docs/sdks/trips/README.md b/frameworks-grpc-gateway/sdk/docs/sdks/trips/README.md new file mode 100644 index 00000000..1502efac --- /dev/null +++ b/frameworks-grpc-gateway/sdk/docs/sdks/trips/README.md @@ -0,0 +1,82 @@ +# Trips + +## Overview + +Timetables and routes for train trips between stations, including pricing and availability. + +### Available Operations + +* [getTrips](#gettrips) - Get available train trips + +## getTrips + +Returns a list of available train trips between the specified origin and destination stations on the given date. + +### Example Usage + + +```typescript +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function run() { + const result = await sdk.trips.getTrips({ + origin: "4c3b4398-6f36-47bf-b258-344f651bc875", + destination: "eccd3bf6-7737-47cd-b359-0596cf854de4", + date: new Date("2026-07-10T20:57:33.258Z"), + }); + + console.log(result); +} + +run(); +``` + +### Standalone function + +The standalone function version of this method: + +```typescript +import { SDKCore } from "openapi/core.js"; +import { tripsGetTrips } from "openapi/funcs/trips-get-trips.js"; + +// Use `SDKCore` for best tree-shaking performance. +// You can create one instance of it to use across an application. +const sdk = new SDKCore(); + +async function run() { + const res = await tripsGetTrips(sdk, { + origin: "4c3b4398-6f36-47bf-b258-344f651bc875", + destination: "eccd3bf6-7737-47cd-b359-0596cf854de4", + date: new Date("2026-07-10T20:57:33.258Z"), + }); + if (res.ok) { + const { value: result } = res; + console.log(result); + } else { + console.log("tripsGetTrips failed:", res.error); + } +} + +run(); +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | [operations.GetTripsRequest](../../models/operations/get-trips-request.md) | :heavy_check_mark: | The request object to use for the request. | +| `options` | RequestOptions | :heavy_minus_sign: | Used to set various options for making HTTP requests. | +| `options.fetchOptions` | [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options) | :heavy_minus_sign: | Options that are passed to the underlying HTTP request. This can be used to inject extra headers for examples. All `Request` options, except `method` and `body`, are allowed. | +| `options.retries` | [RetryConfig](../../lib/utils/retryconfig.md) | :heavy_minus_sign: | Enables retrying HTTP requests under certain failure conditions. | + +### Response + +**Promise\<[operations.GetTripsResponse](../../models/operations/get-trips-response.md)\>** + +### Errors + +| Error Type | Status Code | Content Type | +| ---------------------- | ---------------------- | ---------------------- | +| errors.SDKDefaultError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/examples/.env.template b/frameworks-grpc-gateway/sdk/examples/.env.template new file mode 100644 index 00000000..3f6a0be3 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/examples/.env.template @@ -0,0 +1,3 @@ +# openapi SDK Environment Variables +# Copy this file to .env and fill in your actual values +# DO NOT commit the .env file to version control diff --git a/frameworks-grpc-gateway/sdk/examples/README.md b/frameworks-grpc-gateway/sdk/examples/README.md new file mode 100644 index 00000000..9ed75dd9 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/examples/README.md @@ -0,0 +1,31 @@ +# openapi Examples + +This directory contains example scripts demonstrating how to use the openapi SDK. + +## Prerequisites + +- Node.js (v18 or higher) +- npm + +## Setup + +1. Copy `.env.template` to `.env`: + ```bash + cp .env.template .env + ``` + +2. Edit `.env` and add your actual credentials + +## Running the Examples + +To run an example file from the examples directory: + +```bash +npm run build && npx tsx example.ts +``` + +## Creating new examples + +Duplicate an existing example file, they won't be overwritten by the generation process. + + diff --git a/frameworks-grpc-gateway/sdk/examples/bookingsGetBookings.example.ts b/frameworks-grpc-gateway/sdk/examples/bookingsGetBookings.example.ts new file mode 100644 index 00000000..b1cc6b6f --- /dev/null +++ b/frameworks-grpc-gateway/sdk/examples/bookingsGetBookings.example.ts @@ -0,0 +1,24 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import dotenv from "dotenv"; +dotenv.config(); +/** + * Example usage of the openapi SDK + * + * To run this example from the examples directory: + * npm run build && npx tsx bookingsGetBookings.example.ts + */ + +import { SDK } from "openapi"; + +const sdk = new SDK(); + +async function main() { + const result = await sdk.bookings.getBookings(); + + console.log(result); +} + +main().catch(console.error); diff --git a/frameworks-grpc-gateway/sdk/examples/package.json b/frameworks-grpc-gateway/sdk/examples/package.json new file mode 100644 index 00000000..df551dc7 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/examples/package.json @@ -0,0 +1,18 @@ +{ + "name": "openapi-examples", + "version": "1.0.0", + "private": true, + "scripts": { + "build:parent": "cd .. && npm i && npm run build && cd -", + "build:examples": "npm i", + "build": "npm run build:parent && npm run build:examples" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "dotenv": "^16.4.5", + "tsx": "^4.19.2" + }, + "dependencies": { + "openapi": "file:.." + } +} \ No newline at end of file diff --git a/frameworks-grpc-gateway/sdk/jsr.json b/frameworks-grpc-gateway/sdk/jsr.json new file mode 100644 index 00000000..80dc5af4 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/jsr.json @@ -0,0 +1,27 @@ + + +{ + "name": "openapi", + "version": "0.0.1", + "exports": { + ".": "./src/index.ts", + "./models/errors": "./src/models/errors/index.ts", + "./models": "./src/models/index.ts", + "./models/operations": "./src/models/operations/index.ts", + "./lib/config": "./src/lib/config.ts", + "./lib/http": "./src/lib/http.ts", + "./lib/retries": "./src/lib/retries.ts", + "./lib/sdks": "./src/lib/sdks.ts", + "./types": "./src/types/index.ts" + }, + "publish": { + "include": [ + "LICENSE", + "README.md", + "RUNTIMES.md", + "USAGE.md", + "jsr.json", + "src/**/*.ts" + ] + } +} diff --git a/frameworks-grpc-gateway/sdk/package.json b/frameworks-grpc-gateway/sdk/package.json new file mode 100644 index 00000000..abdcf8be --- /dev/null +++ b/frameworks-grpc-gateway/sdk/package.json @@ -0,0 +1,62 @@ +{ + "name": "openapi", + "version": "0.0.1", + "author": "Speakeasy", + "type": "module", + "main": "./esm/index.js", + "exports": { + ".": { + "source": "./src/index.ts", + "types": "./esm/index.d.ts", + "default": "./esm/index.js" + }, + "./package.json": "./package.json", + "./types": { + "source": "./src/types/index.ts", + "types": "./esm/types/index.d.ts", + "default": "./esm/types/index.js" + }, + "./models/errors": { + "source": "./src/models/errors/index.ts", + "types": "./esm/models/errors/index.d.ts", + "default": "./esm/models/errors/index.js" + }, + "./models": { + "source": "./src/models/index.ts", + "types": "./esm/models/index.d.ts", + "default": "./esm/models/index.js" + }, + "./models/operations": { + "source": "./src/models/operations/index.ts", + "types": "./esm/models/operations/index.d.ts", + "default": "./esm/models/operations/index.js" + }, + "./*.js": { + "source": "./src/*.ts", + "types": "./esm/*.d.ts", + "default": "./esm/*.js" + }, + "./*": { + "source": "./src/*.ts", + "types": "./esm/*.d.ts", + "default": "./esm/*.js" + } + }, + "sideEffects": false, + "scripts": { + "lint": "oxlint --max-warnings=0 --deny-warnings src/**/*.ts src/**/*.tsx", + "build": "tsgo", + "prepublishOnly": "npm run build" + }, + "peerDependencies": { + + }, + "devDependencies": { + "@typescript/native-preview": "7.0.0-dev.20260302.1", + "oxlint": "^1.60.0", + "typescript": "~5.8.3" + }, + "dependencies": { + "zod": "^3.25.65 || ^4.0.0" + } +} diff --git a/frameworks-grpc-gateway/sdk/src/core.ts b/frameworks-grpc-gateway/sdk/src/core.ts new file mode 100644 index 00000000..c4486e4a --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/core.ts @@ -0,0 +1,13 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { ClientSDK } from "./lib/sdks.js"; + +/** + * A minimal client to use when calling standalone SDK functions. Typically, an + * instance of this class would be instantiated once at the start of an + * application and passed around through some dependency injection mechanism to + * parts of an application that need to make SDK calls. + */ +export class SDKCore extends ClientSDK {} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/bookings-create-booking.ts b/frameworks-grpc-gateway/sdk/src/funcs/bookings-create-booking.ts new file mode 100644 index 00000000..c41c6fc5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/bookings-create-booking.ts @@ -0,0 +1,169 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeJSON } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as models from "../models/index.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * Create a booking + * + * @remarks + * A booking is a temporary hold on a trip. It is not confirmed until the payment is processed. + */ +export function bookingsCreateBooking( + client: SDKCore, + request: models.SpeakeasyBooking, + options?: RequestOptions, +): APIPromise< + Result< + operations.CreateBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request: models.SpeakeasyBooking, + options?: RequestOptions, +): Promise< + [ + Result< + operations.CreateBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => z.parse(models.SpeakeasyBooking$outboundSchema, value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = encodeJSON("body", payload, { explode: true }); + + const path = pathToFunc("/v1/bookings")(); + + const headers = new Headers(compactMap({ + "Content-Type": "application/json", + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "create-booking", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "POST", + baseURL: options?.serverURL, + path: path, + headers: headers, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.CreateBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.CreateBookingResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.CreateBookingResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/bookings-delete-booking.ts b/frameworks-grpc-gateway/sdk/src/funcs/bookings-delete-booking.ts new file mode 100644 index 00000000..3e4d6108 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/bookings-delete-booking.ts @@ -0,0 +1,173 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeSimple } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * Delete a booking + * + * @remarks + * Deletes a booking, cancelling the hold on the trip. + */ +export function bookingsDeleteBooking( + client: SDKCore, + request: operations.DeleteBookingRequest, + options?: RequestOptions, +): APIPromise< + Result< + operations.DeleteBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request: operations.DeleteBookingRequest, + options?: RequestOptions, +): Promise< + [ + Result< + operations.DeleteBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => z.parse(operations.DeleteBookingRequest$outboundSchema, value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = null; + + const pathParams = { + bookingId: encodeSimple("bookingId", payload.bookingId, { + explode: false, + charEncoding: "percent", + }), + }; + const path = pathToFunc("/v1/bookings/{bookingId}")(pathParams); + + const headers = new Headers(compactMap({ + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "delete-booking", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "DELETE", + baseURL: options?.serverURL, + path: path, + headers: headers, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.DeleteBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.DeleteBookingResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.DeleteBookingResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-booking.ts b/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-booking.ts new file mode 100644 index 00000000..4a8b6939 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-booking.ts @@ -0,0 +1,173 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeSimple } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * Get a booking + * + * @remarks + * Returns the details of a specific booking. + */ +export function bookingsGetBooking( + client: SDKCore, + request: operations.GetBookingRequest, + options?: RequestOptions, +): APIPromise< + Result< + operations.GetBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request: operations.GetBookingRequest, + options?: RequestOptions, +): Promise< + [ + Result< + operations.GetBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => z.parse(operations.GetBookingRequest$outboundSchema, value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = null; + + const pathParams = { + bookingId: encodeSimple("bookingId", payload.bookingId, { + explode: false, + charEncoding: "percent", + }), + }; + const path = pathToFunc("/v1/bookings/{bookingId}")(pathParams); + + const headers = new Headers(compactMap({ + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "get-booking", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "GET", + baseURL: options?.serverURL, + path: path, + headers: headers, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.GetBookingResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.GetBookingResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.GetBookingResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-bookings.ts b/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-bookings.ts new file mode 100644 index 00000000..b16c8ac7 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/bookings-get-bookings.ts @@ -0,0 +1,173 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeFormQuery } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * List existing bookings + * + * @remarks + * Returns a list of all trip bookings by the authenticated user. + */ +export function bookingsGetBookings( + client: SDKCore, + request?: operations.GetBookingsRequest | undefined, + options?: RequestOptions, +): APIPromise< + Result< + operations.GetBookingsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request?: operations.GetBookingsRequest | undefined, + options?: RequestOptions, +): Promise< + [ + Result< + operations.GetBookingsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => + z.parse(z.optional(operations.GetBookingsRequest$outboundSchema), value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = null; + + const path = pathToFunc("/v1/bookings")(); + + const query = encodeFormQuery({ + "empty": payload?.empty, + }); + + const headers = new Headers(compactMap({ + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "get-bookings", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "GET", + baseURL: options?.serverURL, + path: path, + headers: headers, + query: query, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.GetBookingsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.GetBookingsResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.GetBookingsResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/stations-get-stations.ts b/frameworks-grpc-gateway/sdk/src/funcs/stations-get-stations.ts new file mode 100644 index 00000000..7571a1d1 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/stations-get-stations.ts @@ -0,0 +1,173 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeFormQuery } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * Get a list of train stations + * + * @remarks + * Returns a paginated and searchable list of all train stations. + */ +export function stationsGetStations( + client: SDKCore, + request?: operations.GetStationsRequest | undefined, + options?: RequestOptions, +): APIPromise< + Result< + operations.GetStationsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request?: operations.GetStationsRequest | undefined, + options?: RequestOptions, +): Promise< + [ + Result< + operations.GetStationsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => + z.parse(z.optional(operations.GetStationsRequest$outboundSchema), value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = null; + + const path = pathToFunc("/v1/stations")(); + + const query = encodeFormQuery({ + "empty": payload?.empty, + }); + + const headers = new Headers(compactMap({ + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "get-stations", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "GET", + baseURL: options?.serverURL, + path: path, + headers: headers, + query: query, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.GetStationsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.GetStationsResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.GetStationsResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/funcs/trips-get-trips.ts b/frameworks-grpc-gateway/sdk/src/funcs/trips-get-trips.ts new file mode 100644 index 00000000..69ddcf6d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/funcs/trips-get-trips.ts @@ -0,0 +1,176 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKCore } from "../core.js"; +import { encodeFormQuery } from "../lib/encodings.js"; +import { matchStatusCode } from "../lib/http.js"; +import * as M from "../lib/matchers.js"; +import { compactMap } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { RequestOptions } from "../lib/sdks.js"; +import { pathToFunc } from "../lib/url.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKError } from "../models/errors/sdk-error.js"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import * as operations from "../models/operations/index.js"; +import { APICall, APIPromise } from "../types/async.js"; +import { Result } from "../types/fp.js"; + +/** + * Get available train trips + * + * @remarks + * Returns a list of available train trips between the specified origin and destination stations on the given date. + */ +export function tripsGetTrips( + client: SDKCore, + request: operations.GetTripsRequest, + options?: RequestOptions, +): APIPromise< + Result< + operations.GetTripsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + > +> { + return new APIPromise($do( + client, + request, + options, + )); +} + +async function $do( + client: SDKCore, + request: operations.GetTripsRequest, + options?: RequestOptions, +): Promise< + [ + Result< + operations.GetTripsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >, + APICall, + ] +> { + const parsed = safeParse( + request, + (value) => z.parse(operations.GetTripsRequest$outboundSchema, value), + "Input validation failed", + ); + if (!parsed.ok) { + return [parsed, { status: "invalid" }]; + } + const payload = parsed.value; + const body = null; + + const path = pathToFunc("/v1/trips")(); + + const query = encodeFormQuery({ + "bicycles": payload.bicycles, + "date": payload.date, + "destination": payload.destination, + "dogs": payload.dogs, + "origin": payload.origin, + }); + + const headers = new Headers(compactMap({ + Accept: "application/json", + })); + + const context = { + options: client._options, + baseURL: options?.serverURL ?? client._baseURL ?? "", + operationID: "get-trips", + oAuth2Scopes: null, + + resolvedSecurity: null, + + securitySource: null, + retryConfig: options?.retries + || client._options.retryConfig + || { + strategy: "backoff", + backoff: { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, + }, + retryConnectionErrors: true, + } + || { strategy: "none" }, + retryCodes: options?.retryCodes || ["5XX"], + }; + + const requestRes = client._createRequest(context, { + method: "GET", + baseURL: options?.serverURL, + path: path, + headers: headers, + query: query, + body: body, + userAgent: client._options.userAgent, + timeoutMs: options?.timeoutMs || client._options.timeoutMs || -1, + }, options); + if (!requestRes.ok) { + return [requestRes, { status: "invalid" }]; + } + const req = requestRes.value; + + const doResult = await client._do(req, { + context, + isErrorStatusCode: (statusCode: number) => + matchStatusCode({ status: statusCode } as Response, ["4XX", "5XX"]), + retryConfig: context.retryConfig, + retryCodes: context.retryCodes, + }); + if (!doResult.ok) { + return [doResult, { status: "request-error", request: req }]; + } + const response = doResult.value; + + const [result] = await M.match< + operations.GetTripsResponse, + | SDKError + | ResponseValidationError + | ConnectionError + | RequestAbortedError + | RequestTimeoutError + | InvalidRequestError + | UnexpectedClientError + | SDKValidationError + >( + M.json(200, operations.GetTripsResponse$inboundSchema), + M.fail("4XX"), + M.fail("5XX"), + M.json("default", operations.GetTripsResponse$inboundSchema), + )(response, req); + if (!result.ok) { + return [result, { status: "complete", request: req, response }]; + } + + return [result, { status: "complete", request: req, response }]; +} diff --git a/frameworks-grpc-gateway/sdk/src/hooks/hooks.ts b/frameworks-grpc-gateway/sdk/src/hooks/hooks.ts new file mode 100644 index 00000000..94ad0a32 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/hooks/hooks.ts @@ -0,0 +1,129 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { SDKOptions } from "../lib/config.js"; +import { RequestInput } from "../lib/http.js"; +import { + AfterErrorContext, + AfterErrorHook, + AfterSuccessContext, + AfterSuccessHook, + BeforeCreateRequestContext, + BeforeCreateRequestHook, + BeforeRequestContext, + BeforeRequestHook, + Hook, + Hooks, + SDKInitHook, +} from "./types.js"; + +export class SDKHooks implements Hooks { + sdkInitHooks: SDKInitHook[] = []; + beforeCreateRequestHooks: BeforeCreateRequestHook[] = []; + beforeRequestHooks: BeforeRequestHook[] = []; + afterSuccessHooks: AfterSuccessHook[] = []; + afterErrorHooks: AfterErrorHook[] = []; + + constructor() { + const presetHooks: Array = []; + + for (const hook of presetHooks) { + if ("sdkInit" in hook) { + this.registerSDKInitHook(hook); + } + if ("beforeCreateRequest" in hook) { + this.registerBeforeCreateRequestHook(hook); + } + if ("beforeRequest" in hook) { + this.registerBeforeRequestHook(hook); + } + if ("afterSuccess" in hook) { + this.registerAfterSuccessHook(hook); + } + if ("afterError" in hook) { + this.registerAfterErrorHook(hook); + } + } + } + + registerSDKInitHook(hook: SDKInitHook) { + this.sdkInitHooks.push(hook); + } + + registerBeforeCreateRequestHook(hook: BeforeCreateRequestHook) { + this.beforeCreateRequestHooks.push(hook); + } + + registerBeforeRequestHook(hook: BeforeRequestHook) { + this.beforeRequestHooks.push(hook); + } + + registerAfterSuccessHook(hook: AfterSuccessHook) { + this.afterSuccessHooks.push(hook); + } + + registerAfterErrorHook(hook: AfterErrorHook) { + this.afterErrorHooks.push(hook); + } + + sdkInit(opts: SDKOptions): SDKOptions { + return this.sdkInitHooks.reduce((opts, hook) => hook.sdkInit(opts), opts); + } + + beforeCreateRequest( + hookCtx: BeforeCreateRequestContext, + input: RequestInput, + ): RequestInput { + let inp = input; + + for (const hook of this.beforeCreateRequestHooks) { + inp = hook.beforeCreateRequest(hookCtx, inp); + } + + return inp; + } + + async beforeRequest( + hookCtx: BeforeRequestContext, + request: Request, + ): Promise { + let req = request; + + for (const hook of this.beforeRequestHooks) { + req = await hook.beforeRequest(hookCtx, req); + } + + return req; + } + + async afterSuccess( + hookCtx: AfterSuccessContext, + response: Response, + ): Promise { + let res = response; + + for (const hook of this.afterSuccessHooks) { + res = await hook.afterSuccess(hookCtx, res); + } + + return res; + } + + async afterError( + hookCtx: AfterErrorContext, + response: Response | null, + error: unknown, + ): Promise<{ response: Response | null; error: unknown }> { + let res = response; + let err = error; + + for (const hook of this.afterErrorHooks) { + const result = await hook.afterError(hookCtx, res, err); + res = result.response; + err = result.error; + } + + return { response: res, error: err }; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/hooks/index.ts b/frameworks-grpc-gateway/sdk/src/hooks/index.ts new file mode 100644 index 00000000..f60ec7ac --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/hooks/index.ts @@ -0,0 +1,6 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./hooks.js"; +export * from "./types.js"; diff --git a/frameworks-grpc-gateway/sdk/src/hooks/types.ts b/frameworks-grpc-gateway/sdk/src/hooks/types.ts new file mode 100644 index 00000000..9c36bf01 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/hooks/types.ts @@ -0,0 +1,107 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { SDKOptions } from "../lib/config.js"; +import { RequestInput } from "../lib/http.js"; +import { RetryConfig } from "../lib/retries.js"; +import { SecurityState } from "../lib/security.js"; + +export type HookContext = { + baseURL: string | URL; + operationID: string; + oAuth2Scopes: string[] | null; + securitySource?: any | (() => Promise); + retryConfig: RetryConfig; + resolvedSecurity: SecurityState | null; + options: SDKOptions; +}; + +export type Awaitable = T | Promise; + +export type BeforeCreateRequestContext = HookContext & {}; +export type BeforeRequestContext = HookContext & {}; +export type AfterSuccessContext = HookContext & {}; +export type AfterErrorContext = HookContext & {}; + +/** + * SDKInitHook is called when the SDK is initializing. The + * hook can return a new baseURL and HTTP client to be used by the SDK. + */ +export interface SDKInitHook { + sdkInit: (opts: SDKOptions) => SDKOptions; +} + +export interface BeforeCreateRequestHook { + /** + * A hook that is called before the SDK creates a `Request` object. The hook + * can modify how a request is constructed since certain modifications, like + * changing the request URL, cannot be done on a request object directly. + */ + beforeCreateRequest: ( + hookCtx: BeforeCreateRequestContext, + input: RequestInput, + ) => RequestInput; +} + +export interface BeforeRequestHook { + /** + * A hook that is called before the SDK sends a request. The hook can + * introduce instrumentation code such as logging, tracing and metrics or + * replace the request before it is sent or throw an error to stop the + * request from being sent. + */ + beforeRequest: ( + hookCtx: BeforeRequestContext, + request: Request, + ) => Awaitable; +} + +export interface AfterSuccessHook { + /** + * A hook that is called after the SDK receives a response. The hook can + * introduce instrumentation code such as logging, tracing and metrics or + * modify the response before it is handled or throw an error to stop the + * response from being handled. + */ + afterSuccess: ( + hookCtx: AfterSuccessContext, + response: Response, + ) => Awaitable; +} + +export interface AfterErrorHook { + /** + * A hook that is called after the SDK encounters an error, or a + * non-successful response. The hook can introduce instrumentation code such + * as logging, tracing and metrics or modify the response or error values. + */ + afterError: ( + hookCtx: AfterErrorContext, + response: Response | null, + error: unknown, + ) => Awaitable<{ + response: Response | null; + error: unknown; + }>; +} + +export interface Hooks { + /** Registers a hook to be used by the SDK for initialization event. */ + registerSDKInitHook(hook: SDKInitHook): void; + /** Registers a hook to be used by the SDK for to modify `Request` construction. */ + registerBeforeCreateRequestHook(hook: BeforeCreateRequestHook): void; + /** Registers a hook to be used by the SDK for the before request event. */ + registerBeforeRequestHook(hook: BeforeRequestHook): void; + /** Registers a hook to be used by the SDK for the after success event. */ + registerAfterSuccessHook(hook: AfterSuccessHook): void; + /** Registers a hook to be used by the SDK for the after error event. */ + registerAfterErrorHook(hook: AfterErrorHook): void; +} + +export type Hook = + | SDKInitHook + | BeforeCreateRequestHook + | BeforeRequestHook + | AfterSuccessHook + | AfterErrorHook; diff --git a/frameworks-grpc-gateway/sdk/src/index.ts b/frameworks-grpc-gateway/sdk/src/index.ts new file mode 100644 index 00000000..dbcba164 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/index.ts @@ -0,0 +1,9 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./lib/config.js"; +export * as files from "./lib/files.js"; +export { HTTPClient } from "./lib/http.js"; +export type { Fetcher, HTTPClientOptions } from "./lib/http.js"; +export * from "./sdk/sdk.js"; diff --git a/frameworks-grpc-gateway/sdk/src/lib/base64.ts b/frameworks-grpc-gateway/sdk/src/lib/base64.ts new file mode 100644 index 00000000..44be0eae --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/base64.ts @@ -0,0 +1,39 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; + +export function bytesToBase64(u8arr: Uint8Array): string { + return btoa(String.fromCodePoint(...u8arr)); +} + +export function bytesFromBase64(encoded: string): Uint8Array { + return Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0)); +} + +export function stringToBytes(str: string): Uint8Array { + return new TextEncoder().encode(str); +} + +export function stringFromBytes(u8arr: Uint8Array): string { + return new TextDecoder().decode(u8arr); +} + +export function stringToBase64(str: string): string { + return bytesToBase64(stringToBytes(str)); +} + +export function stringFromBase64(b64str: string): string { + return stringFromBytes(bytesFromBase64(b64str)); +} + +export const zodOutbound = z.union([ + z.custom(x => x instanceof Uint8Array), + z.pipe(z.string(), z.transform(stringToBytes)), +]); + +export const zodInbound = z.union([ + z.custom(x => x instanceof Uint8Array), + z.pipe(z.string(), z.transform(bytesFromBase64)), +]); diff --git a/frameworks-grpc-gateway/sdk/src/lib/config.ts b/frameworks-grpc-gateway/sdk/src/lib/config.ts new file mode 100644 index 00000000..00fefbc1 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/config.ts @@ -0,0 +1,62 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { HTTPClient } from "./http.js"; +import { Logger } from "./logger.js"; +import { RetryConfig } from "./retries.js"; +import { Params, pathToFunc } from "./url.js"; + +/** + * Contains the list of servers available to the SDK + */ +export const ServerList = [ + "https://api.example.com/", +] as const; + +export type SDKOptions = { + httpClient?: HTTPClient; + /** + * Allows overriding the default server used by the SDK + */ + serverIdx?: number | undefined; + /** + * Allows overriding the default server URL used by the SDK + */ + serverURL?: string | undefined; + /** + * Allows overriding the default user agent used by the SDK + */ + userAgent?: string | undefined; + /** + * Allows overriding the default retry config used by the SDK + */ + retryConfig?: RetryConfig; + timeoutMs?: number; + debugLogger?: Logger; +}; + +export function serverURLFromOptions(options: SDKOptions): URL | null { + let serverURL = options.serverURL; + + const params: Params = {}; + + if (!serverURL) { + const serverIdx = options.serverIdx ?? 0; + if (serverIdx < 0 || serverIdx >= ServerList.length) { + throw new Error(`Invalid server index ${serverIdx}`); + } + serverURL = ServerList[serverIdx] || ""; + } + + const u = pathToFunc(serverURL)(params); + return new URL(u); +} + +export const SDK_METADATA = { + language: "typescript", + openapiDocVersion: "1.2.1", + sdkVersion: "0.0.1", + genVersion: "2.893.0", + userAgent: "speakeasy-sdk/typescript 0.0.1 2.893.0 1.2.1 openapi", +} as const; diff --git a/frameworks-grpc-gateway/sdk/src/lib/dlv.ts b/frameworks-grpc-gateway/sdk/src/lib/dlv.ts new file mode 100644 index 00000000..e81091f5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/dlv.ts @@ -0,0 +1,53 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/* +MIT License + +Copyright (c) 2024 Jason Miller (http://jasonformat.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** + * @param obj The object to walk + * @param key The key path to walk the object with + * @param def A default value to return if the result is undefined + * + * @example + * dlv(obj, "a.b.c.d") + * @example + * dlv(object, ["a", "b", "c", "d"]) + * @example + * dlv(object, "foo.bar.baz", "Hello, default value!") + */ +export function dlv( + obj: any, + key: string | string[], + def?: T, + p?: number, + undef?: never, +): T | undefined { + key = Array.isArray(key) ? key : key.split("."); + for (p = 0; p < key.length; p++) { + const k = key[p]; + obj = k != null && obj ? obj[k] : undef; + } + return obj === undef ? def : obj; +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/encodings.ts b/frameworks-grpc-gateway/sdk/src/lib/encodings.ts new file mode 100644 index 00000000..caeae546 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/encodings.ts @@ -0,0 +1,529 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { bytesToBase64 } from "./base64.js"; +import { isPlainObject } from "./is-plain-object.js"; + +export class EncodingError extends Error { + constructor(message: string) { + super(message); + this.name = "EncodingError"; + } +} + +export function encodeMatrix( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +): string | undefined { + let out = ""; + const pairs: [string, unknown][] = options?.explode + ? explode(key, value) + : [[key, value]]; + + if (pairs.every(([_, v]) => v == null)) { + return; + } + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + const encodeValue = (v: unknown) => encodeString(serializeValue(v)); + + pairs.forEach(([pk, pv]) => { + let tmp = ""; + let encValue: string | null | undefined = null; + + if (pv == null) { + return; + } else if (Array.isArray(pv)) { + encValue = mapDefined(pv, (v) => `${encodeValue(v)}`)?.join(","); + } else if (isPlainObject(pv)) { + const mapped = mapDefinedEntries(Object.entries(pv), ([k, v]) => { + return `,${encodeString(k)},${encodeValue(v)}`; + }); + encValue = mapped?.join("").slice(1); + } else { + encValue = `${encodeValue(pv)}`; + } + + if (encValue == null) { + return; + } + + const keyPrefix = encodeString(pk); + tmp = `${keyPrefix}=${encValue}`; + // trim trailing '=' if value was empty + if (tmp === `${keyPrefix}=`) { + tmp = tmp.slice(0, -1); + } + + // If we end up with the nothing then skip forward + if (!tmp) { + return; + } + + out += `;${tmp}`; + }); + + return out; +} + +export function encodeLabel( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +): string | undefined { + let out = ""; + const pairs: [string, unknown][] = options?.explode + ? explode(key, value) + : [[key, value]]; + + if (pairs.every(([_, v]) => v == null)) { + return; + } + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + const encodeValue = (v: unknown) => encodeString(serializeValue(v)); + + pairs.forEach(([pk, pv]) => { + let encValue: string | null | undefined = ""; + + if (pv == null) { + return; + } else if (Array.isArray(pv)) { + encValue = mapDefined(pv, (v) => `${encodeValue(v)}`)?.join("."); + } else if (isPlainObject(pv)) { + const mapped = mapDefinedEntries(Object.entries(pv), ([k, v]) => { + return `.${encodeString(k)}.${encodeValue(v)}`; + }); + encValue = mapped?.join("").slice(1); + } else { + const k = options?.explode && isPlainObject(value) + ? `${encodeString(pk)}=` + : ""; + encValue = `${k}${encodeValue(pv)}`; + } + + out += encValue == null ? "" : `.${encValue}`; + }); + + return out; +} + +type FormEncoder = ( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +) => string | undefined; + +function formEncoder(sep: string): FormEncoder { + return ( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, + ) => { + let out = ""; + const pairs: [string, unknown][] = options?.explode + ? explode(key, value) + : [[key, value]]; + + if (pairs.every(([_, v]) => v == null)) { + return; + } + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + + const encodeValue = (v: unknown) => encodeString(serializeValue(v)); + + const encodedSep = encodeString(sep); + + pairs.forEach(([pk, pv]) => { + let tmp = ""; + let encValue: string | null | undefined = null; + + if (pv == null) { + return; + } else if (Array.isArray(pv)) { + encValue = mapDefined(pv, (v) => `${encodeValue(v)}`)?.join(encodedSep); + } else if (isPlainObject(pv)) { + encValue = mapDefinedEntries(Object.entries(pv), ([k, v]) => { + return `${encodeString(k)}${encodedSep}${encodeValue(v)}`; + })?.join(encodedSep); + } else { + encValue = `${encodeValue(pv)}`; + } + + if (encValue == null) { + return; + } + + tmp = `${encodeString(pk)}=${encValue}`; + + // If we end up with the nothing then skip forward + if (!tmp || tmp === "=") { + return; + } + + out += `&${tmp}`; + }); + + return out.slice(1); + }; +} + +export const encodeForm = formEncoder(","); +export const encodeSpaceDelimited = formEncoder(" "); +export const encodePipeDelimited = formEncoder("|"); + +export function encodeBodyForm( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +): string { + let out = ""; + const pairs: [string, unknown][] = options?.explode + ? explode(key, value) + : [[key, value]]; + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + + const encodeValue = (v: unknown) => encodeString(serializeValue(v)); + + pairs.forEach(([pk, pv]) => { + let tmp = ""; + let encValue = ""; + + if (pv == null) { + return; + } else if (Array.isArray(pv)) { + encValue = JSON.stringify(pv, jsonReplacer); + } else if (isPlainObject(pv)) { + encValue = JSON.stringify(pv, jsonReplacer); + } else { + encValue = `${encodeValue(pv)}`; + } + + tmp = `${encodeString(pk)}=${encValue}`; + + // If we end up with the nothing then skip forward + if (!tmp || tmp === "=") { + return; + } + + out += `&${tmp}`; + }); + + return out.slice(1); +} + +export function encodeDeepObject( + key: string, + value: unknown, + options?: { charEncoding?: "percent" | "none" }, +): string | undefined { + if (value == null) { + return; + } + + if (!isPlainObject(value)) { + throw new EncodingError( + `Value of parameter '${key}' which uses deepObject encoding must be an object or null`, + ); + } + + return encodeDeepObjectObject(key, value, options); +} + +export function encodeDeepObjectObject( + key: string, + value: unknown, + options?: { charEncoding?: "percent" | "none" }, +): string | undefined { + if (value == null) { + return; + } + + let out = ""; + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + + if (!isPlainObject(value)) { + throw new EncodingError(`Expected parameter '${key}' to be an object.`); + } + + Object.entries(value).forEach(([ck, cv]) => { + if (cv == null) { + return; + } + + const pk = `${key}[${ck}]`; + + if (isPlainObject(cv)) { + const objOut = encodeDeepObjectObject(pk, cv, options); + + out += objOut == null ? "" : `&${objOut}`; + + return; + } + + const pairs: unknown[] = Array.isArray(cv) ? cv : [cv]; + const encoded = mapDefined(pairs, (v) => { + return `${encodeString(pk)}=${encodeString(serializeValue(v))}`; + })?.join("&"); + + out += encoded == null ? "" : `&${encoded}`; + }); + + return out.slice(1); +} + +export function encodeJSON( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +): string | undefined { + if (typeof value === "undefined") { + return; + } + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + + const encVal = encodeString(JSON.stringify(value, jsonReplacer)); + + return options?.explode ? encVal : `${encodeString(key)}=${encVal}`; +} + +export const encodeSimple = ( + key: string, + value: unknown, + options?: { explode?: boolean; charEncoding?: "percent" | "none" }, +): string | undefined => { + let out = ""; + const pairs: [string, unknown][] = options?.explode + ? explode(key, value) + : [[key, value]]; + + if (pairs.every(([_, v]) => v == null)) { + return; + } + + const encodeString = (v: string) => { + return options?.charEncoding === "percent" ? encodeURIComponent(v) : v; + }; + const encodeValue = (v: unknown) => encodeString(serializeValue(v)); + + pairs.forEach(([pk, pv]) => { + let tmp: string | null | undefined = ""; + + if (pv == null) { + return; + } else if (Array.isArray(pv)) { + tmp = mapDefined(pv, (v) => `${encodeValue(v)}`)?.join(","); + } else if (isPlainObject(pv)) { + const mapped = mapDefinedEntries(Object.entries(pv), ([k, v]) => { + return `,${encodeString(k)},${encodeValue(v)}`; + }); + tmp = mapped?.join("").slice(1); + } else { + const k = options?.explode && isPlainObject(value) ? `${pk}=` : ""; + tmp = `${k}${encodeValue(pv)}`; + } + + out += tmp ? `,${tmp}` : ""; + }); + + return out.slice(1); +}; + +function explode(key: string, value: unknown): [string, unknown][] { + if (Array.isArray(value)) { + return value.map((v) => [key, v]); + } else if (isPlainObject(value)) { + const o = value ?? {}; + return Object.entries(o).map(([k, v]) => [k, v]); + } else { + return [[key, value]]; + } +} + +function serializeValue(value: unknown): string { + if (value == null) { + return ""; + } else if (value instanceof Date) { + return value.toISOString(); + } else if (value instanceof Uint8Array) { + return bytesToBase64(value); + } else if (typeof value === "object") { + return JSON.stringify(value, jsonReplacer); + } + + return `${value}`; +} + +function jsonReplacer(_: string, value: unknown): unknown { + if (value instanceof Uint8Array) { + return bytesToBase64(value); + } else { + return value; + } +} + +function mapDefined(inp: T[], mapper: (v: T) => R): R[] | null { + const res = inp.reduce((acc, v) => { + if (v == null) { + return acc; + } + + const m = mapper(v); + if (m == null) { + return acc; + } + + acc.push(m); + + return acc; + }, []); + + return res.length ? res : null; +} + +function mapDefinedEntries( + inp: Iterable<[K, V]>, + mapper: (v: [K, V]) => R, +): R[] | null { + const acc: R[] = []; + for (const [k, v] of inp) { + if (v == null) { + continue; + } + + const m = mapper([k, v]); + if (m == null) { + continue; + } + + acc.push(m); + } + + return acc.length ? acc : null; +} + +export function queryJoin(...args: (string | undefined)[]): string { + return args.filter(Boolean).join("&"); +} + +type QueryEncoderOptions = { + explode?: boolean; + charEncoding?: "percent" | "none"; + allowEmptyValue?: string[]; +}; + +type QueryEncoder = ( + key: string, + value: unknown, + options?: QueryEncoderOptions, +) => string | undefined; + +type BulkQueryEncoder = ( + values: Record, + options?: QueryEncoderOptions, +) => string; + +export function queryEncoder(f: QueryEncoder): BulkQueryEncoder { + const bulkEncode = function( + values: Record, + options?: QueryEncoderOptions, + ): string { + const opts: QueryEncoderOptions = { + ...options, + explode: options?.explode ?? true, + charEncoding: options?.charEncoding ?? "percent", + }; + + const allowEmptySet = new Set(options?.allowEmptyValue ?? []); + + const encoded = Object.entries(values).map(([key, value]) => { + if (allowEmptySet.has(key)) { + if ( + value === undefined + || value === null + || value === "" + || (Array.isArray(value) && value.length === 0) + ) { + return `${encodeURIComponent(key)}=`; + } + } + return f(key, value, opts); + }); + return queryJoin(...encoded); + }; + + return bulkEncode; +} + +export const encodeJSONQuery = queryEncoder(encodeJSON); +export const encodeFormQuery = queryEncoder(encodeForm); +export const encodeSpaceDelimitedQuery = queryEncoder(encodeSpaceDelimited); +export const encodePipeDelimitedQuery = queryEncoder(encodePipeDelimited); +export const encodeDeepObjectQuery = queryEncoder(encodeDeepObject); + +function isBlobLike(val: unknown): val is Blob { + if (val instanceof Blob) { + return true; + } + + if (typeof val !== "object" || val == null || !(Symbol.toStringTag in val)) { + return false; + } + + const tag = val[Symbol.toStringTag]; + if (tag !== "Blob" && tag !== "File") { + return false; + } + + return "stream" in val && typeof val.stream === "function"; +} + +export function appendForm( + fd: FormData, + key: string, + value: unknown, + fileName?: string, +): void { + if (value == null) { + return; + } else if (isBlobLike(value)) { + if (fileName) { + fd.append(key, value as Blob, fileName); + } else { + fd.append(key, value as Blob); + } + } else if (Array.isArray(value)) { + value.forEach((v) => { + appendForm(fd, key, v); + }); + } else { + fd.append(key, String(value)); + } +} + +export async function normalizeBlob( + value: Pick, +): Promise { + if (value instanceof Blob) { + return value; + } + return new Blob([await value.arrayBuffer()], { type: value.type }); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/files.ts b/frameworks-grpc-gateway/sdk/src/lib/files.ts new file mode 100644 index 00000000..6ca6b37d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/files.ts @@ -0,0 +1,104 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/** + * Consumes a stream and returns a concatenated array buffer. Useful in + * situations where we need to read the whole file because it forms part of a + * larger payload containing other fields, and we can't modify the underlying + * request structure. + */ +export async function readableStreamToArrayBuffer( + readable: ReadableStream, +): Promise { + const reader = readable.getReader(); + const chunks: Uint8Array[] = []; + + let totalLength = 0; + let done = false; + + while (!done) { + const { value, done: doneReading } = await reader.read(); + + if (doneReading) { + done = true; + } else { + chunks.push(value); + totalLength += value.length; + } + } + + const concatenatedChunks = new Uint8Array(totalLength); + let offset = 0; + + for (const chunk of chunks) { + concatenatedChunks.set(chunk, offset); + offset += chunk.length; + } + + return concatenatedChunks.buffer as ArrayBuffer; +} + +/** + * Determines the MIME content type based on a file's extension. + * Returns null if the extension is not recognized. + */ +export function getContentTypeFromFileName(fileName: string): string | null { + if (!fileName) return null; + + const ext = fileName.toLowerCase().split(".").pop(); + if (!ext) return null; + + const mimeTypes: Record = { + json: "application/json", + xml: "application/xml", + html: "text/html", + htm: "text/html", + txt: "text/plain", + csv: "text/csv", + pdf: "application/pdf", + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + gif: "image/gif", + svg: "image/svg+xml", + js: "application/javascript", + css: "text/css", + zip: "application/zip", + tar: "application/x-tar", + gz: "application/gzip", + mp4: "video/mp4", + mp3: "audio/mpeg", + wav: "audio/wav", + webp: "image/webp", + ico: "image/x-icon", + woff: "font/woff", + woff2: "font/woff2", + ttf: "font/ttf", + otf: "font/otf", + }; + + return mimeTypes[ext] || null; +} + +/** + * Creates a Blob from file content with the given MIME type. + * + * Node.js Buffers are Uint8Array subclasses that may share a pooled + * ArrayBuffer (byteOffset > 0, byteLength < buffer.byteLength). Passing + * such a Buffer directly to `new Blob([buf])` can include the entire + * underlying pool on some runtimes, producing a Blob with extra bytes + * that corrupts multipart uploads. + * + * Copying into a standalone Uint8Array ensures the Blob receives only the + * intended bytes regardless of runtime behaviour. + */ +export function bytesToBlob( + content: Uint8Array | ArrayBuffer | Blob | string, + contentType: string, +): Blob { + if (content instanceof Uint8Array) { + return new Blob([new Uint8Array(content)], { type: contentType }); + } + return new Blob([content as BlobPart], { type: contentType }); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/http.ts b/frameworks-grpc-gateway/sdk/src/lib/http.ts new file mode 100644 index 00000000..13cf1fd7 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/http.ts @@ -0,0 +1,323 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export type Fetcher = ( + input: RequestInfo | URL, + init?: RequestInit, +) => Promise; + +export type Awaitable = T | Promise; + +const DEFAULT_FETCHER: Fetcher = (input, init) => { + // If input is a Request and init is undefined, Bun will discard the method, + // headers, body and other options that were set on the request object. + // Node.js and browers would ignore an undefined init value. This check is + // therefore needed for interop with Bun. + if (init == null) { + return fetch(input); + } else { + return fetch(input, init); + } +}; + +export type RequestInput = { + /** + * The URL the request will use. + */ + url: URL; + /** + * Options used to create a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request). + */ + options?: RequestInit | undefined; +}; + +export interface HTTPClientOptions { + fetcher?: Fetcher; +} + +export type BeforeRequestHook = (req: Request) => Awaitable; +export type RequestErrorHook = (err: unknown, req: Request) => Awaitable; +export type ResponseHook = (res: Response, req: Request) => Awaitable; + +export class HTTPClient { + private fetcher: Fetcher; + private requestHooks: BeforeRequestHook[] = []; + private requestErrorHooks: RequestErrorHook[] = []; + private responseHooks: ResponseHook[] = []; + + constructor(private options: HTTPClientOptions = {}) { + this.fetcher = options.fetcher || DEFAULT_FETCHER; + } + + async request(request: Request): Promise { + let req = request; + for (const hook of this.requestHooks) { + const nextRequest = await hook(req); + if (nextRequest) { + req = nextRequest; + } + } + + try { + const res = await this.fetcher(req); + + for (const hook of this.responseHooks) { + await hook(res, req); + } + + return res; + } catch (err) { + for (const hook of this.requestErrorHooks) { + await hook(err, req); + } + + throw err; + } + } + + /** + * Registers a hook that is called before a request is made. The hook function + * can mutate the request or return a new request. This may be useful to add + * additional information to request such as request IDs and tracing headers. + */ + addHook(hook: "beforeRequest", fn: BeforeRequestHook): this; + /** + * Registers a hook that is called when a request cannot be made due to a + * network error. + */ + addHook(hook: "requestError", fn: RequestErrorHook): this; + /** + * Registers a hook that is called when a response has been received from the + * server. + */ + addHook(hook: "response", fn: ResponseHook): this; + addHook( + ...args: + | [hook: "beforeRequest", fn: BeforeRequestHook] + | [hook: "requestError", fn: RequestErrorHook] + | [hook: "response", fn: ResponseHook] + ) { + if (args[0] === "beforeRequest") { + this.requestHooks.push(args[1]); + } else if (args[0] === "requestError") { + this.requestErrorHooks.push(args[1]); + } else if (args[0] === "response") { + this.responseHooks.push(args[1]); + } else { + throw new Error(`Invalid hook type: ${args[0]}`); + } + return this; + } + + /** Removes a hook that was previously registered with `addHook`. */ + removeHook(hook: "beforeRequest", fn: BeforeRequestHook): this; + /** Removes a hook that was previously registered with `addHook`. */ + removeHook(hook: "requestError", fn: RequestErrorHook): this; + /** Removes a hook that was previously registered with `addHook`. */ + removeHook(hook: "response", fn: ResponseHook): this; + removeHook( + ...args: + | [hook: "beforeRequest", fn: BeforeRequestHook] + | [hook: "requestError", fn: RequestErrorHook] + | [hook: "response", fn: ResponseHook] + ): this { + let target: unknown[]; + if (args[0] === "beforeRequest") { + target = this.requestHooks; + } else if (args[0] === "requestError") { + target = this.requestErrorHooks; + } else if (args[0] === "response") { + target = this.responseHooks; + } else { + throw new Error(`Invalid hook type: ${args[0]}`); + } + + const index = target.findIndex((v) => v === args[1]); + if (index >= 0) { + target.splice(index, 1); + } + + return this; + } + + clone(): HTTPClient { + const child = new HTTPClient(this.options); + child.requestHooks = this.requestHooks.slice(); + child.requestErrorHooks = this.requestErrorHooks.slice(); + child.responseHooks = this.responseHooks.slice(); + + return child; + } +} + +export type StatusCodePredicate = number | string | (number | string)[]; + +// A semicolon surrounded by optional whitespace characters is used to separate +// segments in a media type string. +const mediaParamSeparator = /\s*;\s*/g; + +export function matchContentType(response: Response, pattern: string): boolean { + // `*` is a special case which means anything is acceptable. + if (pattern === "*") { + return true; + } + + let contentType = + response.headers.get("content-type")?.trim() || "application/octet-stream"; + contentType = contentType.toLowerCase(); + + const wantParts = pattern.toLowerCase().trim().split(mediaParamSeparator); + const [wantType = "", ...wantParams] = wantParts; + + if (wantType.split("/").length !== 2) { + return false; + } + + const gotParts = contentType.split(mediaParamSeparator); + const [gotType = "", ...gotParams] = gotParts; + + const [type = "", subtype = ""] = gotType.split("/"); + if (!type || !subtype) { + return false; + } + + if ( + wantType !== "*/*" && + gotType !== wantType && + `${type}/*` !== wantType && + `*/${subtype}` !== wantType + ) { + return false; + } + + if (gotParams.length < wantParams.length) { + return false; + } + + const params = new Set(gotParams); + for (const wantParam of wantParams) { + if (!params.has(wantParam)) { + return false; + } + } + + return true; +} + +const codeRangeRE = new RegExp("^[0-9]xx$", "i"); + +export function matchStatusCode( + response: Response, + codes: StatusCodePredicate, +): boolean { + const actual = `${response.status}`; + const expectedCodes = Array.isArray(codes) ? codes : [codes]; + if (!expectedCodes.length) { + return false; + } + + return expectedCodes.some((ec) => { + const code = `${ec}`; + + if (code === "default") { + return true; + } + + if (!codeRangeRE.test(`${code}`)) { + return code === actual; + } + + const expectFamily = code.charAt(0); + if (!expectFamily) { + throw new Error("Invalid status code range"); + } + + const actualFamily = actual.charAt(0); + if (!actualFamily) { + throw new Error(`Invalid response status code: ${actual}`); + } + + return actualFamily === expectFamily; + }); +} + +export function matchResponse( + response: Response, + code: StatusCodePredicate, + contentTypePattern: string, +): boolean { + return ( + matchStatusCode(response, code) && + matchContentType(response, contentTypePattern) + ); +} + +/** + * Uses various heurisitics to determine if an error is a connection error. + */ +export function isConnectionError(err: unknown): boolean { + if (typeof err !== "object" || err == null) { + return false; + } + + // Covers fetch in Deno as well + const isBrowserErr = + err instanceof TypeError && + err.message.toLowerCase().startsWith("failed to fetch"); + + const isNodeErr = + err instanceof TypeError && + err.message.toLowerCase().startsWith("fetch failed"); + + const isBunErr = "name" in err && err.name === "ConnectionError"; + + const isGenericErr = + "code" in err && + typeof err.code === "string" && + err.code.toLowerCase() === "econnreset"; + + return isBrowserErr || isNodeErr || isGenericErr || isBunErr; +} + +/** + * Uses various heurisitics to determine if an error is a timeout error. + */ +export function isTimeoutError(err: unknown): boolean { + if (typeof err !== "object" || err == null) { + return false; + } + + // Fetch in browser, Node.js, Bun, Deno + const isNative = "name" in err && err.name === "TimeoutError"; + const isLegacyNative = "code" in err && err.code === 23; + + // Node.js HTTP client and Axios + const isGenericErr = + "code" in err && + typeof err.code === "string" && + err.code.toLowerCase() === "econnaborted"; + + return isNative || isLegacyNative || isGenericErr; +} + +/** + * Uses various heurisitics to determine if an error is a abort error. + */ +export function isAbortError(err: unknown): boolean { + if (typeof err !== "object" || err == null) { + return false; + } + + // Fetch in browser, Node.js, Bun, Deno + const isNative = "name" in err && err.name === "AbortError"; + const isLegacyNative = "code" in err && err.code === 20; + + // Node.js HTTP client and Axios + const isGenericErr = + "code" in err && + typeof err.code === "string" && + err.code.toLowerCase() === "econnaborted"; + + return isNative || isLegacyNative || isGenericErr; +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/is-plain-object.ts b/frameworks-grpc-gateway/sdk/src/lib/is-plain-object.ts new file mode 100644 index 00000000..61070d3d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/is-plain-object.ts @@ -0,0 +1,43 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/* +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Taken from https://github.com/sindresorhus/is-plain-obj/blob/97f38e8836f86a642cce98fc6ab3058bc36df181/index.js + +export function isPlainObject(value: unknown): value is object { + if (typeof value !== "object" || value === null) { + return false; + } + + const prototype = Object.getPrototypeOf(value); + return ( + (prototype === null || + prototype === Object.prototype || + Object.getPrototypeOf(prototype) === null) && + !(Symbol.toStringTag in value) && + !(Symbol.iterator in value) + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/logger.ts b/frameworks-grpc-gateway/sdk/src/lib/logger.ts new file mode 100644 index 00000000..d181f293 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/logger.ts @@ -0,0 +1,9 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export interface Logger { + group(label?: string): void; + groupEnd(): void; + log(message: any, ...args: any[]): void; +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/matchers.ts b/frameworks-grpc-gateway/sdk/src/lib/matchers.ts new file mode 100644 index 00000000..29c742f1 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/matchers.ts @@ -0,0 +1,353 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { ResponseValidationError } from "../models/errors/response-validation-error.js"; +import { SDKDefaultError } from "../models/errors/sdk-default-error.js"; +import { ERR, OK, Result } from "../types/fp.js"; +import { matchResponse, matchStatusCode, StatusCodePredicate } from "./http.js"; +import { isPlainObject } from "./is-plain-object.js"; + +export type Encoding = + | "jsonl" + | "json" + | "text" + | "bytes" + | "stream" + | "sse" + | "nil" + | "fail"; + +const DEFAULT_CONTENT_TYPES: Record = { + jsonl: "application/jsonl", + json: "application/json", + text: "text/plain", + bytes: "application/octet-stream", + stream: "application/octet-stream", + sse: "text/event-stream", + nil: "*", + fail: "*", +}; + +type Schema = { parse(raw: unknown): T }; + +type MatchOptions = { + ctype?: string; + hdrs?: boolean; + key?: string; + sseSentinel?: string; +}; + +export type ValueMatcher = MatchOptions & { + enc: Encoding; + codes: StatusCodePredicate; + schema: Schema; +}; + +export type ErrorMatcher = MatchOptions & { + enc: Encoding; + codes: StatusCodePredicate; + schema: Schema; + err: true; +}; + +export type FailMatcher = { + enc: "fail"; + codes: StatusCodePredicate; +}; + +export type Matcher = ValueMatcher | ErrorMatcher | FailMatcher; + +export function jsonErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "json", codes, schema }; +} +export function json( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "json", codes, schema }; +} + +export function jsonl( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "jsonl", codes, schema }; +} + +export function jsonlErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "jsonl", codes, schema }; +} +export function textErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "text", codes, schema }; +} +export function text( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "text", codes, schema }; +} + +export function bytesErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "bytes", codes, schema }; +} +export function bytes( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "bytes", codes, schema }; +} + +export function streamErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "stream", codes, schema }; +} +export function stream( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "stream", codes, schema }; +} + +export function sseErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "sse", codes, schema }; +} +export function sse( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "sse", codes, schema }; +} + +export function nilErr( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ErrorMatcher { + return { ...options, err: true, enc: "nil", codes, schema }; +} +export function nil( + codes: StatusCodePredicate, + schema: Schema, + options?: MatchOptions, +): ValueMatcher { + return { ...options, enc: "nil", codes, schema }; +} + +export function fail(codes: StatusCodePredicate): FailMatcher { + return { enc: "fail", codes }; +} + +export type MatchedValue = Matchers extends Matcher[] + ? T + : never; +export type MatchedError = Matchers extends Matcher[] + ? E + : never; +export type MatchFunc = ( + response: Response, + request: Request, + options?: { resultKey?: string; extraFields?: Record }, +) => Promise<[result: Result, raw: unknown]>; + +export function match( + ...matchers: Array> +): MatchFunc { + return async function matchFunc( + response: Response, + request: Request, + options?: { resultKey?: string; extraFields?: Record }, + ): Promise< + [ + result: Result, + raw: unknown, + ] + > { + let raw: unknown; + let matcher: Matcher | undefined; + for (const match of matchers) { + const { codes } = match; + const ctpattern = "ctype" in match + ? match.ctype + : DEFAULT_CONTENT_TYPES[match.enc]; + if (ctpattern && matchResponse(response, codes, ctpattern)) { + matcher = match; + break; + } else if (!ctpattern && matchStatusCode(response, codes)) { + matcher = match; + break; + } + } + + if (!matcher) { + return [{ + ok: false, + error: new SDKDefaultError("Unexpected Status or Content-Type", { + response, + request, + body: await response.text().catch(() => ""), + }), + }, raw]; + } + + const encoding = matcher.enc; + let body = ""; + switch (encoding) { + case "json": + body = await response.text(); + raw = JSON.parse(body); + break; + case "jsonl": + raw = response.body; + break; + case "bytes": + raw = new Uint8Array(await response.arrayBuffer()); + break; + case "stream": + raw = response.body; + break; + case "text": + body = await response.text(); + raw = body; + break; + case "sse": + raw = response.body; + break; + case "nil": + body = await response.text(); + raw = undefined; + break; + case "fail": + body = await response.text(); + raw = body; + break; + default: + throw new Error( + `Unsupported response type: ${encoding satisfies never}`, + ); + } + + if (matcher.enc === "fail") { + return [{ + ok: false, + error: new SDKDefaultError("API error occurred", { + request, + response, + body, + }), + }, raw]; + } + + const resultKey = matcher.key || options?.resultKey; + let data: unknown; + + if ("err" in matcher) { + data = { + ...options?.extraFields, + ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), + ...(isPlainObject(raw) ? raw : null), + request$: request, + response$: response, + body$: body, + }; + } else if (resultKey) { + data = { + ...options?.extraFields, + ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), + [resultKey]: raw, + }; + } else if (matcher.hdrs) { + data = { + ...options?.extraFields, + ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), + ...(isPlainObject(raw) ? raw : null), + }; + } else { + data = raw; + } + + if ("err" in matcher) { + const result = safeParseResponse( + data, + (v: unknown) => matcher.schema.parse(v), + "Response validation failed", + { request, response, body }, + ); + return [result.ok ? { ok: false, error: result.value } : result, raw]; + } else { + return [ + safeParseResponse( + data, + (v: unknown) => matcher.schema.parse(v), + "Response validation failed", + { request, response, body }, + ), + raw, + ]; + } + }; +} + +const headerValRE = /, */; +/** + * Iterates over a Headers object and returns an object with all the header + * entries. Values are represented as an array to account for repeated headers. + */ +export function unpackHeaders(headers: Headers): Record { + const out: Record = {}; + + for (const [k, v] of headers.entries()) { + out[k] = v.split(headerValRE); + } + + return out; +} + +function safeParseResponse( + rawValue: Inp, + fn: (value: Inp) => Out, + errorMessage: string, + httpMeta: { response: Response; request: Request; body: string }, +): Result { + try { + return OK(fn(rawValue)); + } catch (err) { + return ERR( + new ResponseValidationError(errorMessage, { + cause: err, + rawValue, + rawMessage: errorMessage, + ...httpMeta, + }), + ); + } +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/primitives.ts b/frameworks-grpc-gateway/sdk/src/lib/primitives.ts new file mode 100644 index 00000000..d21f1dc4 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/primitives.ts @@ -0,0 +1,150 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +class InvariantError extends Error { + constructor(message: string) { + super(message); + this.name = "InvariantError"; + } +} + +export function invariant( + condition: unknown, + message: string, +): asserts condition { + if (!condition) { + throw new InvariantError(message); + } +} + +export type ExactPartial = { + [P in keyof T]?: T[P] | undefined; +}; + +export type Remap = { + [k in keyof Inp as Mapping[k] extends string /* if we have a string mapping for this key then use it */ + ? Mapping[k] + : Mapping[k] extends null /* if the mapping is to `null` then drop the key */ + ? never + : k /* otherwise keep the key as-is */]: Inp[k]; +}; + +/** + * Converts or omits an object's keys according to a mapping. + * + * @param inp An object whose keys will be remapped + * @param mappings A mapping of original keys to new keys. If a key is not present in the mapping, it will be left as is. If a key is mapped to `null`, it will be removed in the resulting object. + * @returns A new object with keys remapped or omitted according to the mappings + */ +export function remap< + Inp extends Record, + const Mapping extends { [k in keyof Inp]?: string | null }, +>(inp: Inp, mappings: Mapping): Remap { + let out: any = {}; + + if (!Object.keys(mappings).length) { + out = inp; + return out; + } + + for (const [k, v] of Object.entries(inp)) { + const j = mappings[k]; + if (j === null) { + continue; + } + out[j ?? k] = v; + } + + return out; +} + +export function combineSignals( + ...signals: Array +): AbortSignal | null { + const filtered: AbortSignal[] = []; + for (const signal of signals) { + if (signal) { + filtered.push(signal); + } + } + + switch (filtered.length) { + case 0: + case 1: + return filtered[0] || null; + default: + if ("any" in AbortSignal && typeof AbortSignal.any === "function") { + return AbortSignal.any(filtered); + } + return abortSignalAny(filtered); + } +} + +export function abortSignalAny(signals: AbortSignal[]): AbortSignal { + const controller = new AbortController(); + const result = controller.signal; + if (!signals.length) { + return controller.signal; + } + + if (signals.length === 1) { + return signals[0] || controller.signal; + } + + for (const signal of signals) { + if (signal.aborted) { + return signal; + } + } + + function abort(this: AbortSignal) { + controller.abort(this.reason); + clean(); + } + + const signalRefs: WeakRef[] = []; + function clean() { + for (const signalRef of signalRefs) { + const signal = signalRef.deref(); + if (signal) { + signal.removeEventListener("abort", abort); + } + } + } + + for (const signal of signals) { + signalRefs.push(new WeakRef(signal)); + signal.addEventListener("abort", abort); + } + + return result; +} + +export function compactMap( + values: Record, +): Record { + const out: Record = {}; + + for (const [k, v] of Object.entries(values)) { + if (typeof v !== "undefined") { + out[k] = v; + } + } + + return out; +} + +export function allRequired>( + v: V, +): + | { + [K in keyof V]: NonNullable; + } + | undefined { + if (Object.values(v).every((x) => x == null)) { + return void 0; + } + + return v as ReturnType>; +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/retries.ts b/frameworks-grpc-gateway/sdk/src/lib/retries.ts new file mode 100644 index 00000000..e3ce9aba --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/retries.ts @@ -0,0 +1,218 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { isConnectionError, isTimeoutError } from "./http.js"; + +export type BackoffStrategy = { + initialInterval: number; + maxInterval: number; + exponent: number; + maxElapsedTime: number; +}; + +const defaultBackoff: BackoffStrategy = { + initialInterval: 500, + maxInterval: 60000, + exponent: 1.5, + maxElapsedTime: 3600000, +}; + +export type RetryConfig = + | { strategy: "none" } + | { + strategy: "backoff"; + backoff?: BackoffStrategy; + retryConnectionErrors?: boolean; + }; + +/** + * PermanentError is an error that is not recoverable. Throwing this error will + * cause a retry loop to terminate. + */ +export class PermanentError extends Error { + /** The underlying cause of the error. */ + override readonly cause: unknown; + + constructor(message: string, options?: { cause?: unknown }) { + let msg = message; + if (options?.cause) { + msg += `: ${options.cause}`; + } + + super(msg, options); + this.name = "PermanentError"; + // In older runtimes, the cause field would not have been assigned through + // the super() call. + if (typeof this.cause === "undefined") { + this.cause = options?.cause; + } + + Object.setPrototypeOf(this, PermanentError.prototype); + } +} + +/** + * TemporaryError is an error is used to signal that an HTTP request can be + * retried as part of a retry loop. If retry attempts are exhausted and this + * error is thrown, the response will be returned to the caller. + */ +export class TemporaryError extends Error { + response: Response; + + constructor(message: string, response: Response) { + super(message); + this.response = response; + this.name = "TemporaryError"; + + Object.setPrototypeOf(this, TemporaryError.prototype); + } +} + +export async function retry( + fetchFn: () => Promise, + options: { + config: RetryConfig; + statusCodes: string[]; + }, +): Promise { + switch (options.config.strategy) { + case "backoff": + return retryBackoff( + wrapFetcher(fetchFn, { + statusCodes: options.statusCodes, + retryConnectionErrors: !!options.config.retryConnectionErrors, + }), + options.config.backoff ?? defaultBackoff, + ); + default: + return await fetchFn(); + } +} + +function wrapFetcher( + fn: () => Promise, + options: { + statusCodes: string[]; + retryConnectionErrors: boolean; + }, +): () => Promise { + return async () => { + try { + const res = await fn(); + if (isRetryableResponse(res, options.statusCodes)) { + throw new TemporaryError( + "Response failed with retryable status code", + res, + ); + } + + return res; + } catch (err: unknown) { + if (err instanceof TemporaryError) { + throw err; + } + + if ( + options.retryConnectionErrors && + (isTimeoutError(err) || isConnectionError(err)) + ) { + throw err; + } + + throw new PermanentError("Permanent error", { cause: err }); + } + }; +} + +const codeRangeRE = new RegExp("^[0-9]xx$", "i"); + +function isRetryableResponse(res: Response, statusCodes: string[]): boolean { + const actual = `${res.status}`; + + return statusCodes.some((code) => { + if (!codeRangeRE.test(code)) { + return code === actual; + } + + const expectFamily = code.charAt(0); + if (!expectFamily) { + throw new Error("Invalid status code range"); + } + + const actualFamily = actual.charAt(0); + if (!actualFamily) { + throw new Error(`Invalid response status code: ${actual}`); + } + + return actualFamily === expectFamily; + }); +} + +async function retryBackoff( + fn: () => Promise, + strategy: BackoffStrategy, +): Promise { + const { maxElapsedTime, initialInterval, exponent, maxInterval } = strategy; + + const start = Date.now(); + let x = 0; + + while (true) { + try { + const res = await fn(); + return res; + } catch (err: unknown) { + if (err instanceof PermanentError) { + throw err.cause; + } + const elapsed = Date.now() - start; + if (elapsed > maxElapsedTime) { + if (err instanceof TemporaryError) { + return err.response; + } + + throw err; + } + + let retryInterval = 0; + if (err instanceof TemporaryError) { + retryInterval = retryIntervalFromResponse(err.response); + } + + if (retryInterval <= 0) { + retryInterval = + initialInterval * Math.pow(x, exponent) + Math.random() * 1000; + } + + const d = Math.min(retryInterval, maxInterval); + + await delay(d); + x++; + } + } +} + +function retryIntervalFromResponse(res: Response): number { + const retryVal = res.headers.get("retry-after") || ""; + if (!retryVal) { + return 0; + } + + const parsedNumber = Number(retryVal); + if (Number.isInteger(parsedNumber)) { + return parsedNumber * 1000; + } + + const parsedDate = Date.parse(retryVal); + if (Number.isInteger(parsedDate)) { + const deltaMS = parsedDate - Date.now(); + return deltaMS > 0 ? Math.ceil(deltaMS) : 0; + } + + return 0; +} + +async function delay(delay: number): Promise { + return new Promise((resolve) => setTimeout(resolve, delay)); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/schemas.ts b/frameworks-grpc-gateway/sdk/src/lib/schemas.ts new file mode 100644 index 00000000..cf280491 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/schemas.ts @@ -0,0 +1,94 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { SDKValidationError } from "../models/errors/sdk-validation-error.js"; +import { ERR, OK, Result } from "../types/fp.js"; + +/** + * Utility function that executes some code which may throw a ZodError. It + * intercepts this error and converts it to an SDKValidationError so as to not + * leak Zod implementation details to user code. + */ +export function parse( + rawValue: Inp, + fn: (value: Inp) => Out, + errorMessage: string, +): Out { + try { + return fn(rawValue); + } catch (err) { + if (err instanceof z.core.$ZodError) { + throw new SDKValidationError(errorMessage, err, rawValue); + } + throw err; + } +} + +/** + * Utility function that executes some code which may result in a ZodError. It + * intercepts this error and converts it to an SDKValidationError so as to not + * leak Zod implementation details to user code. + */ +export function safeParse( + rawValue: Inp, + fn: (value: Inp) => Out, + errorMessage: string, +): Result { + try { + return OK(fn(rawValue)); + } catch (err) { + return ERR(new SDKValidationError(errorMessage, err, rawValue)); + } +} + +export function collectExtraKeys< + Shape extends z.core.$ZodShape, + Catchall extends z.ZodMiniType, + K extends string, + Optional extends boolean, +>( + obj: z.ZodMiniObject>, + extrasKey: K, + optional: Optional, +): z.ZodMiniPipe< + z.ZodMiniObject>, + z.ZodMiniTransform< + & z.output> + & (Optional extends false ? { + [k in K]: Record>; + } + : { + [k in K]?: Record> | undefined; + }), + z.output>> + > +> { + return z.pipe( + obj, + z.transform((val: any) => { + const extras: Record> = {}; + const { shape } = obj; + for (const [key] of Object.entries(val)) { + if (key in shape) { + continue; + } + + const v = val[key]; + if (typeof v === "undefined") { + continue; + } + + extras[key] = v; + delete val[key]; + } + + if (optional && Object.keys(extras).length === 0) { + return val; + } + + return { ...val, [extrasKey]: extras }; + }), + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/sdks.ts b/frameworks-grpc-gateway/sdk/src/lib/sdks.ts new file mode 100644 index 00000000..c6777f2c --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/sdks.ts @@ -0,0 +1,401 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { SDKHooks } from "../hooks/hooks.js"; +import { HookContext } from "../hooks/types.js"; +import { + ConnectionError, + InvalidRequestError, + RequestAbortedError, + RequestTimeoutError, + UnexpectedClientError, +} from "../models/errors/http-client-errors.js"; +import { ERR, OK, Result } from "../types/fp.js"; +import { stringToBase64 } from "./base64.js"; +import { SDK_METADATA, SDKOptions, serverURLFromOptions } from "./config.js"; +import { encodeForm } from "./encodings.js"; +import { + HTTPClient, + isAbortError, + isConnectionError, + isTimeoutError, + matchContentType, +} from "./http.js"; +import { Logger } from "./logger.js"; +import { retry, RetryConfig } from "./retries.js"; +import { SecurityState } from "./security.js"; + +export type RequestOptions = { + /** + * Sets a timeout, in milliseconds, on HTTP requests made by an SDK method. If + * `fetchOptions.signal` is set then it will take precedence over this option. + */ + timeoutMs?: number; + /** + * Set or override a retry policy on HTTP calls. + */ + retries?: RetryConfig; + /** + * Specifies the status codes which should be retried using the given retry policy. + */ + retryCodes?: string[]; + /** + * Overrides the base server URL that will be used by an operation. + */ + serverURL?: string | URL; + /** + * @deprecated `fetchOptions` has been flattened into `RequestOptions`. + * + * Sets various request options on the `fetch` call made by an SDK method. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#options|Request} + */ + fetchOptions?: Omit; +} & Omit; + +type RequestConfig = { + method: string; + path: string; + baseURL?: string | URL | undefined; + query?: string; + body?: RequestInit["body"]; + headers?: HeadersInit; + security?: SecurityState | null; + uaHeader?: string; + userAgent?: string | undefined; + timeoutMs?: number; +}; + +const gt: unknown = typeof globalThis === "undefined" ? null : globalThis; +const webWorkerLike = typeof gt === "object" + && gt != null + && "importScripts" in gt + && typeof gt["importScripts"] === "function"; +const isBrowserLike = webWorkerLike + || (typeof navigator !== "undefined" && "serviceWorker" in navigator) + || (typeof window === "object" && typeof window.document !== "undefined"); + +export class ClientSDK { + readonly #httpClient: HTTPClient; + readonly #hooks: SDKHooks; + readonly #logger?: Logger | undefined; + public readonly _baseURL: URL | null; + public readonly _options: SDKOptions & { hooks?: SDKHooks }; + + constructor(options: SDKOptions = {}) { + const opt = options as unknown; + if ( + typeof opt === "object" + && opt != null + && "hooks" in opt + && opt.hooks instanceof SDKHooks + ) { + this.#hooks = opt.hooks; + } else { + this.#hooks = new SDKHooks(); + } + const defaultHttpClient = new HTTPClient(); + options.httpClient = options.httpClient || defaultHttpClient; + options = this.#hooks.sdkInit(options); + + const url = serverURLFromOptions(options); + if (url) { + url.pathname = url.pathname.replace(/\/+$/, "") + "/"; + } + this._baseURL = url; + this.#httpClient = options.httpClient || defaultHttpClient; + + this._options = { ...options, hooks: this.#hooks }; + + this.#logger = this._options.debugLogger; + } + + public _createRequest( + context: HookContext, + conf: RequestConfig, + options?: RequestOptions, + ): Result { + const { method, path, query, headers: opHeaders, security } = conf; + + const base = conf.baseURL ?? this._baseURL; + if (!base) { + return ERR(new InvalidRequestError("No base URL provided for operation")); + } + const baseURL = new URL(base); + let reqURL: URL; + if (path) { + baseURL.pathname = baseURL.pathname.replace(/\/+$/, "") + "/"; + reqURL = new URL(path, baseURL); + } else { + reqURL = baseURL; + } + reqURL.hash = ""; + + let finalQuery = query || ""; + + const secQuery: string[] = []; + for (const [k, v] of Object.entries(security?.queryParams || {})) { + const q = encodeForm(k, v, { charEncoding: "percent" }); + if (typeof q !== "undefined") { + secQuery.push(q); + } + } + if (secQuery.length) { + finalQuery += `&${secQuery.join("&")}`; + } + + if (finalQuery) { + const q = finalQuery.startsWith("&") ? finalQuery.slice(1) : finalQuery; + reqURL.search = `?${q}`; + } + + const headers = new Headers(opHeaders); + + const username = security?.basic.username; + const password = security?.basic.password; + if (username != null || password != null) { + const encoded = stringToBase64( + [username || "", password || ""].join(":"), + ); + headers.set("Authorization", `Basic ${encoded}`); + } + + const securityHeaders = new Headers(security?.headers || {}); + for (const [k, v] of securityHeaders) { + headers.set(k, v); + } + + let cookie = headers.get("cookie") || ""; + for (const [k, v] of Object.entries(security?.cookies || {})) { + cookie += `; ${k}=${v}`; + } + cookie = cookie.startsWith("; ") ? cookie.slice(2) : cookie; + headers.set("cookie", cookie); + + const userHeaders = new Headers( + options?.headers ?? options?.fetchOptions?.headers, + ); + for (const [k, v] of userHeaders) { + headers.set(k, v); + } + + // Only set user agent header in non-browser-like environments since CORS + // policy disallows setting it in browsers e.g. Chrome throws an error. + if (!isBrowserLike) { + headers.set( + conf.uaHeader ?? "user-agent", + conf.userAgent ?? SDK_METADATA.userAgent, + ); + } + + const fetchOptions: Omit = { + ...options?.fetchOptions, + ...options, + }; + if (!fetchOptions?.signal && conf.timeoutMs && conf.timeoutMs > 0) { + const timeoutSignal = AbortSignal.timeout(conf.timeoutMs); + fetchOptions.signal = timeoutSignal; + } + + if (conf.body instanceof ReadableStream) { + Object.assign(fetchOptions, { duplex: "half" }); + } + + let input; + try { + input = this.#hooks.beforeCreateRequest(context, { + url: reqURL, + options: { + ...fetchOptions, + body: conf.body ?? null, + headers, + method, + }, + }); + } catch (err: unknown) { + return ERR( + new UnexpectedClientError("Create request hook failed to execute", { + cause: err, + }), + ); + } + + return OK(new Request(input.url, input.options)); + } + + public async _do( + request: Request, + options: { + context: HookContext; + isErrorStatusCode: (statusCode: number) => boolean; + retryConfig: RetryConfig; + retryCodes: string[]; + }, + ): Promise< + Result< + Response, + | RequestAbortedError + | RequestTimeoutError + | ConnectionError + | UnexpectedClientError + > + > { + const { context, isErrorStatusCode } = options; + + return retry( + async () => { + const req = await this.#hooks.beforeRequest(context, request.clone()); + await logRequest(this.#logger, req).catch((e) => + this.#logger?.log("Failed to log request:", e) + ); + + let response = await this.#httpClient.request(req); + + try { + if (isErrorStatusCode(response.status)) { + const result = await this.#hooks.afterError( + context, + response, + null, + ); + if (result.error) { + throw result.error; + } + response = result.response || response; + } else { + response = await this.#hooks.afterSuccess(context, response); + } + } finally { + await logResponse(this.#logger, response, req) + .catch(e => this.#logger?.log("Failed to log response:", e)); + } + + return response; + }, + { config: options.retryConfig, statusCodes: options.retryCodes }, + ).then( + (r) => OK(r), + (err) => { + switch (true) { + case isAbortError(err): + return ERR( + new RequestAbortedError("Request aborted by client", { + cause: err, + }), + ); + case isTimeoutError(err): + return ERR( + new RequestTimeoutError("Request timed out", { cause: err }), + ); + case isConnectionError(err): + return ERR( + new ConnectionError("Unable to make request", { cause: err }), + ); + default: + return ERR( + new UnexpectedClientError("Unexpected HTTP client error", { + cause: err, + }), + ); + } + }, + ); + } +} + +const jsonLikeContentTypeRE = /^(application|text)\/([^+]+\+)*json.*/; +const jsonlLikeContentTypeRE = + /^(application|text)\/([^+]+\+)*(jsonl|x-ndjson)\b.*/; +async function logRequest(logger: Logger | undefined, req: Request) { + if (!logger) { + return; + } + + const contentType = req.headers.get("content-type"); + const ct = contentType?.split(";")[0] || ""; + + logger.group(`> Request: ${req.method} ${req.url}`); + + logger.group("Headers:"); + for (const [k, v] of req.headers.entries()) { + logger.log(`${k}: ${v}`); + } + logger.groupEnd(); + + logger.group("Body:"); + switch (true) { + case jsonLikeContentTypeRE.test(ct): + logger.log(await req.clone().json()); + break; + case ct.startsWith("text/"): + logger.log(await req.clone().text()); + break; + case ct === "multipart/form-data": { + const body = await req.clone().formData(); + for (const [k, v] of body) { + const vlabel = v instanceof Blob ? "" : v; + logger.log(`${k}: ${vlabel}`); + } + break; + } + default: + logger.log(`<${contentType}>`); + break; + } + logger.groupEnd(); + + logger.groupEnd(); +} + +async function logResponse( + logger: Logger | undefined, + res: Response, + req: Request, +) { + if (!logger) { + return; + } + + const contentType = res.headers.get("content-type"); + const ct = contentType?.split(";")[0] || ""; + + logger.group(`< Response: ${req.method} ${req.url}`); + logger.log("Status Code:", res.status, res.statusText); + + logger.group("Headers:"); + for (const [k, v] of res.headers.entries()) { + logger.log(`${k}: ${v}`); + } + logger.groupEnd(); + + logger.group("Body:"); + switch (true) { + case matchContentType(res, "application/json") + || jsonLikeContentTypeRE.test(ct) && !jsonlLikeContentTypeRE.test(ct): + logger.log(await res.clone().json()); + break; + case matchContentType(res, "application/jsonl") + || jsonlLikeContentTypeRE.test(ct): + case matchContentType(res, "text/event-stream"): + logger.log(`<${contentType}>`); + break; + case matchContentType(res, "text/*"): + logger.log(await res.clone().text()); + break; + case matchContentType(res, "multipart/form-data"): { + const body = await res.clone().formData(); + for (const [k, v] of body) { + const vlabel = v instanceof Blob ? "" : v; + logger.log(`${k}: ${vlabel}`); + } + break; + } + default: + logger.log(`<${contentType}>`); + break; + } + logger.groupEnd(); + + logger.groupEnd(); +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/security.ts b/frameworks-grpc-gateway/sdk/src/lib/security.ts new file mode 100644 index 00000000..a9cfc4db --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/security.ts @@ -0,0 +1,236 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +type OAuth2PasswordFlow = { + username: string; + password: string; + clientID?: string | undefined; + clientSecret?: string | undefined; + tokenURL: string; +}; + +export enum SecurityErrorCode { + Incomplete = "incomplete", + UnrecognisedSecurityType = "unrecognized_security_type", +} + +export class SecurityError extends Error { + constructor( + public code: SecurityErrorCode, + message: string, + ) { + super(message); + this.name = "SecurityError"; + } + + static incomplete(): SecurityError { + return new SecurityError( + SecurityErrorCode.Incomplete, + "Security requirements not met in order to perform the operation", + ); + } + static unrecognizedType(type: string): SecurityError { + return new SecurityError( + SecurityErrorCode.UnrecognisedSecurityType, + `Unrecognised security type: ${type}`, + ); + } +} + +export type SecurityState = { + basic: { username?: string | undefined; password?: string | undefined }; + headers: Record; + queryParams: Record; + cookies: Record; + oauth2: ({ type: "password" } & OAuth2PasswordFlow) | { type: "none" }; +}; + +type SecurityInputBasic = { + type: "http:basic"; + value: + | { username?: string | undefined; password?: string | undefined } + | null + | undefined; +}; + +type SecurityInputBearer = { + type: "http:bearer"; + value: string | null | undefined; + fieldName: string; +}; + +type SecurityInputAPIKey = { + type: "apiKey:header" | "apiKey:query" | "apiKey:cookie"; + value: string | null | undefined; + fieldName: string; +}; + +type SecurityInputOIDC = { + type: "openIdConnect"; + value: string | null | undefined; + fieldName: string; +}; + +type SecurityInputOAuth2 = { + type: "oauth2"; + value: string | null | undefined; + fieldName: string; +}; + +type SecurityInputOAuth2ClientCredentials = { + type: "oauth2:client_credentials"; + value: + | { + clientID?: string | undefined; + clientSecret?: string | undefined; + } + | null + | string + | undefined; + fieldName?: string; +}; + +type SecurityInputOAuth2PasswordCredentials = { + type: "oauth2:password"; + value: + | string + | null + | undefined; + fieldName?: string; +}; + +type SecurityInputCustom = { + type: "http:custom"; + value: any | null | undefined; + fieldName?: string; +}; + +export type SecurityInput = + | SecurityInputBasic + | SecurityInputBearer + | SecurityInputAPIKey + | SecurityInputOAuth2 + | SecurityInputOAuth2ClientCredentials + | SecurityInputOAuth2PasswordCredentials + | SecurityInputOIDC + | SecurityInputCustom; + +export function resolveSecurity( + ...options: SecurityInput[][] +): SecurityState | null { + const state: SecurityState = { + basic: {}, + headers: {}, + queryParams: {}, + cookies: {}, + oauth2: { type: "none" }, + }; + + const option = options.find((opts) => { + return opts.every((o) => { + if (o.value == null) { + return false; + } else if (o.type === "http:basic") { + return o.value.username != null || o.value.password != null; + } else if (o.type === "http:custom") { + return null; + } else if (o.type === "oauth2:password") { + return ( + typeof o.value === "string" && !!o.value + ); + } else if (o.type === "oauth2:client_credentials") { + if (typeof o.value == "string") { + return !!o.value; + } + return o.value.clientID != null || o.value.clientSecret != null; + } else if (typeof o.value === "string") { + return !!o.value; + } else { + throw new Error( + `Unrecognized security type: ${o.type} (value type: ${typeof o + .value})`, + ); + } + }); + }); + if (option == null) { + return null; + } + + option.forEach((spec) => { + if (spec.value == null) { + return; + } + + const { type } = spec; + + switch (type) { + case "apiKey:header": + state.headers[spec.fieldName] = spec.value; + break; + case "apiKey:query": + state.queryParams[spec.fieldName] = spec.value; + break; + case "apiKey:cookie": + state.cookies[spec.fieldName] = spec.value; + break; + case "http:basic": + applyBasic(state, spec); + break; + case "http:custom": + break; + case "http:bearer": + applyBearer(state, spec); + break; + case "oauth2": + applyBearer(state, spec); + break; + case "oauth2:password": + applyBearer(state, spec); + break; + case "oauth2:client_credentials": + break; + case "openIdConnect": + applyBearer(state, spec); + break; + default: + throw SecurityError.unrecognizedType((spec satisfies never, type)); + } + }); + + return state; +} + +function applyBasic( + state: SecurityState, + spec: SecurityInputBasic, +) { + if (spec.value == null) { + return; + } + + state.basic = spec.value; +} + +function applyBearer( + state: SecurityState, + spec: + | SecurityInputBearer + | SecurityInputOAuth2 + | SecurityInputOIDC + | SecurityInputOAuth2PasswordCredentials, +) { + if (typeof spec.value !== "string" || !spec.value) { + return; + } + + let value = spec.value; + if (value.slice(0, 7).toLowerCase() !== "bearer ") { + value = `Bearer ${value}`; + } + + if (spec.fieldName !== undefined) { + state.headers[spec.fieldName] = value; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/lib/url.ts b/frameworks-grpc-gateway/sdk/src/lib/url.ts new file mode 100644 index 00000000..79e7ce66 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/lib/url.ts @@ -0,0 +1,35 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +const hasOwn = Object.prototype.hasOwnProperty; + +export type Params = Partial>; + +export function pathToFunc( + pathPattern: string, + options?: { charEncoding?: "percent" | "none" }, +): (params?: Params) => string { + const paramRE = /\{([a-zA-Z0-9_][a-zA-Z0-9_-]*?)\}/g; + + return function buildURLPath(params: Record = {}): string { + return pathPattern + .replace(paramRE, function (_, placeholder) { + if (!hasOwn.call(params, placeholder)) { + throw new Error(`Parameter '${placeholder}' is required`); + } + + const value = params[placeholder]; + if (typeof value !== "string" && typeof value !== "number") { + throw new Error( + `Parameter '${placeholder}' must be a string or number`, + ); + } + + return options?.charEncoding === "percent" + ? encodeURIComponent(`${value}`) + : `${value}`; + }) + .replace(/^\/+/, ""); + }; +} diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/http-client-errors.ts b/frameworks-grpc-gateway/sdk/src/models/errors/http-client-errors.ts new file mode 100644 index 00000000..b34f6121 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/http-client-errors.ts @@ -0,0 +1,62 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/** + * Base class for all HTTP errors. + */ +export class HTTPClientError extends Error { + /** The underlying cause of the error. */ + override readonly cause: unknown; + override name = "HTTPClientError"; + constructor(message: string, opts?: { cause?: unknown }) { + let msg = message; + if (opts?.cause) { + msg += `: ${opts.cause}`; + } + + super(msg, opts); + // In older runtimes, the cause field would not have been assigned through + // the super() call. + if (typeof this.cause === "undefined") { + this.cause = opts?.cause; + } + } +} + +/** + * An error to capture unrecognised or unexpected errors when making HTTP calls. + */ +export class UnexpectedClientError extends HTTPClientError { + override name = "UnexpectedClientError"; +} + +/** + * An error that is raised when any inputs used to create a request are invalid. + */ +export class InvalidRequestError extends HTTPClientError { + override name = "InvalidRequestError"; +} + +/** + * An error that is raised when a HTTP request was aborted by the client error. + */ +export class RequestAbortedError extends HTTPClientError { + override readonly name = "RequestAbortedError"; +} + +/** + * An error that is raised when a HTTP request timed out due to an AbortSignal + * signal timeout. + */ +export class RequestTimeoutError extends HTTPClientError { + override readonly name = "RequestTimeoutError"; +} + +/** + * An error that is raised when a HTTP client is unable to make a request to + * a server. + */ +export class ConnectionError extends HTTPClientError { + override readonly name = "ConnectionError"; +} diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/index.ts b/frameworks-grpc-gateway/sdk/src/models/errors/index.ts new file mode 100644 index 00000000..f5808ce5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/index.ts @@ -0,0 +1,9 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./http-client-errors.js"; +export * from "./response-validation-error.js"; +export * from "./sdk-default-error.js"; +export * from "./sdk-error.js"; +export * from "./sdk-validation-error.js"; diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/response-validation-error.ts b/frameworks-grpc-gateway/sdk/src/models/errors/response-validation-error.ts new file mode 100644 index 00000000..6dce5e64 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/response-validation-error.ts @@ -0,0 +1,50 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4/core"; +import { SDKError } from "./sdk-error.js"; +import { formatZodError } from "./sdk-validation-error.js"; + +export class ResponseValidationError extends SDKError { + /** + * The raw value that failed validation. + */ + public readonly rawValue: unknown; + + /** + * The raw message that failed validation. + */ + public readonly rawMessage: unknown; + + constructor( + message: string, + extra: { + response: Response; + request: Request; + body: string; + cause: unknown; + rawValue: unknown; + rawMessage: unknown; + }, + ) { + super(message, extra); + this.name = "ResponseValidationError"; + this.cause = extra.cause; + this.rawValue = extra.rawValue; + this.rawMessage = extra.rawMessage; + } + + /** + * Return a pretty-formatted error message if the underlying validation error + * is a ZodError or some other recognized error type, otherwise return the + * default error message. + */ + public pretty(): string { + if (this.cause instanceof z.$ZodError) { + return `${this.rawMessage}\n${formatZodError(this.cause)}`; + } else { + return this.toString(); + } + } +} diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/sdk-default-error.ts b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-default-error.ts new file mode 100644 index 00000000..8312be82 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-default-error.ts @@ -0,0 +1,40 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { SDKError } from "./sdk-error.js"; + +/** The fallback error class if no more specific error class is matched */ +export class SDKDefaultError extends SDKError { + constructor( + message: string, + httpMeta: { + response: Response; + request: Request; + body: string; + }, + ) { + if (message) { + message += `: `; + } + message += `Status ${httpMeta.response.status}`; + const contentType = httpMeta.response.headers.get("content-type") || `""`; + if (contentType !== "application/json") { + message += ` Content-Type ${ + contentType.includes(" ") ? `"${contentType}"` : contentType + }`; + } + const body = httpMeta.body || `""`; + message += body.length > 100 ? "\n" : ". "; + let bodyDisplay = body; + if (body.length > 10000) { + const truncated = body.substring(0, 10000); + const remaining = body.length - 10000; + bodyDisplay = `${truncated}...and ${remaining} more chars`; + } + message += `Body: ${bodyDisplay}`; + message = message.trim(); + super(message, httpMeta); + this.name = "SDKDefaultError"; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/sdk-error.ts b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-error.ts new file mode 100644 index 00000000..efcdf908 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-error.ts @@ -0,0 +1,35 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/** The base class for all HTTP error responses */ +export class SDKError extends Error { + /** HTTP status code */ + public readonly statusCode: number; + /** HTTP body */ + public readonly body: string; + /** HTTP headers */ + public readonly headers: Headers; + /** HTTP content type */ + public readonly contentType: string; + /** Raw response */ + public readonly rawResponse: Response; + + constructor( + message: string, + httpMeta: { + response: Response; + request: Request; + body: string; + }, + ) { + super(message); + this.statusCode = httpMeta.response.status; + this.body = httpMeta.body; + this.headers = httpMeta.response.headers; + this.contentType = httpMeta.response.headers.get("content-type") || ""; + this.rawResponse = httpMeta.response; + + this.name = "SDKError"; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/models/errors/sdk-validation-error.ts b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-validation-error.ts new file mode 100644 index 00000000..db00022e --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/errors/sdk-validation-error.ts @@ -0,0 +1,54 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4/core"; + +export class SDKValidationError extends Error { + /** + * The raw value that failed validation. + */ + public readonly rawValue: unknown; + + /** + * The raw message that failed validation. + */ + public readonly rawMessage: unknown; + + // Allows for backwards compatibility for `instanceof` checks of `ResponseValidationError` + static override [Symbol.hasInstance]( + instance: unknown, + ): instance is SDKValidationError { + if (!(instance instanceof Error)) return false; + if (!("rawValue" in instance)) return false; + if (!("rawMessage" in instance)) return false; + if (!("pretty" in instance)) return false; + if (typeof instance.pretty !== "function") return false; + return true; + } + + constructor(message: string, cause: unknown, rawValue: unknown) { + super(`${message}: ${cause}`); + this.name = "SDKValidationError"; + this.cause = cause; + this.rawValue = rawValue; + this.rawMessage = message; + } + + /** + * Return a pretty-formatted error message if the underlying validation error + * is a ZodError or some other recognized error type, otherwise return the + * default error message. + */ + public pretty(): string { + if (this.cause instanceof z.$ZodError) { + return `${this.rawMessage}\n${formatZodError(this.cause)}`; + } else { + return this.toString(); + } + } +} + +export function formatZodError(err: z.$ZodError): string { + return z.prettifyError(err); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/index.ts b/frameworks-grpc-gateway/sdk/src/models/index.ts new file mode 100644 index 00000000..de597b2f --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/index.ts @@ -0,0 +1,10 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./protobuf-any.js"; +export * from "./rpc-status.js"; +export * from "./speakeasy-booking.js"; +export * from "./speakeasy-delete-booking-response.js"; +export * from "./speakeasy-station.js"; +export * from "./speakeasy-trip.js"; diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/create-booking.ts b/frameworks-grpc-gateway/sdk/src/models/operations/create-booking.ts new file mode 100644 index 00000000..3a8aafe5 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/create-booking.ts @@ -0,0 +1,31 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type CreateBookingResponse = models.SpeakeasyBooking | models.RpcStatus; + +/** @internal */ +export const CreateBookingResponse$inboundSchema: z.ZodMiniType< + CreateBookingResponse, + unknown +> = smartUnion([ + models.SpeakeasyBooking$inboundSchema, + models.RpcStatus$inboundSchema, +]); + +export function createBookingResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => CreateBookingResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'CreateBookingResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/delete-booking.ts b/frameworks-grpc-gateway/sdk/src/models/operations/delete-booking.ts new file mode 100644 index 00000000..a5b81809 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/delete-booking.ts @@ -0,0 +1,61 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type DeleteBookingRequest = { + /** + * The ID of the booking to delete. + */ + bookingId: string; +}; + +export type DeleteBookingResponse = + | models.SpeakeasyDeleteBookingResponse + | models.RpcStatus; + +/** @internal */ +export type DeleteBookingRequest$Outbound = { + bookingId: string; +}; + +/** @internal */ +export const DeleteBookingRequest$outboundSchema: z.ZodMiniType< + DeleteBookingRequest$Outbound, + DeleteBookingRequest +> = z.object({ + bookingId: z.string(), +}); + +export function deleteBookingRequestToJSON( + deleteBookingRequest: DeleteBookingRequest, +): string { + return JSON.stringify( + DeleteBookingRequest$outboundSchema.parse(deleteBookingRequest), + ); +} + +/** @internal */ +export const DeleteBookingResponse$inboundSchema: z.ZodMiniType< + DeleteBookingResponse, + unknown +> = smartUnion([ + models.SpeakeasyDeleteBookingResponse$inboundSchema, + models.RpcStatus$inboundSchema, +]); + +export function deleteBookingResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => DeleteBookingResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'DeleteBookingResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/get-booking.ts b/frameworks-grpc-gateway/sdk/src/models/operations/get-booking.ts new file mode 100644 index 00000000..4d68a56b --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/get-booking.ts @@ -0,0 +1,59 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type GetBookingRequest = { + /** + * The ID of the booking to retrieve. + */ + bookingId: string; +}; + +export type GetBookingResponse = models.SpeakeasyBooking | models.RpcStatus; + +/** @internal */ +export type GetBookingRequest$Outbound = { + bookingId: string; +}; + +/** @internal */ +export const GetBookingRequest$outboundSchema: z.ZodMiniType< + GetBookingRequest$Outbound, + GetBookingRequest +> = z.object({ + bookingId: z.string(), +}); + +export function getBookingRequestToJSON( + getBookingRequest: GetBookingRequest, +): string { + return JSON.stringify( + GetBookingRequest$outboundSchema.parse(getBookingRequest), + ); +} + +/** @internal */ +export const GetBookingResponse$inboundSchema: z.ZodMiniType< + GetBookingResponse, + unknown +> = smartUnion([ + models.SpeakeasyBooking$inboundSchema, + models.RpcStatus$inboundSchema, +]); + +export function getBookingResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GetBookingResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GetBookingResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/get-bookings.ts b/frameworks-grpc-gateway/sdk/src/models/operations/get-bookings.ts new file mode 100644 index 00000000..8ebc2375 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/get-bookings.ts @@ -0,0 +1,77 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type GetBookingsEmpty = {}; + +export type GetBookingsRequest = { + empty?: GetBookingsEmpty | undefined; +}; + +export type GetBookingsResponse = + | Array + | models.RpcStatus; + +/** @internal */ +export type GetBookingsEmpty$Outbound = {}; + +/** @internal */ +export const GetBookingsEmpty$outboundSchema: z.ZodMiniType< + GetBookingsEmpty$Outbound, + GetBookingsEmpty +> = z.object({}); + +export function getBookingsEmptyToJSON( + getBookingsEmpty: GetBookingsEmpty, +): string { + return JSON.stringify( + GetBookingsEmpty$outboundSchema.parse(getBookingsEmpty), + ); +} + +/** @internal */ +export type GetBookingsRequest$Outbound = { + empty?: GetBookingsEmpty$Outbound | undefined; +}; + +/** @internal */ +export const GetBookingsRequest$outboundSchema: z.ZodMiniType< + GetBookingsRequest$Outbound, + GetBookingsRequest +> = z.object({ + empty: z.optional(z.lazy(() => GetBookingsEmpty$outboundSchema)), +}); + +export function getBookingsRequestToJSON( + getBookingsRequest: GetBookingsRequest, +): string { + return JSON.stringify( + GetBookingsRequest$outboundSchema.parse(getBookingsRequest), + ); +} + +/** @internal */ +export const GetBookingsResponse$inboundSchema: z.ZodMiniType< + GetBookingsResponse, + unknown +> = smartUnion([ + z.array(models.SpeakeasyBooking$inboundSchema), + models.RpcStatus$inboundSchema, +]); + +export function getBookingsResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GetBookingsResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GetBookingsResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/get-stations.ts b/frameworks-grpc-gateway/sdk/src/models/operations/get-stations.ts new file mode 100644 index 00000000..204df6ee --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/get-stations.ts @@ -0,0 +1,77 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type GetStationsEmpty = {}; + +export type GetStationsRequest = { + empty?: GetStationsEmpty | undefined; +}; + +export type GetStationsResponse = + | Array + | models.RpcStatus; + +/** @internal */ +export type GetStationsEmpty$Outbound = {}; + +/** @internal */ +export const GetStationsEmpty$outboundSchema: z.ZodMiniType< + GetStationsEmpty$Outbound, + GetStationsEmpty +> = z.object({}); + +export function getStationsEmptyToJSON( + getStationsEmpty: GetStationsEmpty, +): string { + return JSON.stringify( + GetStationsEmpty$outboundSchema.parse(getStationsEmpty), + ); +} + +/** @internal */ +export type GetStationsRequest$Outbound = { + empty?: GetStationsEmpty$Outbound | undefined; +}; + +/** @internal */ +export const GetStationsRequest$outboundSchema: z.ZodMiniType< + GetStationsRequest$Outbound, + GetStationsRequest +> = z.object({ + empty: z.optional(z.lazy(() => GetStationsEmpty$outboundSchema)), +}); + +export function getStationsRequestToJSON( + getStationsRequest: GetStationsRequest, +): string { + return JSON.stringify( + GetStationsRequest$outboundSchema.parse(getStationsRequest), + ); +} + +/** @internal */ +export const GetStationsResponse$inboundSchema: z.ZodMiniType< + GetStationsResponse, + unknown +> = smartUnion([ + z.array(models.SpeakeasyStation$inboundSchema), + models.RpcStatus$inboundSchema, +]); + +export function getStationsResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GetStationsResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GetStationsResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/get-trips.ts b/frameworks-grpc-gateway/sdk/src/models/operations/get-trips.ts new file mode 100644 index 00000000..445d1f77 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/get-trips.ts @@ -0,0 +1,81 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../../lib/schemas.js"; +import { Result as SafeParseResult } from "../../types/fp.js"; +import { smartUnion } from "../../types/smart-union.js"; +import { SDKValidationError } from "../errors/sdk-validation-error.js"; +import * as models from "../index.js"; + +export type GetTripsRequest = { + /** + * The ID of the origin station. + */ + origin: string; + /** + * The ID of the destination station. + */ + destination: string; + /** + * The date and time of the trip in ISO 8601 format. + */ + date: Date; + /** + * Only return trips where bicycles are allowed. + */ + bicycles?: boolean | undefined; + /** + * Only return trips where dogs are allowed. + */ + dogs?: boolean | undefined; +}; + +export type GetTripsResponse = Array | models.RpcStatus; + +/** @internal */ +export type GetTripsRequest$Outbound = { + origin: string; + destination: string; + date: string; + bicycles?: boolean | undefined; + dogs?: boolean | undefined; +}; + +/** @internal */ +export const GetTripsRequest$outboundSchema: z.ZodMiniType< + GetTripsRequest$Outbound, + GetTripsRequest +> = z.object({ + origin: z.string(), + destination: z.string(), + date: z.pipe(z.date(), z.transform(v => v.toISOString())), + bicycles: z.optional(z.boolean()), + dogs: z.optional(z.boolean()), +}); + +export function getTripsRequestToJSON( + getTripsRequest: GetTripsRequest, +): string { + return JSON.stringify(GetTripsRequest$outboundSchema.parse(getTripsRequest)); +} + +/** @internal */ +export const GetTripsResponse$inboundSchema: z.ZodMiniType< + GetTripsResponse, + unknown +> = smartUnion([ + z.array(models.SpeakeasyTrip$inboundSchema), + models.RpcStatus$inboundSchema, +]); + +export function getTripsResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => GetTripsResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'GetTripsResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/operations/index.ts b/frameworks-grpc-gateway/sdk/src/models/operations/index.ts new file mode 100644 index 00000000..3f8f8639 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/operations/index.ts @@ -0,0 +1,10 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./create-booking.js"; +export * from "./delete-booking.js"; +export * from "./get-booking.js"; +export * from "./get-bookings.js"; +export * from "./get-stations.js"; +export * from "./get-trips.js"; diff --git a/frameworks-grpc-gateway/sdk/src/models/protobuf-any.ts b/frameworks-grpc-gateway/sdk/src/models/protobuf-any.ts new file mode 100644 index 00000000..3d2d80f3 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/protobuf-any.ts @@ -0,0 +1,41 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { remap as remap$ } from "../lib/primitives.js"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import * as types from "../types/primitives.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; + +export type ProtobufAny = { + atType?: string | undefined; + [additionalProperties: string]: unknown; +}; + +/** @internal */ +export const ProtobufAny$inboundSchema: z.ZodMiniType = z + .pipe( + z.catchall( + z.object({ + "@type": types.optional(types.string()), + }), + z.any(), + ), + z.transform((v) => { + return remap$(v, { + "@type": "atType", + }); + }), + ); + +export function protobufAnyFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => ProtobufAny$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'ProtobufAny' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/rpc-status.ts b/frameworks-grpc-gateway/sdk/src/models/rpc-status.ts new file mode 100644 index 00000000..25d29585 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/rpc-status.ts @@ -0,0 +1,34 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import * as types from "../types/primitives.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; +import { ProtobufAny, ProtobufAny$inboundSchema } from "./protobuf-any.js"; + +export type RpcStatus = { + code?: number | undefined; + details?: Array | undefined; + message?: string | undefined; +}; + +/** @internal */ +export const RpcStatus$inboundSchema: z.ZodMiniType = z + .object({ + code: types.optional(types.number()), + details: types.optional(z.array(ProtobufAny$inboundSchema)), + message: types.optional(types.string()), + }); + +export function rpcStatusFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => RpcStatus$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'RpcStatus' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/speakeasy-booking.ts b/frameworks-grpc-gateway/sdk/src/models/speakeasy-booking.ts new file mode 100644 index 00000000..4c64cfda --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/speakeasy-booking.ts @@ -0,0 +1,84 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import * as types from "../types/primitives.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; + +/** + * A booking for a train trip. + */ +export type SpeakeasyBooking = { + /** + * Indicates whether the passenger has a bicycle. + */ + hasBicycle?: boolean | undefined; + /** + * Indicates whether the passenger has a dog. + */ + hasDog?: boolean | undefined; + /** + * Unique identifier for the booking. + */ + id?: string | undefined; + /** + * Name of the passenger. + */ + passengerName: string; + /** + * Identifier of the booked trip. + */ + tripId: string; +}; + +/** @internal */ +export const SpeakeasyBooking$inboundSchema: z.ZodMiniType< + SpeakeasyBooking, + unknown +> = z.object({ + hasBicycle: types.optional(types.boolean()), + hasDog: types.optional(types.boolean()), + id: types.optional(types.string()), + passengerName: types.string(), + tripId: types.string(), +}); +/** @internal */ +export type SpeakeasyBooking$Outbound = { + hasBicycle?: boolean | undefined; + hasDog?: boolean | undefined; + id?: string | undefined; + passengerName: string; + tripId: string; +}; + +/** @internal */ +export const SpeakeasyBooking$outboundSchema: z.ZodMiniType< + SpeakeasyBooking$Outbound, + SpeakeasyBooking +> = z.object({ + hasBicycle: z.optional(z.boolean()), + hasDog: z.optional(z.boolean()), + id: z.optional(z.string()), + passengerName: z.string(), + tripId: z.string(), +}); + +export function speakeasyBookingToJSON( + speakeasyBooking: SpeakeasyBooking, +): string { + return JSON.stringify( + SpeakeasyBooking$outboundSchema.parse(speakeasyBooking), + ); +} +export function speakeasyBookingFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => SpeakeasyBooking$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'SpeakeasyBooking' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/speakeasy-delete-booking-response.ts b/frameworks-grpc-gateway/sdk/src/models/speakeasy-delete-booking-response.ts new file mode 100644 index 00000000..70fdf628 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/speakeasy-delete-booking-response.ts @@ -0,0 +1,26 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; + +export type SpeakeasyDeleteBookingResponse = {}; + +/** @internal */ +export const SpeakeasyDeleteBookingResponse$inboundSchema: z.ZodMiniType< + SpeakeasyDeleteBookingResponse, + unknown +> = z.object({}); + +export function speakeasyDeleteBookingResponseFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => SpeakeasyDeleteBookingResponse$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'SpeakeasyDeleteBookingResponse' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/speakeasy-station.ts b/frameworks-grpc-gateway/sdk/src/models/speakeasy-station.ts new file mode 100644 index 00000000..e78a9dcc --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/speakeasy-station.ts @@ -0,0 +1,57 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import * as types from "../types/primitives.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; + +/** + * A train station. + */ +export type SpeakeasyStation = { + /** + * The address of the station. + */ + address: string; + /** + * The country code of the station. + */ + countryCode: string; + /** + * Unique identifier for the station. + */ + id: string; + /** + * The name of the station. + */ + name: string; + /** + * The timezone of the station in IANA format. + */ + timezone?: string | undefined; +}; + +/** @internal */ +export const SpeakeasyStation$inboundSchema: z.ZodMiniType< + SpeakeasyStation, + unknown +> = z.object({ + address: types.string(), + countryCode: types.string(), + id: types.string(), + name: types.string(), + timezone: types.optional(types.string()), +}); + +export function speakeasyStationFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => SpeakeasyStation$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'SpeakeasyStation' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/models/speakeasy-trip.ts b/frameworks-grpc-gateway/sdk/src/models/speakeasy-trip.ts new file mode 100644 index 00000000..54cef26e --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/models/speakeasy-trip.ts @@ -0,0 +1,77 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { safeParse } from "../lib/schemas.js"; +import { Result as SafeParseResult } from "../types/fp.js"; +import * as types from "../types/primitives.js"; +import { SDKValidationError } from "./errors/sdk-validation-error.js"; + +/** + * A train trip between two stations. + */ +export type SpeakeasyTrip = { + /** + * The arrival time in ISO 8601 format. + */ + arrivalTime?: Date | undefined; + /** + * Indicates whether bicycles are allowed on the trip. + */ + bicyclesAllowed?: boolean | undefined; + /** + * The departure time in ISO 8601 format. + */ + departureTime?: Date | undefined; + /** + * The ID of the destination station. + */ + destination?: string | undefined; + /** + * Indicates whether dogs are allowed on the trip. + */ + dogsAllowed?: boolean | undefined; + /** + * Unique identifier for the trip. + */ + id?: string | undefined; + /** + * The name of the operator of the trip. + */ + operator?: string | undefined; + /** + * The ID of the origin station. + */ + origin?: string | undefined; + /** + * The cost of the trip. + */ + price?: number | undefined; +}; + +/** @internal */ +export const SpeakeasyTrip$inboundSchema: z.ZodMiniType< + SpeakeasyTrip, + unknown +> = z.object({ + arrivalTime: types.optional(types.date()), + bicyclesAllowed: types.optional(types.boolean()), + departureTime: types.optional(types.date()), + destination: types.optional(types.string()), + dogsAllowed: types.optional(types.boolean()), + id: types.optional(types.string()), + operator: types.optional(types.string()), + origin: types.optional(types.string()), + price: types.optional(types.number()), +}); + +export function speakeasyTripFromJSON( + jsonString: string, +): SafeParseResult { + return safeParse( + jsonString, + (x) => SpeakeasyTrip$inboundSchema.parse(JSON.parse(x)), + `Failed to parse 'SpeakeasyTrip' from JSON`, + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/sdk/bookings.ts b/frameworks-grpc-gateway/sdk/src/sdk/bookings.ts new file mode 100644 index 00000000..23cf80e9 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/sdk/bookings.ts @@ -0,0 +1,82 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { bookingsCreateBooking } from "../funcs/bookings-create-booking.js"; +import { bookingsDeleteBooking } from "../funcs/bookings-delete-booking.js"; +import { bookingsGetBooking } from "../funcs/bookings-get-booking.js"; +import { bookingsGetBookings } from "../funcs/bookings-get-bookings.js"; +import { ClientSDK, RequestOptions } from "../lib/sdks.js"; +import * as models from "../models/index.js"; +import * as operations from "../models/operations/index.js"; +import { unwrapAsync } from "../types/fp.js"; + +export class Bookings extends ClientSDK { + /** + * List existing bookings + * + * @remarks + * Returns a list of all trip bookings by the authenticated user. + */ + async getBookings( + request?: operations.GetBookingsRequest | undefined, + options?: RequestOptions, + ): Promise { + return unwrapAsync(bookingsGetBookings( + this, + request, + options, + )); + } + + /** + * Create a booking + * + * @remarks + * A booking is a temporary hold on a trip. It is not confirmed until the payment is processed. + */ + async createBooking( + request: models.SpeakeasyBooking, + options?: RequestOptions, + ): Promise { + return unwrapAsync(bookingsCreateBooking( + this, + request, + options, + )); + } + + /** + * Delete a booking + * + * @remarks + * Deletes a booking, cancelling the hold on the trip. + */ + async deleteBooking( + request: operations.DeleteBookingRequest, + options?: RequestOptions, + ): Promise { + return unwrapAsync(bookingsDeleteBooking( + this, + request, + options, + )); + } + + /** + * Get a booking + * + * @remarks + * Returns the details of a specific booking. + */ + async getBooking( + request: operations.GetBookingRequest, + options?: RequestOptions, + ): Promise { + return unwrapAsync(bookingsGetBooking( + this, + request, + options, + )); + } +} diff --git a/frameworks-grpc-gateway/sdk/src/sdk/index.ts b/frameworks-grpc-gateway/sdk/src/sdk/index.ts new file mode 100644 index 00000000..ecac2264 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/sdk/index.ts @@ -0,0 +1,5 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export * from "./sdk.js"; diff --git a/frameworks-grpc-gateway/sdk/src/sdk/sdk.ts b/frameworks-grpc-gateway/sdk/src/sdk/sdk.ts new file mode 100644 index 00000000..914b2683 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/sdk/sdk.ts @@ -0,0 +1,25 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { ClientSDK } from "../lib/sdks.js"; +import { Bookings } from "./bookings.js"; +import { Stations } from "./stations.js"; +import { Trips } from "./trips.js"; + +export class SDK extends ClientSDK { + private _bookings?: Bookings; + get bookings(): Bookings { + return (this._bookings ??= new Bookings(this._options)); + } + + private _stations?: Stations; + get stations(): Stations { + return (this._stations ??= new Stations(this._options)); + } + + private _trips?: Trips; + get trips(): Trips { + return (this._trips ??= new Trips(this._options)); + } +} diff --git a/frameworks-grpc-gateway/sdk/src/sdk/stations.ts b/frameworks-grpc-gateway/sdk/src/sdk/stations.ts new file mode 100644 index 00000000..591239a3 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/sdk/stations.ts @@ -0,0 +1,27 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { stationsGetStations } from "../funcs/stations-get-stations.js"; +import { ClientSDK, RequestOptions } from "../lib/sdks.js"; +import * as operations from "../models/operations/index.js"; +import { unwrapAsync } from "../types/fp.js"; + +export class Stations extends ClientSDK { + /** + * Get a list of train stations + * + * @remarks + * Returns a paginated and searchable list of all train stations. + */ + async getStations( + request?: operations.GetStationsRequest | undefined, + options?: RequestOptions, + ): Promise { + return unwrapAsync(stationsGetStations( + this, + request, + options, + )); + } +} diff --git a/frameworks-grpc-gateway/sdk/src/sdk/trips.ts b/frameworks-grpc-gateway/sdk/src/sdk/trips.ts new file mode 100644 index 00000000..d689f683 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/sdk/trips.ts @@ -0,0 +1,27 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { tripsGetTrips } from "../funcs/trips-get-trips.js"; +import { ClientSDK, RequestOptions } from "../lib/sdks.js"; +import * as operations from "../models/operations/index.js"; +import { unwrapAsync } from "../types/fp.js"; + +export class Trips extends ClientSDK { + /** + * Get available train trips + * + * @remarks + * Returns a list of available train trips between the specified origin and destination stations on the given date. + */ + async getTrips( + request: operations.GetTripsRequest, + options?: RequestOptions, + ): Promise { + return unwrapAsync(tripsGetTrips( + this, + request, + options, + )); + } +} diff --git a/frameworks-grpc-gateway/sdk/src/types/async.ts b/frameworks-grpc-gateway/sdk/src/types/async.ts new file mode 100644 index 00000000..1543b95c --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/async.ts @@ -0,0 +1,69 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export type APICall = + | { + status: "complete"; + request: Request; + response: Response; + } + | { + status: "request-error"; + request: Request; + response?: undefined; + } + | { + status: "invalid"; + request?: undefined; + response?: undefined; + }; + +export class APIPromise implements Promise { + readonly #promise: Promise<[T, APICall]>; + #unwrapped: Promise | null; + + readonly [Symbol.toStringTag] = "APIPromise"; + + constructor(p: [T, APICall] | Promise<[T, APICall]>) { + this.#promise = p instanceof Promise ? p : Promise.resolve(p); + this.#unwrapped = p instanceof Promise ? null : Promise.resolve(p[0]); + } + + #getUnwrapped(): Promise { + return (this.#unwrapped ??= this.#promise.then(([value]) => value)); + } + + then( + onfulfilled?: + | ((value: T) => TResult1 | PromiseLike) + | null + | undefined, + onrejected?: + | ((reason: any) => TResult2 | PromiseLike) + | null + | undefined, + ): Promise { + return this.#promise.then( + onfulfilled ? ([value]) => onfulfilled(value) : void 0, + onrejected, + ); + } + + catch( + onrejected?: + | ((reason: any) => TResult | PromiseLike) + | null + | undefined, + ): Promise { + return this.#getUnwrapped().catch(onrejected); + } + + finally(onfinally?: (() => void) | null | undefined): Promise { + return this.#getUnwrapped().finally(onfinally); + } + + $inspect(): Promise<[T, APICall]> { + return this.#promise; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/types/blobs.ts b/frameworks-grpc-gateway/sdk/src/types/blobs.ts new file mode 100644 index 00000000..3deb988d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/blobs.ts @@ -0,0 +1,33 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; + +export const blobLikeSchema: z.ZodMiniType = z.custom( + isBlobLike, + { + message: "expected a Blob, File or Blob-like object", + abort: true, + }, +); + +export function isBlobLike(val: unknown): val is Blob { + if (val instanceof Blob) { + return true; + } + + if (typeof val !== "object" || val == null || !(Symbol.toStringTag in val)) { + return false; + } + + const name = val[Symbol.toStringTag]; + if (typeof name !== "string") { + return false; + } + if (name !== "Blob" && name !== "File") { + return false; + } + + return "stream" in val && typeof val.stream === "function"; +} diff --git a/frameworks-grpc-gateway/sdk/src/types/const-date-time.ts b/frameworks-grpc-gateway/sdk/src/types/const-date-time.ts new file mode 100644 index 00000000..586ee46a --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/const-date-time.ts @@ -0,0 +1,15 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; + +export function constDateTime( + val: string, +): z.ZodMiniType { + return z.custom((v) => { + return ( + typeof v === "string" && new Date(v).getTime() === new Date(val).getTime() + ); + }, `Value must be equivalent to ${val}`); +} diff --git a/frameworks-grpc-gateway/sdk/src/types/default-to-zero-value.ts b/frameworks-grpc-gateway/sdk/src/types/default-to-zero-value.ts new file mode 100644 index 00000000..c4cad53d --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/default-to-zero-value.ts @@ -0,0 +1,33 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { type Unrecognized, unrecognized } from "./unrecognized.js"; + +let globalCount = 0; +let refCount = 0; + +export function defaultToZeroValue(value: T): Unrecognized { + globalCount++; + return unrecognized(value); +} + +export function startCountingDefaultToZeroValue() { + refCount++; + const start = globalCount; + return { + /** + * Ends counting and returns the delta. + * @param delta - If provided, only this amount is added to the parent counter + * (used for nested unions where we only want to record the winning option's count). + * If not provided, records all counts since start(). + */ + end: (delta?: number) => { + const count = globalCount - start; + // Reset globalCount back to start, then add only the specified delta + globalCount = start + (delta ?? count); + if (--refCount === 0) globalCount = 0; + return count; + }, + }; +} diff --git a/frameworks-grpc-gateway/sdk/src/types/enums.ts b/frameworks-grpc-gateway/sdk/src/types/enums.ts new file mode 100644 index 00000000..a523bdde --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/enums.ts @@ -0,0 +1,45 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { Unrecognized, unrecognized } from "./unrecognized.js"; + +export type ClosedEnum>> = + T[keyof T]; +export type OpenEnum>> = + | T[keyof T] + | Unrecognized; + +export function inboundSchema>( + enumObj: T, +): z.ZodMiniType, unknown> { + const options = Object.values(enumObj); + return z.union([ + ...options.map(x => z.literal(x)), + z.pipe(z.string(), z.transform(x => unrecognized(x))), + ] as any); +} + +export function inboundSchemaInt>( + enumObj: T, +): z.ZodMiniType, unknown> { + // For numeric enums, Object.values returns both numbers and string keys + const options = Object.values(enumObj).filter(v => typeof v === "number"); + return z.union([ + ...options.map(x => z.literal(x)), + z.pipe(z.int(), z.transform(x => unrecognized(x))), + ] as any); +} + +export function outboundSchema>( + _: T, +): z.ZodMiniType> { + return z.string() as any; +} + +export function outboundSchemaInt>( + _: T, +): z.ZodMiniType> { + return z.int() as any; +} diff --git a/frameworks-grpc-gateway/sdk/src/types/fp.ts b/frameworks-grpc-gateway/sdk/src/types/fp.ts new file mode 100644 index 00000000..ccbe51ea --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/fp.ts @@ -0,0 +1,50 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +/** + * A monad that captures the result of a function call or an error if it was not + * successful. Railway programming, enabled by this type, can be a nicer + * alternative to traditional exception throwing because it allows functions to + * declare all _known_ errors with static types and then check for them + * exhaustively in application code. Thrown exception have a type of `unknown` + * and break out of regular control flow of programs making them harder to + * inspect and more verbose work with due to try-catch blocks. + */ +export type Result = + | { ok: true; value: T; error?: never } + | { ok: false; value?: never; error: E }; + +export function OK(value: V): Result { + return { ok: true, value }; +} + +export function ERR(error: E): Result { + return { ok: false, error }; +} + +/** + * unwrap is a convenience function for extracting a value from a result or + * throwing if there was an error. + */ +export function unwrap(r: Result): T { + if (!r.ok) { + throw r.error; + } + return r.value; +} + +/** + * unwrapAsync is a convenience function for resolving a value from a Promise + * of a result or rejecting if an error occurred. + */ +export async function unwrapAsync( + pr: Promise>, +): Promise { + const r = await pr; + if (!r.ok) { + throw r.error; + } + + return r.value; +} diff --git a/frameworks-grpc-gateway/sdk/src/types/index.ts b/frameworks-grpc-gateway/sdk/src/types/index.ts new file mode 100644 index 00000000..d75cb43b --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/index.ts @@ -0,0 +1,13 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export { blobLikeSchema, isBlobLike } from "./blobs.js"; +export * from "./default-to-zero-value.js"; +export type { ClosedEnum, OpenEnum } from "./enums.js"; +export type { Result } from "./fp.js"; +export type { PageIterator, Paginator } from "./operations.js"; +export { createPageIterator } from "./operations.js"; +export * from "./primitives.js"; +export { RFCDate } from "./rfcdate.js"; +export * from "./unrecognized.js"; diff --git a/frameworks-grpc-gateway/sdk/src/types/operations.ts b/frameworks-grpc-gateway/sdk/src/types/operations.ts new file mode 100644 index 00000000..beb81e10 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/operations.ts @@ -0,0 +1,105 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import { Result } from "./fp.js"; + +export type Paginator = () => Promise }> | null; + +export type PageIterator = V & { + next: Paginator; + [Symbol.asyncIterator]: () => AsyncIterableIterator; + "~next"?: PageState | undefined; +}; + +export function createPageIterator( + page: V & { next: Paginator }, + halt: (v: V) => boolean, +): { + [Symbol.asyncIterator]: () => AsyncIterableIterator; +} { + return { + [Symbol.asyncIterator]: async function* paginator() { + yield page; + if (halt(page)) { + return; + } + + let p: typeof page | null = page; + for (p = await p.next(); p != null; p = await p.next()) { + yield p; + if (halt(p)) { + return; + } + } + }, + }; +} + +/** + * This utility create a special iterator that yields a single value and + * terminates. It is useful in paginated SDK functions that have early return + * paths when things go wrong. + */ +export function haltIterator( + v: V, +): PageIterator { + return { + ...v, + next: () => null, + [Symbol.asyncIterator]: async function* paginator() { + yield v; + }, + }; +} + +/** + * Converts an async iterator of `Result` into an async iterator of `V`. + * When error results occur, the underlying error value is thrown. + */ +export async function unwrapResultIterator( + iteratorPromise: Promise, PageState>>, +): Promise> { + const resultIter = await iteratorPromise; + + if (!resultIter.ok) { + throw resultIter.error; + } + + return { + ...resultIter.value, + next: unwrapPaginator(resultIter.next), + "~next": resultIter["~next"], + [Symbol.asyncIterator]: async function* paginator() { + for await (const page of resultIter) { + if (!page.ok) { + throw page.error; + } + yield page.value; + } + }, + }; +} + +function unwrapPaginator( + paginator: Paginator>, +): Paginator { + return () => { + const nextResult = paginator(); + if (nextResult == null) { + return null; + } + return nextResult.then((res) => { + if (!res.ok) { + throw res.error; + } + const out = { + ...res.value, + next: unwrapPaginator(res.next), + }; + return out; + }); + }; +} + +export const URL_OVERRIDE = Symbol("URL_OVERRIDE"); diff --git a/frameworks-grpc-gateway/sdk/src/types/primitives.ts b/frameworks-grpc-gateway/sdk/src/types/primitives.ts new file mode 100644 index 00000000..5c4e93d2 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/primitives.ts @@ -0,0 +1,140 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +import * as z from "zod/v4-mini"; +import { defaultToZeroValue } from "./default-to-zero-value.js"; +import { unrecognized } from "./unrecognized.js"; + +export function string(): z.ZodMiniType { + return z.union([ + z.string(), + + // Null or undefined -> "" + zodDefaultToZeroValue(""), + + // Any other value -> String(x) + z.pipe(z.any(), z.transform((x) => unrecognized(JSON.stringify(x)))), + ]); +} + +export function boolean(): z.ZodMiniType { + return z.union([ + z.boolean(), + + // String "true" (case insensitive) -> true, "false" -> false + z.pipe( + z.string(), + z.transform((x, ctx) => { + const lower = x.toLowerCase(); + if (lower === "true") return unrecognized(true); + if (lower === "false") return unrecognized(false); + ctx.issues.push({ + input: x, + code: "invalid_type", + expected: "boolean", + received: "string", + }); + return z.NEVER; + }), + ), + + zodDefaultToZeroValue(false), + ]); +} + +export function number(): z.ZodMiniType { + return z.union([ + z.number(), + + // String -> Number + z.pipe( + z.string(), + z.transform((x, ctx) => { + const num = Number(x); + if (isNaN(num)) { + ctx.issues.push({ + input: x, + code: "invalid_type", + expected: "number", + received: "string", + }); + return z.NEVER; + } + return unrecognized(num); + }), + ), + + // Null or undefined -> 0 + zodDefaultToZeroValue(0), + ]); +} + +export function date(): z.ZodMiniType { + return z.union([ + z.pipe( + z.pipe( + z.union([z.string(), zodDefaultToZeroValue(0)]), + z.transform((x) => new Date(x)), + ), + z.date(), + ), + z.pipe( + z.number(), + z.transform((x, ctx) => { + const date = new Date(x); + if (isNaN(date.getTime())) { + ctx.issues.push({ + input: x, + code: "invalid_type", + expected: "date", + received: "number", + }); + return z.NEVER; + } + return unrecognized(date); + }), + ), + ]); +} + +export function literal( + value: T, +): z.ZodMiniType { + return z.union([z.literal(value), zodDefaultToZeroValue(value)]); +} + +export function optional(t: T) { + return z.optional(z.union([ + // Null -> undefined + z.pipe(z.null(), z.transform(() => unrecognized(undefined))), + t, + ])); +} + +export function nullable(t: T) { + return z.union([ + z.null(), + + // Undefined -> null + z.pipe(z.undefined(), z.transform(() => defaultToZeroValue(null))), + t, + ]); +} + +function zodDefaultToZeroValue(value: T): z.ZodMiniType { + return z.pipe( + z.any(), + z.transform((input, ctx) => { + if (input === undefined) return defaultToZeroValue(value); + if (input === null) return defaultToZeroValue(value); + ctx.issues.push({ + input: input, + code: "invalid_type", + expected: "undefined", + received: "unknown", + }); + return z.NEVER; + }), + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/types/rfcdate.ts b/frameworks-grpc-gateway/sdk/src/types/rfcdate.ts new file mode 100644 index 00000000..c79b3f53 --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/rfcdate.ts @@ -0,0 +1,54 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +const dateRE = /^\d{4}-\d{2}-\d{2}$/; + +export class RFCDate { + private serialized: string; + + /** + * Creates a new RFCDate instance using today's date. + */ + static today(): RFCDate { + return new RFCDate(new Date()); + } + + /** + * Creates a new RFCDate instance using the provided input. + * If a string is used then in must be in the format YYYY-MM-DD. + * + * @param date A Date object or a date string in YYYY-MM-DD format + * @example + * new RFCDate("2022-01-01") + * @example + * new RFCDate(new Date()) + */ + constructor(date: Date | string) { + if (typeof date === "string" && !dateRE.test(date)) { + throw new RangeError( + "RFCDate: date strings must be in the format YYYY-MM-DD: " + date, + ); + } + + const value = new Date(date); + if (isNaN(+value)) { + throw new RangeError("RFCDate: invalid date provided: " + date); + } + + this.serialized = value.toISOString().slice(0, "YYYY-MM-DD".length); + if (!dateRE.test(this.serialized)) { + throw new TypeError( + `RFCDate: failed to build valid date with given value: ${date} serialized to ${this.serialized}`, + ); + } + } + + toJSON(): string { + return this.toString(); + } + + toString(): string { + return this.serialized; + } +} diff --git a/frameworks-grpc-gateway/sdk/src/types/smart-union.ts b/frameworks-grpc-gateway/sdk/src/types/smart-union.ts new file mode 100644 index 00000000..a9db55cf --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/smart-union.ts @@ -0,0 +1,153 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +// Not needed if lax mode +import * as z from "zod/v4-mini"; +import { startCountingDefaultToZeroValue } from "./default-to-zero-value.js"; +import { RFCDate } from "./rfcdate.js"; +import { startCountingUnrecognized } from "./unrecognized.js"; + +interface Candidate { + data: unknown; + /** Total number of fields in the parsed data */ + fieldCount: number; + /** Number of fields which only matched due to lax parsing */ + inexactCount: number; + /** Number of fields which defaulted to zero values */ + zeroDefaultCount: number; +} + +/** + * Smart union parser that tries all schemas and returns the best match + * based on the number of populated fields. + */ +export function smartUnion< + Options extends readonly [z.ZodMiniType, z.ZodMiniType, ...z.ZodMiniType[]], +>( + options: Options, +): z.ZodMiniType, z.input> { + return z.pipe( + z.unknown(), + z.transform((input, ctx) => { + const candidates: Candidate[] = []; + const errors: z.core.$ZodIssue[][] = options.map(() => []); + + const parentUnrecognizedCtr = startCountingUnrecognized(); + const parentZeroDefaultCtr = startCountingDefaultToZeroValue(); + + // Filter out invalid options + for (const [i, option] of options.entries()) { + const unrecognizedCtr = startCountingUnrecognized(); + const zeroDefaultCtr = startCountingDefaultToZeroValue(); + const result = option.safeParse(input); + const inexactCount = unrecognizedCtr.end(); + const zeroDefaultCount = zeroDefaultCtr.end(); + if (result.success) { + candidates.push({ + data: result.data, + inexactCount, + zeroDefaultCount, + fieldCount: -1, // We'll count this later if needed + }); + continue; + } + errors[i]!.push(...result.error.issues); + } + + // No valid options + if (candidates.length === 0) { + parentUnrecognizedCtr.end(0); + parentZeroDefaultCtr.end(0); + ctx.issues.push({ + input: input, + code: "invalid_union", + errors: errors, + }); + return z.NEVER; + } + + let best = candidates[0]!; + + // Find the best option + for (const candidate of candidates) { + // Minor optimization to avoid counting fields if there's only one candidate + if (candidates.length > 1) { + candidate.fieldCount = countFieldsRecursive(candidate.data); + } + best = better(candidate, best); + } + + // The cost of this union should be the cost of the best candidate not all the candidates + parentUnrecognizedCtr.end(best.inexactCount); + parentZeroDefaultCtr.end(best.zeroDefaultCount); + + return best.data; + }), + ) as any; +} + +function better(a: Candidate, b: Candidate): Candidate { + // First prefer exact matches over inexact ones + const aIsExact = a.inexactCount === 0; + const bIsExact = b.inexactCount === 0; + if (aIsExact !== bIsExact) { + return aIsExact ? a : b; + } + + // Then compare field counts + const actualFieldCountA = a.fieldCount - a.zeroDefaultCount; + const actualFieldCountB = b.fieldCount - b.zeroDefaultCount; + if (actualFieldCountA !== actualFieldCountB) { + return actualFieldCountA > actualFieldCountB ? a : b; + } + return a.inexactCount < b.inexactCount ? a : b; +} + +/** + * Counts the number of fields in a parsed value recursively. + * @param `parsed` assumed to *not* contain cycles + * fieldCount: total number of fields found + * inexactCount: number of primitive values that are not unrecognized enum values + */ +function countFieldsRecursive(parsed: unknown): number { + let fieldCount = 0; + + const queue: unknown[] = [parsed]; + let index = 0; + + while (index < queue.length) { + const value = queue[index++]; + if (value === undefined) { + continue; + } + + // Check if it's a primitive value + const type = typeof value; + if ( + value === null + || type === "number" + || type === "string" + || type === "boolean" + || type === "bigint" + || value instanceof Date + || value instanceof RFCDate + ) { + fieldCount++; + continue; + } + + // Handle arrays + if (Array.isArray(value)) { + queue.push(...value); + continue; + } + + // Handle objects + if (type === "object") { + queue.push(...Object.values(value)); + } + } + + return fieldCount; +} diff --git a/frameworks-grpc-gateway/sdk/src/types/streams.ts b/frameworks-grpc-gateway/sdk/src/types/streams.ts new file mode 100644 index 00000000..a0163e7a --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/streams.ts @@ -0,0 +1,21 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +export function isReadableStream( + val: unknown, +): val is ReadableStream { + if (typeof val !== "object" || val === null) { + return false; + } + + // Check for the presence of methods specific to ReadableStream + const stream = val as ReadableStream; + + // ReadableStream has methods like getReader, cancel, and tee + return ( + typeof stream.getReader === "function" && + typeof stream.cancel === "function" && + typeof stream.tee === "function" + ); +} diff --git a/frameworks-grpc-gateway/sdk/src/types/unrecognized.ts b/frameworks-grpc-gateway/sdk/src/types/unrecognized.ts new file mode 100644 index 00000000..b7a2a13f --- /dev/null +++ b/frameworks-grpc-gateway/sdk/src/types/unrecognized.ts @@ -0,0 +1,35 @@ +/* + * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + */ + +declare const __brand: unique symbol; +export type Unrecognized = T & { [__brand]: "unrecognized" }; + +function unrecognized(value: T): Unrecognized { + globalCount++; + return value as Unrecognized; +} + +let globalCount = 0; +let refCount = 0; +export function startCountingUnrecognized() { + refCount++; + const start = globalCount; + return { + /** + * Ends counting and returns the delta. + * @param delta - If provided, only this amount is added to the parent counter + * (used for nested unions where we only want to record the winning option's count). + * If not provided, records all counts since start(). + */ + end: (delta?: number) => { + const count = globalCount - start; + // Reset globalCount back to start, then add only the specified delta + globalCount = start + (delta ?? count); + if (--refCount === 0) globalCount = 0; + return count; + }, + }; +} + +export { unrecognized }; diff --git a/frameworks-grpc-gateway/sdk/tsconfig.json b/frameworks-grpc-gateway/sdk/tsconfig.json new file mode 100644 index 00000000..e6cd950a --- /dev/null +++ b/frameworks-grpc-gateway/sdk/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "incremental": false, + "target": "ES2020", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "react-jsx", + + "module": "Node16", + "moduleResolution": "Node16", + + "allowJs": true, + + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "esm", + + + // https://github.com/tsconfig/bases/blob/a1bf7c0fa2e094b068ca3e1448ca2ece4157977e/bases/strictest.json + "strict": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "exactOptionalPropertyTypes": true, + "useUnknownInCatchVariables": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "isolatedModules": true, + "checkJs": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"], + "exclude": ["node_modules", "src/__tests__"] +} diff --git a/frameworks-grpc-gateway/server/server.go b/frameworks-grpc-gateway/server/server.go new file mode 100644 index 00000000..ff222992 --- /dev/null +++ b/frameworks-grpc-gateway/server/server.go @@ -0,0 +1,161 @@ +package server + +import ( + "context" + "fmt" + "slices" + "sync" + + speakeasyv1 "github.com/speakeasy-api/speakeasy-grpc-gateway-example/proto/speakeasy/v1" +) + +type Backend struct { + mu sync.Mutex + Stations []*speakeasyv1.Station + Trips []*speakeasyv1.Trip + Bookings []*speakeasyv1.Booking +} + +func New() *Backend { + return &Backend{ + Stations: slices.Clone(stations), + Trips: slices.Clone(trips), + Bookings: slices.Clone(bookings), + } +} + +var ( + stations = []*speakeasyv1.Station{ + { + Id: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + Name: "Berlin Hauptbahnhof", + Address: "Invalidenstrasse 10557 Berlin, Germany", + CountryCode: "DE", + Timezone: "Europe/Berlin", + }, + { + Id: "b2e783e1-c824-4d63-b37a-d8d698862f1d", + Name: "Paris Gare du Nord", + Address: "18 Rue de Dunkerque 75010 Paris, France", + CountryCode: "FR", + Timezone: "Europe/Paris", + }, + } + + trips = []*speakeasyv1.Trip{ + { + Id: "ea399ba1-6d95-433f-92d1-83f67b775594", + Origin: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + Destination: "b2e783e1-c824-4d63-b37a-d8d698862f1d", + DepartureTime: "2024-02-01T10:00:00Z", + ArrivalTime: "2024-02-01T16:00:00Z", + Price: 50, + Operator: "Deutsche Bahn", + BicyclesAllowed: true, + DogsAllowed: true, + }, + { + Id: "4d67459c-af07-40bb-bb12-178dbb88e09f", + Origin: "b2e783e1-c824-4d63-b37a-d8d698862f1d", + Destination: "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + DepartureTime: "2024-02-01T12:00:00Z", + ArrivalTime: "2024-02-01T18:00:00Z", + Price: 50, + Operator: "SNCF", + BicyclesAllowed: true, + DogsAllowed: true, + }, + } + + bookings = []*speakeasyv1.Booking{ + { + Id: "1725ff48-ab45-4bb5-9d02-88745177dedb", + TripId: "ea399ba1-6d95-433f-92d1-83f67b775594", + PassengerName: "John Doe", + HasBicycle: true, + HasDog: true, + }, + } +) + +func (b *Backend) ListStations(ctx context.Context, in *speakeasyv1.ListStationsRequest) (*speakeasyv1.ListStationsResponse, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return &speakeasyv1.ListStationsResponse{Stations: slices.Clone(b.Stations)}, nil +} + +func (b *Backend) GetTrips(ctx context.Context, in *speakeasyv1.GetTripsRequest) (*speakeasyv1.GetTripsResponse, error) { + b.mu.Lock() + defer b.mu.Unlock() + + filtered := make([]*speakeasyv1.Trip, 0) + for _, trip := range b.Trips { + if trip.Origin != in.Origin || trip.Destination != in.Destination { + continue + } + if in.Bicycles && !trip.BicyclesAllowed { + continue + } + if in.Dogs && !trip.DogsAllowed { + continue + } + filtered = append(filtered, trip) + } + + return &speakeasyv1.GetTripsResponse{Trips: filtered}, nil +} + +func (b *Backend) ListBookings(ctx context.Context, in *speakeasyv1.ListBookingsRequest) (*speakeasyv1.ListBookingsResponse, error) { + b.mu.Lock() + defer b.mu.Unlock() + + return &speakeasyv1.ListBookingsResponse{Bookings: slices.Clone(b.Bookings)}, nil +} + +func (b *Backend) CreateBooking(ctx context.Context, in *speakeasyv1.CreateBookingRequest) (*speakeasyv1.Booking, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if in.Booking == nil { + return nil, fmt.Errorf("booking payload is required") + } + + newBooking := &speakeasyv1.Booking{ + Id: fmt.Sprintf("booking-%d", len(b.Bookings)+1), + TripId: in.Booking.TripId, + PassengerName: in.Booking.PassengerName, + HasBicycle: in.Booking.HasBicycle, + HasDog: in.Booking.HasDog, + } + + b.Bookings = append(b.Bookings, newBooking) + return newBooking, nil +} + +func (b *Backend) GetBooking(ctx context.Context, in *speakeasyv1.GetBookingRequest) (*speakeasyv1.Booking, error) { + b.mu.Lock() + defer b.mu.Unlock() + + for _, booking := range b.Bookings { + if booking.Id == in.BookingId { + return booking, nil + } + } + + return nil, fmt.Errorf("booking %s not found", in.BookingId) +} + +func (b *Backend) DeleteBooking(ctx context.Context, in *speakeasyv1.DeleteBookingRequest) (*speakeasyv1.DeleteBookingResponse, error) { + b.mu.Lock() + defer b.mu.Unlock() + + for i, booking := range b.Bookings { + if booking.Id == in.BookingId { + b.Bookings = append(b.Bookings[:i], b.Bookings[i+1:]...) + return &speakeasyv1.DeleteBookingResponse{}, nil + } + } + + return nil, fmt.Errorf("booking %s not found", in.BookingId) +}