diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 99b9c42..c783225 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -20,7 +20,7 @@ jobs: - name: Init MySQL run: | - go run ./cmd/gateway/main.go init -e dev + go run ./cmd/begonia init -e dev cat ~/.begonia/admin-app.json - name: Type a temporary tag diff --git a/Makefile b/Makefile index a785872..2fef709 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ OUTPUT_DIR=./bin +GO_BIN_DIR := $(shell go env GOPATH)/bin version=$(shell git describe --tags --abbrev=0) commit=$(shell git rev-parse --short HEAD) build_time=$(shell date '+%Y%m%d%H%M%S') build: - go build -ldflags -X=github.com/begonia-org/begonia.Version=$(version)\ -X=github.com/begonia-org/begonia.BuildTime=$(build_time)\ -X=github.com/begonia-org/begonia.Commit=$(commit) -o $(OUTPUT_DIR)/begonia cmd/gateway/main.go - -.DEFAULT_GOAL := build + go build -ldflags -X=github.com/begonia-org/begonia.Version=$(version)\ -X=github.com/begonia-org/begonia.BuildTime=$(build_time)\ -X=github.com/begonia-org/begonia.Commit=$(commit) -o $(OUTPUT_DIR)/begonia cmd/begonia/*.go +install: + go install -ldflags -X=github.com/begonia-org/begonia.Version=$(version)\ -X=github.com/begonia-org/begonia.BuildTime=$(build_time)\ -X=github.com/begonia-org/begonia.Commit=$(commit) cmd/begonia/*.go +all: build install +.DEFAULT_GOAL := all diff --git a/README.md b/README.md index 9946f33..e7e8949 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,81 @@ -
-

Begonia

+

Begonia

+
+ +[![Go Report Card](https://goreportcard.com/badge/github.com/begonia-org/begonia)](https://goreportcard.com/report/github.com/begonia-org/begonia) +[![codecov](https://codecov.io/github/begonia-org/begonia/graph/badge.svg?token=VGGAA5A87B)](https://codecov.io/github/begonia-org/begonia) + +
+ +
+ +[English](README.md) | [中文](README_ZH.md) + +

-A gateway service that reverse proxies HTTP requests to gRPC services. +Begonia is an HTTP to gRPC reverse proxy server, which registers services defined by `gRPC-gateway` to the gateway based on the descriptor_set_out generated by protoc, thereby implementing reverse proxy functionality. The HTTP service follows the RESTful standard to handle HTTP requests and forwards RESTful requests to gRPC services.

-
-# About +# Features +- Compatible with all `gRPC-gateway` feature functionalities + +- Supports converting all HTTP request methods and parameter formats into gRPC requests and data formats + +- Supports converting gRPC's bi-directional streaming transport into websocket protocol for request handling + +- Supports converting server-side streaming transport into SSE (Server-Side-Event) protocol for request handling + +- Based on custom `application/begonia-client-stream` request type to forward gRPC client streaming requests + +- Supports requests with `application/x-www-form-urlencoded` and `multipart/form-data` parameter formats + +- Rich built-in middleware, such as APIKEY verification, AKSK verification, `go-playground/validator` parameter verification middleware + +# Getting Started + +### Installation +```bash +git clone https://github.com/begonia-org/begonia.git +``` + +```bash +cd begonia && make install +``` + +### Define proto +Refer to [example/example.proto](example/example.proto) + +### Generate Descriptor Set + +```shell +protoc --descriptor_set_out=example.pb --include_imports --proto_path=./ example.proto +``` + +### Start the Gateway Service + +#### 1. Build the runtime environment + +```bash +docker compose up -d +``` + +#### 2. Initialize the database + +```bash +begonia init -e dev +``` + +#### 3. Start the service + +```bash +begonia start -e dev +``` -Begonia is an HTTP to gRPC reverse proxy server that registers service routes to the gateway based on descriptor_set_out generated by protoc, thus implementing reverse proxy functionality. The HTTP service adheres to RESTful standards to handle HTTP requests and forwards RESTful requests to gRPC services +#### 4. Register the service +```bash +go run . endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 +``` +#### 5. Test request service +``` +curl -vvv http://127.0.0.1:12138/api/v1/example/hello +``` \ No newline at end of file diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..d69b307 --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,88 @@ +
+

Begonia

+
+ +[![Go Report Card](https://goreportcard.com/badge/github.com/begonia-org/begonia)](https://goreportcard.com/report/github.com/begonia-org/begonia) +[![codecov](https://codecov.io/github/begonia-org/begonia/graph/badge.svg?token=VGGAA5A87B)](https://codecov.io/github/begonia-org/begonia) + +
+ +
+ +[English](README.md) | [中文](README_ZH.md) + +
+

+Begonia 是一个 HTTP 到 gRPC 的反向代理服务器,它基于 protoc 生成的 descriptor_set_out 注册由`gRPC-gateway`定义的服务路由到网关,从而实现反向代理功能。HTTP 服务遵循 RESTful 标准来处理 HTTP 请求,并将 RESTful 请求转发到 gRPC 服务。 +

+
+ +# 特性 + +- 兼容所有的`gRPC-gateway`功能特性 + +- 支持所有 HTTP 请求方式和参数格式转换为 gRPC 请求和数据格式 + +- 支持将 gRPC 的双向流式传输转换为 websocket 协议进行请求处理 + +- 支持将服务端流式传输转换为 SSE(Server-Side-Event)协议进行请求处理 + +- 基于自定义的`application/begonia-client-stream` 请求类型转发 gRPC 的客户端流式请求 + +- 支持`application/x-www-form-urlencoded`和`multipart/form-data`参数格式的请求 + +- 丰富的内置中间件,例如 APIKEY 校验、AKSK 校验,`go-playground/validator`参数校验中间件 + +# 开始 + +### 安装 + +```bash +git clone https://github.com/begonia-org/begonia.git +``` + +```bash +cd begonia && make install +``` + +### 定义 proto + +参考[example/example.proto](example/example.proto) + +### 生成 Descriptor Set + +```shell +protoc --descriptor_set_out=example.pb --include_imports --proto_path=./ example.proto +``` + +### 启动网关服务 + +#### 1、构建运行环境 + +```bash +docker compose up -d +``` + +#### 2、初始化数据库 + +```bash +begonia init -e dev +``` + +#### 3、启动服务 + +```bash +begonia start -e dev +``` + +#### 4、注册服务 + +```bash +go run . endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 +``` + +#### 5、测试请求服务 + +``` +curl -vvv http://127.0.0.1:12138/api/v1/example/hello +``` diff --git a/cmd/begonia/endpoint.go b/cmd/begonia/endpoint.go new file mode 100644 index 0000000..f314d15 --- /dev/null +++ b/cmd/begonia/endpoint.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + + goloadbalancer "github.com/begonia-org/go-loadbalancer" + api "github.com/begonia-org/go-sdk/api/app/v1" + endpoint "github.com/begonia-org/go-sdk/api/endpoint/v1" + "github.com/begonia-org/go-sdk/client" +) + +var accessKey string +var secret string +var addr string + +func readInitAPP() { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Fatalf(err.Error()) + return + } + path := filepath.Join(homeDir, ".begonia") + path = filepath.Join(path, "admin-app.json") + file, err := os.Open(path) + if err != nil { + log.Fatalf(err.Error()) + return + + } + defer file.Close() + + decoder := json.NewDecoder(file) + app := &api.Apps{} + err = decoder.Decode(app) + if err != nil { + log.Fatalf(err.Error()) + return + + } + accessKey = app.AccessKey + secret = app.Secret + path2 := filepath.Join(homeDir, ".begonia") + path2 = filepath.Join(path2, "gateway.json") + gwFile, err := os.Open(path2) + if err != nil { + log.Fatalf(err.Error()) + return + + } + defer gwFile.Close() + gwDecoder := json.NewDecoder(gwFile) + gw := &struct { + Addr string `json:"addr"` + }{} + err = gwDecoder.Decode(gw) + if err != nil { + log.Fatalf(fmt.Sprintf("read gateway file error:%s", err.Error())) + return + + } + addr = gw.Addr + +} +func RegisterEndpoint(name string, endpoints []string, pbFile string, opts ...client.EndpointOption) { + readInitAPP() + pb, err := os.ReadFile(pbFile) + if err != nil { + panic(err) + + } + apiClient := client.NewEndpointAPI(addr, accessKey, secret) + meta := make([]*endpoint.EndpointMeta, 0) + for i, v := range endpoints { + meta = append(meta, &endpoint.EndpointMeta{ + Addr: v, + Weight: int32(i), + }) + } + endpoint := &endpoint.EndpointSrvConfig{ + DescriptorSet: pb, + Name: name, + ServiceName: name, + Description: name, + Balance: string(goloadbalancer.RRBalanceType), + Endpoints: meta, + Tags: make([]string, 0), + } + for _, opt := range opts { + opt(endpoint) + + } + rsp, err := apiClient.PostEndpointConfig(context.Background(), endpoint) + if err != nil { + log.Fatalf(err.Error()) + panic(err.Error()) + } + log.Printf("#####################Add Endpoint Success#####################") + log.Printf("#####################ID:%s####################################", rsp.Id) +} +func DeleteEndpoint(id string) { + readInitAPP() + apiClient := client.NewEndpointAPI(addr, accessKey, secret) + log.Printf("#####################Delete Endpoint:%s#####################", id) + _, err := apiClient.DeleteEndpointConfig(context.Background(), id) + if err != nil { + log.Printf("delete err %v ", err) + panic(err.Error()) + } + log.Printf("#####################Delete Endpoint Success#####################") +} diff --git a/cmd/begonia/main.go b/cmd/begonia/main.go new file mode 100644 index 0000000..ec3a7fb --- /dev/null +++ b/cmd/begonia/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal" + "github.com/begonia-org/go-sdk/client" + "github.com/spf13/cobra" +) + +// var ProviderSet = wire.NewSet(NewMasterCmd) +func addCommonCommand(cmd *cobra.Command) *cobra.Command { + cmd.Flags().StringP("env", "e", "dev", "Runtime Environment") + return cmd +} +func NewInitCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "init", + Short: "Init Database", + Run: func(cmd *cobra.Command, args []string) { + env, _ := cmd.Flags().GetString("env") + config := config.ReadConfig(env) + operator := internal.InitOperatorApp(config) + log.Printf("init database") + err := operator.Init() + if err != nil { + log.Fatalf("failed to init database: %v", err) + } + }, + } + return cmd + +} +func NewGatewayCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "start", + Short: "Start Gateway Server", + + Run: func(cmd *cobra.Command, args []string) { + endpoint, _ := cmd.Flags().GetString("endpoint") + env, _ := cmd.Flags().GetString("env") + config := config.ReadConfig(env) + worker := internal.New(config, gateway.Log, endpoint) + hd, _ := os.UserHomeDir() + _ = os.WriteFile(hd+"/.begonia/gateway.json", []byte(fmt.Sprintf(`{"addr":"http://%s"}`, endpoint)), 0666) + worker.Start() + + }, + } + cmd.Flags().StringP("endpoint", "", "127.0.0.1:12138", "Endpoint Of Your Service") + // cmd.Flags().StringP("name", "", "begonia", "Name Of Your Gateway Server") + + return cmd +} +func NewEndpointDelCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "del", + Short: "Delete Service From Gateway", + + Run: func(cmd *cobra.Command, args []string) { + id, _ := cmd.Flags().GetString("id") + + DeleteEndpoint(id) + }, + } + cmd.Flags().StringP("id", "i", "", "ID Of Your Service") + _ = cmd.MarkFlagRequired("id") + return cmd + +} +func NewEndpointAddCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "add", + Short: "Add Service To Gateway", + + Run: func(cmd *cobra.Command, args []string) { + name, _ := cmd.Flags().GetString("name") + desc, _ := cmd.Flags().GetString("desc") + tags, _ := cmd.Flags().GetStringArray("tags") + balance, _ := cmd.Flags().GetString("balance") + endpoints, _ := cmd.Flags().GetStringArray("endpoint") + + RegisterEndpoint(name, endpoints, desc, client.WithBalance(strings.ToUpper(balance)), client.WithTags(tags)) + }, + } + cmd.Flags().StringArrayP("endpoint", "p", []string{}, "Endpoint Of Your Service (example:127.0.0.1:1949)") + cmd.Flags().StringP("name", "n", "", "Service Name") + cmd.Flags().StringP("desc", "d", "", "Descriptions Set Of Your Service (example:./example/example.pb)") + cmd.Flags().StringArrayP("tags", "t", []string{}, "Tags Of Your Service") + cmd.Flags().StringP("balance", "b", "RR", "Balance Type Of Your Service (options: RR WRR LC WLC CH SED NQ)") + _ = cmd.MarkFlagRequired("name") + _ = cmd.MarkFlagRequired("endpoint") + _ = cmd.MarkFlagRequired("desc") + return cmd +} +func NewEndpointCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "endpoint", + Short: "Manage Service Of Gateway", + } + // cmd.Flags().StringP("addr", "a", "http://127.0.0.1:12138", "Address Of Begonia Gateway server") + // _ = cmd.MarkFlagRequired("addr") + + cmd.AddCommand(NewEndpointAddCmd()) + cmd.AddCommand(NewEndpointDelCmd()) + return cmd +} +func NewBegoniaInfoCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "info", + Short: "Output version info", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Begonia Version: ", begonia.Version) + fmt.Println("Build Time: ", begonia.BuildTime) + fmt.Println("Commit: ", begonia.Commit) + fmt.Println("Env: ", begonia.Env) + }, + } + return cmd +} + +func main() { + cmd := NewGatewayCmd() + cmd = addCommonCommand(cmd) + rootCmd := &cobra.Command{Use: "gateway", Short: "网关服务"} + rootCmd.AddCommand(cmd) + rootCmd.AddCommand(NewBegoniaInfoCmd()) + rootCmd.AddCommand(addCommonCommand(NewInitCmd())) + rootCmd.AddCommand(NewEndpointCmd()) + if err := cmd.Execute(); err != nil { + log.Fatalf("failed to start master: %v", err) + } +} diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go deleted file mode 100644 index 12374f7..0000000 --- a/cmd/gateway/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "log" - - "github.com/begonia-org/begonia/config" - "github.com/begonia-org/begonia/gateway" - "github.com/begonia-org/begonia/internal" - "github.com/spf13/cobra" -) - -// var ProviderSet = wire.NewSet(NewMasterCmd) -func addCommonCommand(cmd *cobra.Command) *cobra.Command { - cmd.Flags().StringP("env", "e", "dev", "Runtime Environment") - return cmd -} -func NewInitCmd() *cobra.Command { - var cmd = &cobra.Command{ - Use: "init", - Short: "初始化数据库", - Run: func(cmd *cobra.Command, args []string) { - env, _ := cmd.Flags().GetString("env") - config := config.ReadConfig(env) - operator := internal.InitOperatorApp(config) - log.Printf("init database") - err := operator.Init() - if err != nil { - log.Fatalf("failed to init database: %v", err) - } - }, - } - return cmd - -} -func NewGatewayCmd() *cobra.Command { - var cmd = &cobra.Command{ - Use: "start", - Short: "启动网关服务", - - Run: func(cmd *cobra.Command, args []string) { - endpoint, _ := cmd.Flags().GetString("endpoint") - // name, _ := cmd.Flags().GetString("name") - env, _ := cmd.Flags().GetString("env") - config := config.ReadConfig(env) - worker := internal.New(config, gateway.Log, endpoint) - worker.Start() - - }, - } - cmd.Flags().StringP("endpoint", "", "127.0.0.1:12138", "Endpoint Of Your Service") - // cmd.Flags().StringP("name", "", "begonia", "Name Of Your Gateway Server") - - return cmd -} - -func main() { - cmd := NewGatewayCmd() - cmd = addCommonCommand(cmd) - rootCmd := &cobra.Command{Use: "gateway", Short: "网关服务"} - rootCmd.AddCommand(cmd) - rootCmd.AddCommand(addCommonCommand(NewInitCmd())) - - if err := cmd.Execute(); err != nil { - log.Fatalf("failed to start master: %v", err) - } -} diff --git a/config/settings.yml b/config/settings.yml index 1abcc40..6dea531 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -69,23 +69,23 @@ gateway: auth: 4 # only_api_key_auth: 4 rpc: - - server: - name: "example-server" - endpoint: - - addr: "127.0.0.1:21216" - weight: 1 - - addr: "127.0.0.1:21217" - weight: 2 - priority: 10 - timeout: 30 - lb: "round_robin" - pool: - max_open_conns: 100 - max_idle_conns: 50 - size: 50 - timeout: 10 - min_idle_conns: 25 - max_active_conns: 20 + # - server: + # name: "example-server" + # endpoint: + # - addr: "127.0.0.1:21216" + # weight: 1 + # - addr: "127.0.0.1:21217" + # weight: 2 + # priority: 10 + # timeout: 30 + # lb: "round_robin" + # pool: + # max_open_conns: 100 + # max_idle_conns: 50 + # size: 50 + # timeout: 10 + # min_idle_conns: 25 + # max_active_conns: 20 descriptor: out_dir: "/tmp/begonia/descriptors" test: @@ -108,6 +108,37 @@ test: rsa: private_key: "/tmp/auth_private_key.pem" public_key: "/tmp/auth_public_key.pem" + gateway: + cors: + - "localhost" + - "127.0.0.1:8081" + - "example.com" + plugins: + local: + logger: 1 + exception: 0 + http: 2 + params_validator: 3 + auth: 4 + # only_api_key_auth: 4 + rpc: + - server: + name: "example-server" + endpoint: + - addr: "127.0.0.1:21216" + weight: 1 + - addr: "127.0.0.1:21217" + weight: 2 + priority: 10 + timeout: 30 + lb: "round_robin" + pool: + max_open_conns: 100 + max_idle_conns: 50 + size: 50 + timeout: 10 + min_idle_conns: 25 + max_active_conns: 20 common: cache_prefix_key: "begonia" filter_key_prefix: "filter" diff --git a/example/example.pb b/example/example.pb new file mode 100644 index 0000000..aa9e1b3 Binary files /dev/null and b/example/example.pb differ diff --git a/example/example.proto b/example/example.proto new file mode 100644 index 0000000..6533842 --- /dev/null +++ b/example/example.proto @@ -0,0 +1,101 @@ +syntax = "proto3"; + +option go_package = "example"; +option java_multiple_files = true; +option java_package = "io.grpc.examples.example"; +option java_outer_classname = "ExampleProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/descriptor.proto"; +import "google/api/httpbody.proto"; + +// The greeting service definition. +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) { + option (google.api.http)={ + post:"/api/v1/example/post" + body:"*" + }; + } + rpc SayHelloGet (HelloRequest) returns (HelloReply) { + option (google.api.http)={ + get:"/api/v1/example/{name}" + }; + } + + rpc SayHelloServerSideEvent (HelloRequest) returns (stream HelloReply) { + option (google.api.http) = { + get: "/api/v1/example/server/sse/{name}" + }; + } + + rpc SayHelloClientStream (stream HelloRequest) returns (RepeatedReply) { + option (google.api.http) = { + post: "/api/v1/example/client/stream" + body: "*" + }; + + } + rpc SayHelloWebsocket (stream HelloRequest) returns (stream HelloReply) { + option (google.api.http) = { + get: "/api/v1/example/server/websocket" + }; + } + rpc SayHelloBody (google.api.HttpBody) returns (google.api.HttpBody) { + option (google.api.http) = { + post: "/api/v1/example/body" + }; + } + rpc SayHelloError (ErrorRequest) returns (HelloReply) { + option (google.api.http) = { + get: "/api/v1/example/error/test" + }; + } + +} + +message ErrorRequest { + string msg = 1; + int32 code = 2; +} +// The request message containing the user's name. +message HelloRequest { + string msg = 1; + string name = 2; +} +message HelloSubRequest{ + // @gotags: validate:"required" + string sub_msg = 1[json_name="sub_msg"]; + // @gotags: validate:"required" + string sub_name = 2[json_name="sub_name"]; + google.protobuf.FieldMask update_mask = 3[json_name="update_mask"]; +} +message HelloRequestWithValidator{ + // @gotags: validate:"required" + string msg = 1; + // @gotags: validate:"required" + string name = 2; + // @gotags: validate:"gte=18,lte=35" + int32 age = 3; + // @gotags: validate:"required" + HelloSubRequest sub = 4; + // @gotags: validate:"dive,required" + repeated HelloSubRequest subs = 5; + google.protobuf.FieldMask update_mask = 6[json_name="update_mask"]; + + +} +// The response message containing the greetings +message HelloReply { + string message = 1; + string name = 2; +} +message RepeatedReply{ + repeated HelloReply replies = 1; +} diff --git a/example/google/api/annotations.proto b/example/google/api/annotations.proto new file mode 100644 index 0000000..efdab3d --- /dev/null +++ b/example/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/example/google/api/http.proto b/example/google/api/http.proto new file mode 100644 index 0000000..31d867a --- /dev/null +++ b/example/google/api/http.proto @@ -0,0 +1,379 @@ +// Copyright 2023 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They +// are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL +// query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP +// request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax + // details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/example/google/api/httpbody.proto b/example/google/api/httpbody.proto new file mode 100644 index 0000000..7f1685e --- /dev/null +++ b/example/google/api/httpbody.proto @@ -0,0 +1,81 @@ +// Copyright 2023 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) +// returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) +// returns (google.protobuf.Empty); +// +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} diff --git a/example/google/protobuf/any.proto b/example/google/protobuf/any.proto new file mode 100755 index 0000000..561da0c --- /dev/null +++ b/example/google/protobuf/any.proto @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/example/google/protobuf/descriptor.proto b/example/google/protobuf/descriptor.proto new file mode 100755 index 0000000..3b38675 --- /dev/null +++ b/example/google/protobuf/descriptor.proto @@ -0,0 +1,975 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + +syntax = "proto2"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; +option csharp_namespace = "Google.Protobuf.Reflection"; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; + + // The syntax of the proto file. + // The supported values are "proto2", "proto3", and "editions". + // + // If `edition` is present, this value must be "editions". + optional string syntax = 12; + + // The edition of the proto file, which is an opaque string. + optional string edition = 13; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + + optional ExtensionRangeOptions options = 3; + } + repeated ExtensionRange extension_range = 5; + + repeated OneofDescriptorProto oneof_decl = 8; + + optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; +} + +message ExtensionRangeOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + } + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + optional string default_value = 7; + + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + optional int32 oneof_index = 9; + + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + optional string json_name = 10; + + optional FieldOptions options = 8; + + // If true, this is a proto3 "optional". When a proto3 field is optional, it + // tracks presence regardless of field type. + // + // When proto3_optional is true, this field must be belong to a oneof to + // signal to old proto3 clients that presence is tracked for this field. This + // oneof is known as a "synthetic" oneof, and this field must be its sole + // member (each proto3 optional field gets its own synthetic oneof). Synthetic + // oneofs exist in the descriptor only, and do not generate any API. Synthetic + // oneofs must be ordered after all "real" oneofs. + // + // For message fields, proto3_optional doesn't create any semantic change, + // since non-repeated message fields always track presence. However it still + // indicates the semantic detail of whether the user wrote "optional" or not. + // This can be useful for round-tripping the .proto file. For consistency we + // give message fields a synthetic oneof also, even though it is not required + // to track presence. This is especially important because the parser can't + // tell if a field is a message or an enum, so it must always create a + // synthetic oneof. + // + // Proto2 optional fields do not set this flag, because they already indicate + // optional with `LABEL_OPTIONAL`. + optional bool proto3_optional = 17; +} + +// Describes a oneof. +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; + + // Range of reserved numeric values. Reserved values may not be used by + // entries in the same enum. Reserved ranges may not overlap. + // + // Note that this is distinct from DescriptorProto.ReservedRange in that it + // is inclusive such that it can appropriately represent the entire int32 + // domain. + message EnumReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. + } + + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + repeated EnumReservedRange reserved_range = 4; + + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + repeated string reserved_name = 5; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; + + // Identifies if client streams multiple client messages + optional bool client_streaming = 5 [default = false]; + // Identifies if server streams multiple server messages + optional bool server_streaming = 6 [default = false]; +} + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: +// https://developers.google.com/protocol-buffers/docs/proto#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + // Controls the name of the wrapper Java class generated for the .proto file. + // That class will always contain the .proto file's getDescriptor() method as + // well as any top-level extensions defined in the .proto file. + // If java_multiple_files is disabled, then all the other classes from the + // .proto file will be nested inside the single wrapper outer class. + optional string java_outer_classname = 8; + + // If enabled, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the wrapper class + // named by java_outer_classname. However, the wrapper class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default = false]; + + // This option does nothing. + optional bool java_generate_equals_and_hash = 20 [deprecated=true]; + + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + optional bool java_string_check_utf8 = 27 [default = false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default = SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + optional string go_package = 11; + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool php_generic_services = 42 [default = false]; + + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + optional bool deprecated = 23 [default = false]; + + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default = true]; + + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; + + // Namespace for generated classes; defaults to the package. + optional string csharp_namespace = 37; + + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + optional string swift_prefix = 39; + + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + optional string php_class_prefix = 40; + + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + optional string php_namespace = 41; + + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + optional string php_metadata_namespace = 44; + + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + optional string ruby_package = 45; + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. + extensions 1000 to max; + + reserved 38; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default = false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default = false]; + + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + optional bool deprecated = 3 [default = false]; + + reserved 4, 5, 6; + + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + // + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementations still need to work as + // if the field is a repeated message field. + optional bool map_entry = 7; + + reserved 8; // javalite_serializable + reserved 9; // javanano_as_lite + + // Enable the legacy handling of JSON field name conflicts. This lowercases + // and strips underscored from the fields before comparison in proto3 only. + // The new behavior takes `json_name` into account and applies to proto2 as + // well. + // + // This should only be used as a temporary measure against broken builds due + // to the change in behavior for JSON field name conflicts. + // + // TODO(b/261750190) This is legacy behavior we plan to remove once downstream + // teams have had time to migrate. + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + optional bool packed = 2; + + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + // + // As of May 2022, lazy verifies the contents of the byte stream during + // parsing. An invalid byte stream will cause the overall parsing to fail. + optional bool lazy = 5 [default = false]; + + // unverified_lazy does no correctness checks on the byte stream. This should + // only be used where lazy with verification is prohibitive for performance + // reasons. + optional bool unverified_lazy = 15 [default = false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default = false]; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default = false]; + + // Indicate that the field value should not be printed out when using debug + // formats, e.g. when the field contains sensitive credentials. + optional bool debug_redact = 16 [default = false]; + + // If set to RETENTION_SOURCE, the option will be omitted from the binary. + // Note: as of January 2023, support for this is in progress and does not yet + // have an effect (b/264593489). + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + + optional OptionRetention retention = 17; + + // This indicates the types of entities that the field may apply to when used + // as an option. If it is unset, then the field may be freely used as an + // option on any kind of entity. Note: as of January 2023, support for this is + // in progress and does not yet have an effect (b/264593489). + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + + optional OptionTargetType target = 18; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; + + reserved 4; // removed jtype +} + +message OneofOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to true to allow mapping different tag names to the same + // value. + optional bool allow_alias = 2; + + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + optional bool deprecated = 3 [default = false]; + + reserved 5; // javanano_as_lite + + // Enable the legacy handling of JSON field name conflicts. This lowercases + // and strips underscored from the fields before comparison in proto3 only. + // The new behavior takes `json_name` into account and applies to proto2 as + // well. + // TODO(b/261750190) Remove this legacy behavior once downstream teams have + // had time to migrate. + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + optional bool deprecated = 1 [default = false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + optional bool deprecated = 33 [default = false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + optional bool deprecated = 33 [default = false]; + + // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, + // or neither? HTTP based RPC implementation may choose GET verb for safe + // methods, and PUT verb for idempotent methods instead of the default POST. + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects + } + optional IdempotencyLevel idempotency_level = 34 + [default = IDEMPOTENCY_UNKNOWN]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents + // "foo.(bar.baz).moo". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendant. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition occurs. + // For example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed = true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed = true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to moo. + // // + // // Another line attached to moo. + // optional double moo = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to moo or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +message GeneratedCodeInfo { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + repeated Annotation annotation = 1; + message Annotation { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + repeated int32 path = 1 [packed = true]; + + // Identifies the filesystem path to the original source .proto. + optional string source_file = 2; + + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + optional int32 begin = 3; + + // Identifies the ending offset in bytes in the generated code that + // relates to the identified object. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + optional int32 end = 4; + + // Represents the identified object's effect on the element in the original + // .proto file. + enum Semantic { + // There is no effect or the effect is indescribable. + NONE = 0; + // The element is set or otherwise mutated. + SET = 1; + // An alias to the element is returned. + ALIAS = 2; + } + optional Semantic semantic = 5; + } +} diff --git a/example/google/protobuf/empty.proto b/example/google/protobuf/empty.proto new file mode 100644 index 0000000..2211524 --- /dev/null +++ b/example/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} \ No newline at end of file diff --git a/example/google/protobuf/field_mask.proto b/example/google/protobuf/field_mask.proto new file mode 100755 index 0000000..b28334b --- /dev/null +++ b/example/google/protobuf/field_mask.proto @@ -0,0 +1,245 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "FieldMaskProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb"; +option cc_enable_arenas = true; + +// `FieldMask` represents a set of symbolic field paths, for example: +// +// paths: "f.a" +// paths: "f.b.d" +// +// Here `f` represents a field in some root message, `a` and `b` +// fields in the message found in `f`, and `d` a field found in the +// message in `f.b`. +// +// Field masks are used to specify a subset of fields that should be +// returned by a get operation or modified by an update operation. +// Field masks also have a custom JSON encoding (see below). +// +// # Field Masks in Projections +// +// When used in the context of a projection, a response message or +// sub-message is filtered by the API to only contain those fields as +// specified in the mask. For example, if the mask in the previous +// example is applied to a response message as follows: +// +// f { +// a : 22 +// b { +// d : 1 +// x : 2 +// } +// y : 13 +// } +// z: 8 +// +// The result will not contain specific values for fields x,y and z +// (their value will be set to the default, and omitted in proto text +// output): +// +// +// f { +// a : 22 +// b { +// d : 1 +// } +// } +// +// A repeated field is not allowed except at the last position of a +// paths string. +// +// If a FieldMask object is not present in a get operation, the +// operation applies to all fields (as if a FieldMask of all fields +// had been specified). +// +// Note that a field mask does not necessarily apply to the +// top-level response message. In case of a REST get operation, the +// field mask applies directly to the response, but in case of a REST +// list operation, the mask instead applies to each individual message +// in the returned resource list. In case of a REST custom method, +// other definitions may be used. Where the mask applies will be +// clearly documented together with its declaration in the API. In +// any case, the effect on the returned resource/resources is required +// behavior for APIs. +// +// # Field Masks in Update Operations +// +// A field mask in update operations specifies which fields of the +// targeted resource are going to be updated. The API is required +// to only change the values of the fields as specified in the mask +// and leave the others untouched. If a resource is passed in to +// describe the updated values, the API ignores the values of all +// fields not covered by the mask. +// +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. +// +// If a sub-message is specified in the last position of the field mask for an +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: +// +// f { +// b { +// d: 1 +// x: 2 +// } +// c: [1] +// } +// +// And an update message: +// +// f { +// b { +// d: 10 +// } +// c: [2] +// } +// +// then if the field mask is: +// +// paths: ["f.b", "f.c"] +// +// then the result will be: +// +// f { +// b { +// d: 10 +// x: 2 +// } +// c: [1, 2] +// } +// +// An implementation may provide options to override this default behavior for +// repeated and message fields. +// +// In order to reset a field's value to the default, the field must +// be in the mask and set to the default value in the provided resource. +// Hence, in order to reset all fields of a resource, provide a default +// instance of the resource and set all fields in the mask, or do +// not provide a mask as described below. +// +// If a field mask is not present on update, the operation applies to +// all fields (as if a field mask of all fields has been specified). +// Note that in the presence of schema evolution, this may mean that +// fields the client does not know and has therefore not filled into +// the request will be reset to their default. If this is unwanted +// behavior, a specific service may require a client to always specify +// a field mask, producing an error if not. +// +// As with get operations, the location of the resource which +// describes the updated values in the request message depends on the +// operation kind. In any case, the effect of the field mask is +// required to be honored by the API. +// +// ## Considerations for HTTP REST +// +// The HTTP kind of an update operation which uses a field mask must +// be set to PATCH instead of PUT in order to satisfy HTTP semantics +// (PUT must only be used for full updates). +// +// # JSON Encoding of Field Masks +// +// In JSON, a field mask is encoded as a single string where paths are +// separated by a comma. Fields name in each path are converted +// to/from lower-camel naming conventions. +// +// As an example, consider the following message declarations: +// +// message Profile { +// User user = 1; +// Photo photo = 2; +// } +// message User { +// string display_name = 1; +// string address = 2; +// } +// +// In proto a field mask for `Profile` may look as such: +// +// mask { +// paths: "user.display_name" +// paths: "photo" +// } +// +// In JSON, the same mask is represented as below: +// +// { +// mask: "user.displayName,photo" +// } +// +// # Field Masks and Oneof Fields +// +// Field masks treat fields in oneofs just as regular fields. Consider the +// following message: +// +// message SampleMessage { +// oneof test_oneof { +// string name = 4; +// SubMessage sub_message = 9; +// } +// } +// +// The field mask can be: +// +// mask { +// paths: "name" +// } +// +// Or: +// +// mask { +// paths: "sub_message" +// } +// +// Note that oneof type names ("test_oneof" in this case) cannot be used in +// paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is unmappable. +message FieldMask { + // The set of field mask paths. + repeated string paths = 1; +} diff --git a/example/google/protobuf/struct.proto b/example/google/protobuf/struct.proto new file mode 100644 index 0000000..e07e343 --- /dev/null +++ b/example/google/protobuf/struct.proto @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of these +// variants. Absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} \ No newline at end of file diff --git a/example/google/protobuf/timestamp.proto b/example/google/protobuf/timestamp.proto new file mode 100755 index 0000000..2fb527c --- /dev/null +++ b/example/google/protobuf/timestamp.proto @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..49bace7 --- /dev/null +++ b/example/main.go @@ -0,0 +1,9 @@ +package main + +import "github.com/begonia-org/go-sdk/example" + +func main() { + go example.Run("0.0.0.0:1949") + example.Run("0.0.0.0:2024") + +} diff --git a/gateway/formdata_test.go b/gateway/formdata_test.go index f6244d8..64b2c44 100644 --- a/gateway/formdata_test.go +++ b/gateway/formdata_test.go @@ -150,6 +150,23 @@ func TestFormUrlEncodedErr(t *testing.T) { patch.Reset() } + formData := url.Values{} + formData.Add("message", "John Doe") + formData.Add("allow", api.EnumAllow_DENY.String()) + req := make(map[string]interface{}) + decoder := &FormUrlEncodedDecoder{r: strings.NewReader(formData.Encode())} + err := decoder.Decode(req) + c.So(err, c.ShouldBeNil) + + patch := gomonkey.ApplyFuncReturn(url.ParseQuery, nil, fmt.Errorf("parseQuery error")) + defer patch.Reset() + req2 := make(map[string]interface{}) + + decoder2 := &FormUrlEncodedDecoder{r: strings.NewReader(formData.Encode())} + err = decoder2.Decode(req2) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "parseQuery error") + patch.Reset() }) } @@ -270,5 +287,6 @@ func TestFormDataValueErr(t *testing.T) { c.So(err, c.ShouldNotBeNil) } } + }) } diff --git a/gateway/gateway.go b/gateway/gateway.go index c2e9863..b5b6eca 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -90,9 +90,9 @@ func (g *GatewayServer) RegisterService(ctx context.Context, pd ProtobufDescript return g.httpGateway.RegisterHandlerClient(ctx, pd, g.gatewayMux) } func (g *GatewayServer) RegisterLocalService(ctx context.Context, pd ProtobufDescription, sd *grpc.ServiceDesc, ss any) error { - info:=g.grpcServer.GetServiceInfo() - if _,ok:=info[sd.ServiceName];ok{ - return fmt.Errorf("service %s already exists",sd.ServiceName) + info := g.grpcServer.GetServiceInfo() + if _, ok := info[sd.ServiceName]; ok { + return fmt.Errorf("service %s already exists", sd.ServiceName) } g.grpcServer.RegisterService(sd, ss) return g.httpGateway.RegisterHandlerClient(ctx, pd, g.gatewayMux) diff --git a/gateway/http_test.go b/gateway/http_test.go index e812e0f..1ca1c2c 100644 --- a/gateway/http_test.go +++ b/gateway/http_test.go @@ -1180,8 +1180,18 @@ func testPanicRecover(t *testing.T) { patch.Reset() }) } +func testHttp2(t *testing.T) { + c.Convey("test http2", t, func() { + exampleCli := example.NewClient(fmt.Sprintf("127.0.0.1:%d", gwPort)) + msg, err := exampleCli.SayHello() + c.So(err, c.ShouldBeNil) + t.Log(msg) + c.So(msg, c.ShouldNotBeEmpty) + }) +} func TestHttp(t *testing.T) { t.Run("testRegisterClient", testRegisterClient) + t.Run("testHttp2", testHttp2) t.Run("testRequestGet", testRequestGet) t.Run("testPanicRecover", testPanicRecover) t.Run("testCors", testCors) diff --git a/gateway/types_test.go b/gateway/types_test.go new file mode 100644 index 0000000..31c56f0 --- /dev/null +++ b/gateway/types_test.go @@ -0,0 +1,30 @@ +package gateway + +import ( + "fmt" + "testing" + + "github.com/agiledragon/gomonkey/v2" + hello "github.com/begonia-org/go-sdk/api/example/v1" + c "github.com/smartystreets/goconvey/convey" +) + +func TestClientSideStreamClient(t *testing.T) { + c.Convey("test clientSideStreamClient", t, func() { + out := &hello.HelloReply{} + client := &clientSideStreamClient{ClientStream: &clientStreamMock{}, out: out.ProtoReflect().Descriptor()} + patch := gomonkey.ApplyFuncReturn((*clientStreamMock).CloseSend, fmt.Errorf("test")) + defer patch.Reset() + _, err := client.CloseAndRecv() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test") + patch.Reset() + patch2 := gomonkey.ApplyFuncReturn((*clientStreamMock).RecvMsg, fmt.Errorf("test recv error")) + defer patch2.Reset() + _, err = client.CloseAndRecv() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test recv error") + patch2.Reset() + + }) +} diff --git a/gateway/websocket.go b/gateway/websocket.go index 25254b3..c36502a 100644 --- a/gateway/websocket.go +++ b/gateway/websocket.go @@ -22,7 +22,7 @@ type websocketForwarder struct { http.ResponseWriter websocket *websocket.Conn responseType int - mux sync.Mutex + mux sync.Mutex } func NewWebsocketForwarder(w http.ResponseWriter, req *http.Request, responseType int) (WebsocketForwarder, error) { @@ -34,7 +34,7 @@ func NewWebsocketForwarder(w http.ResponseWriter, req *http.Request, responseTyp if err != nil { return nil, err } - return &websocketForwarder{w, conn, responseType,sync.Mutex{}}, nil + return &websocketForwarder{w, conn, responseType, sync.Mutex{}}, nil } func (w *websocketForwarder) Flush() { } diff --git a/go.mod b/go.mod index 4839603..f574cc6 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240601175127-e9531218a7f3 + github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 diff --git a/go.sum b/go.sum index 5673527..c99676e 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -24,6 +25,8 @@ github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= github.com/begonia-org/go-sdk v0.0.0-20240601175127-e9531218a7f3 h1:DLlhzfvYD81+QjEQqcgkKccS0nowjTU6wp/GCsLHT2M= github.com/begonia-org/go-sdk v0.0.0-20240601175127-e9531218a7f3/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e h1:R7xQlKsiWhWrR7ewCwpkHNYJqs1mXBoiI2+3TXD1qT0= +github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -114,6 +117,7 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -154,6 +158,7 @@ github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCy github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -191,6 +196,7 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -393,6 +399,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/testdata/gateway.json b/testdata/gateway.json index 7959aab..14807a4 100644 --- a/testdata/gateway.json +++ b/testdata/gateway.json @@ -1,302 +1,296 @@ { - "/integration.TestService/Body": [ - { - "FullMethodName": "/integration.TestService/Body", - "HttpMethod": "GET", - "HttpUri": "/test/body", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "HttpBody", - "OutPkg": "google.api", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "body" - ], - "Template": "/test/body", - "Verb": "", - "Version": 1 - } - } - ], - "/integration.TestService/Custom": [ - { - "FullMethodName": "/integration.TestService/Custom", - "HttpMethod": "GET", - "HttpUri": "/test/custom", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestRequest", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "custom" - ], - "Template": "/test/custom", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestService/Delete": [ - { - "FullMethodName": "/integration.TestService/Delete", - "HttpMethod": "DELETE", - "HttpUri": "/test/del", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "del" - ], - "Template": "/test/del", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestService/Get": [ - { - "FullMethodName": "/integration.TestService/Get", - "HttpMethod": "GET", - "HttpUri": "/test/get", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "get" - ], - "Template": "/test/get", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestService/Patch": [ - { - "FullMethodName": "/integration.TestService/Patch", - "HttpMethod": "PATCH", - "HttpUri": "/test/patch", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "patch" - ], - "Template": "/test/patch", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestService/Post": [ - { - "FullMethodName": "/integration.TestService/Post", - "HttpMethod": "POST", - "HttpUri": "/test/post", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "post" - ], - "Template": "/test/post", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestService/Put": [ - { - "FullMethodName": "/integration.TestService/Put", - "HttpMethod": "PUT", - "HttpUri": "/test/put", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1 - ], - "Pool": [ - "test", - "put" - ], - "Template": "/test/put", - "Verb": "", - "Version": 1 - }, - "http_response": "begonia.org.sdk.common.HttpResponse" - } - ], - "/integration.TestServiceWithoutOptions/Get": [ - { - "FullMethodName": "/integration.TestServiceWithoutOptions/Get", - "HttpMethod": "GET", - "HttpUri": "/test/v2/get", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1, - 2, - 2 - ], - "Pool": [ - "test", - "v2", - "get" - ], - "Template": "/test/v2/get", - "Verb": "", - "Version": 1 - } - } - ], - "/integration.TestServiceWithoutOptions/Post": [ - { - "FullMethodName": "/integration.TestServiceWithoutOptions/Post", - "HttpMethod": "POST", - "HttpUri": "/test/v2/post", - "InName": "TestRequest", - "InPkg": "integration", - "IsClientStream": false, - "IsServerStream": false, - "OutName": "TestResponse", - "OutPkg": "integration", - "PathParams": [], - "Pattern": {}, - "Pkg": "integration", - "Template": { - "Fields": null, - "OpCodes": [ - 2, - 0, - 2, - 1, - 2, - 2 - ], - "Pool": [ - "test", - "v2", - "post" - ], - "Template": "/test/v2/post", - "Verb": "", - "Version": 1 - } - } - ] -} + "/integration.TestService/Body": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "body" + ], + "Verb": "", + "Fields": null, + "Template": "/test/body" + }, + "HttpMethod": "GET", + "FullMethodName": "/integration.TestService/Body", + "HttpUri": "/test/body", + "PathParams": [], + "InName": "TestRequest", + "OutName": "HttpBody", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "google.api" + } + ], + "/integration.TestService/Custom": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "custom" + ], + "Verb": "", + "Fields": null, + "Template": "/test/custom" + }, + "HttpMethod": "GET", + "FullMethodName": "/integration.TestService/Custom", + "HttpUri": "/test/custom", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestRequest", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestService/Delete": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "del" + ], + "Verb": "", + "Fields": null, + "Template": "/test/del" + }, + "HttpMethod": "DELETE", + "FullMethodName": "/integration.TestService/Delete", + "HttpUri": "/test/del", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestService/Get": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "get" + ], + "Verb": "", + "Fields": null, + "Template": "/test/get" + }, + "HttpMethod": "GET", + "FullMethodName": "/integration.TestService/Get", + "HttpUri": "/test/get", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestService/Patch": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "patch" + ], + "Verb": "", + "Fields": null, + "Template": "/test/patch" + }, + "HttpMethod": "PATCH", + "FullMethodName": "/integration.TestService/Patch", + "HttpUri": "/test/patch", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestService/Post": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "post" + ], + "Verb": "", + "Fields": null, + "Template": "/test/post" + }, + "HttpMethod": "POST", + "FullMethodName": "/integration.TestService/Post", + "HttpUri": "/test/post", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestService/Put": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1 + ], + "Pool": [ + "test", + "put" + ], + "Verb": "", + "Fields": null, + "Template": "/test/put" + }, + "HttpMethod": "PUT", + "FullMethodName": "/integration.TestService/Put", + "HttpUri": "/test/put", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestServiceWithoutOptions/Get": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1, + 2, + 2 + ], + "Pool": [ + "test", + "v2", + "get" + ], + "Verb": "", + "Fields": null, + "Template": "/test/v2/get" + }, + "HttpMethod": "GET", + "FullMethodName": "/integration.TestServiceWithoutOptions/Get", + "HttpUri": "/test/v2/get", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ], + "/integration.TestServiceWithoutOptions/Post": [ + { + "Pattern": {}, + "Template": { + "Version": 1, + "OpCodes": [ + 2, + 0, + 2, + 1, + 2, + 2 + ], + "Pool": [ + "test", + "v2", + "post" + ], + "Verb": "", + "Fields": null, + "Template": "/test/v2/post" + }, + "HttpMethod": "POST", + "FullMethodName": "/integration.TestServiceWithoutOptions/Post", + "HttpUri": "/test/v2/post", + "PathParams": [], + "InName": "TestRequest", + "OutName": "TestResponse", + "IsClientStream": false, + "IsServerStream": false, + "Pkg": "integration", + "InPkg": "integration", + "OutPkg": "integration" + } + ] +} \ No newline at end of file