syntax = "proto3"; package grpc.gateway.examples.internal.proto.examplepb; import "examples/internal/proto/oneofenum/oneof_enum.proto"; import "examples/internal/proto/pathenum/path_enum.proto"; import "examples/internal/proto/sub/message.proto"; import "examples/internal/proto/sub2/message.proto"; import "google/api/annotations.proto"; import "google/api/field_behavior.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "google/rpc/status.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/proto/examplepb"; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "A Bit of Everything"; version: "1.0"; contact: { name: "gRPC-Gateway project"; url: "https://github.com/grpc-ecosystem/grpc-gateway"; email: "none@example.com"; }; license: { name: "BSD 3-Clause License"; url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE"; }; extensions: { key: "x-something-something"; value {string_value: "yadda"} } }; // Overwriting host entry breaks tests, so this is not done here. external_docs: { url: "https://github.com/grpc-ecosystem/grpc-gateway"; description: "More about gRPC-Gateway"; } schemes: HTTP; schemes: HTTPS; schemes: WSS; consumes: "application/json"; consumes: "application/x-foo-mime"; produces: "application/json"; produces: "application/x-foo-mime"; security_definitions: { security: { key: "BasicAuth"; value: {type: TYPE_BASIC} } security: { key: "ApiKeyAuth"; value: { type: TYPE_API_KEY; in: IN_HEADER; name: "X-API-Key"; extensions: { key: "x-amazon-apigateway-authtype"; value {string_value: "oauth2"} } extensions: { key: "x-amazon-apigateway-authorizer"; value { struct_value { fields { key: "type"; value {string_value: "token"} } fields { key: "authorizerResultTtlInSeconds"; value {number_value: 60} } } } } } } security: { key: "OAuth2"; value: { type: TYPE_OAUTH2; flow: FLOW_ACCESS_CODE; authorization_url: "https://example.com/oauth/authorize"; token_url: "https://example.com/oauth/token"; scopes: { scope: { key: "read"; value: "Grants read access"; } scope: { key: "write"; value: "Grants write access"; } scope: { key: "admin"; value: "Grants read and write access to administrative information"; } } } } } security: { security_requirement: { key: "BasicAuth"; value: {}; } security_requirement: { key: "ApiKeyAuth"; value: {}; } } security: { security_requirement: { key: "OAuth2"; value: { scope: "read"; scope: "write"; } } security_requirement: { key: "ApiKeyAuth"; value: {}; } } responses: { key: "403"; value: {description: "Returned when the user does not have permission to access the resource."} } responses: { key: "404"; value: { description: "Returned when the resource does not exist."; schema: { json_schema: {type: STRING} } } } responses: { key: "418"; value: { description: "I'm a teapot."; schema: { json_schema: {ref: ".grpc.gateway.examples.internal.proto.examplepb.NumericEnum"} } } } responses: { key: "500"; value: { description: "Server error"; headers: { key: "X-Correlation-Id" value: { description: "Unique event identifier for server requests" type: "string" format: "uuid" default: "\"2438ac3c-37eb-4902-adef-ed16b4431030\"" pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$" } }; schema: { json_schema: {ref: ".grpc.gateway.examples.internal.proto.examplepb.ErrorResponse"} } } } tags: { name: "echo rpc" description: "Echo Rpc description" extensions: { key: "x-traitTag"; value {bool_value: true} } } extensions: { key: "x-grpc-gateway-foo"; value {string_value: "bar"} } extensions: { key: "x-grpc-gateway-baz-list"; value { list_value: { values: {string_value: "one"} values: {bool_value: true} } } } }; message ErrorResponse { string correlationId = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$", title: "x-correlation-id", description: "Unique event identifier for server requests", format: "uuid", example: "\"2438ac3c-37eb-4902-adef-ed16b4431030\"" }]; ErrorObject error = 2; } message ErrorObject { int32 code = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { pattern: "^[0-9]$", title: "code", description: "Response code", format: "integer" }]; string message = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { pattern: "^[a-zA-Z0-9]{1, 32}$", title: "message", description: "Response message" }]; } // Intentionally complicated message type to cover many features of Protobuf. message ABitOfEverything { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { json_schema: { title: "A bit of everything" description: "Intentionally complicated message type to cover many features of Protobuf." required: [ "uuid", "int64_value", "double_value", "required_field_schema_json_name" ] extensions: { key: "x-a-bit-of-everything-foo"; value {string_value: "bar"} } } external_docs: { url: "https://github.com/grpc-ecosystem/grpc-gateway"; description: "Find out more about ABitOfEverything"; } example: "{\"int64_value\": 12, \"double_value\": 12.3}" }; // Nested is nested type. message Nested { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {example: "{\"ok\": \"TRUE\"}"}; // name is nested field. string name = 1; uint32 amount = 2; // DeepEnum is one or zero. enum DeepEnum { // FALSE is false. FALSE = 0; // TRUE is true. TRUE = 1; } // DeepEnum comment. DeepEnum ok = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "DeepEnum description."}]; } Nested single_nested = 25; string uuid = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", min_length: 1, field_configuration: {path_param_name: "uuidName"}, format: "uuid", extensions: { key: "x-internal"; value {bool_value: true} } }]; repeated Nested nested = 2; float float_value = 3 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { description: "Float value field", default: "0.2", required: ["float_value"] }]; double double_value = 4; int64 int64_value = 5; uint64 uint64_value = 6; int32 int32_value = 7; fixed64 fixed64_value = 8; fixed32 fixed32_value = 9; bool bool_value = 10; string string_value = 11; bytes bytes_value = 29; uint32 uint32_value = 13; NumericEnum enum_value = 14; pathenum.PathEnum path_enum_value = 30; pathenum.MessagePathEnum.NestedPathEnum nested_path_enum_value = 31; sfixed32 sfixed32_value = 15; sfixed64 sfixed64_value = 16; sint32 sint32_value = 17; sint64 sint64_value = 18; repeated string repeated_string_value = 19; oneof oneof_value { google.protobuf.Empty oneof_empty = 20; string oneof_string = 21; } map map_value = 22; map mapped_string_value = 23; map mapped_nested_value = 24; string nonConventionalNameValue = 26; google.protobuf.Timestamp timestamp_value = 27; // repeated enum value. it is comma-separated in query repeated NumericEnum repeated_enum_value = 28; // repeated numeric enum comment (This comment is overridden by the field annotation) repeated NumericEnum repeated_enum_annotation = 32 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { title: "Repeated numeric enum title", description: "Repeated numeric enum description." }]; // numeric enum comment (This comment is overridden by the field annotation) NumericEnum enum_value_annotation = 33 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { title: "Numeric enum title", description: "Numeric enum description." }]; // repeated string comment (This comment is overridden by the field annotation) repeated string repeated_string_annotation = 34 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { title: "Repeated string title", description: "Repeated string description." }]; // repeated nested object comment (This comment is overridden by the field annotation) repeated Nested repeated_nested_annotation = 35 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { title: "Repeated nested object title", description: "Repeated nested object description." }]; // nested object comments (This comment is overridden by the field annotation) Nested nested_annotation = 36 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { title: "Nested object title", description: "Nested object description." }]; int64 int64_override_type = 37 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {type: INTEGER}]; // mark a field as required in Open API definition string required_string_via_field_behavior_annotation = 38 [(google.api.field_behavior) = REQUIRED]; // mark a field as readonly in Open API definition string output_only_string_via_field_behavior_annotation = 39 [(google.api.field_behavior) = OUTPUT_ONLY]; optional string optional_string_value = 40; // Test openapiv2 generation of repeated fields repeated string product_id = 41 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { pattern: "^[0-9]+$" max_length: 19 min_length: 1 description: "Only digits are allowed." }]; // Test openapiv2 generation of required fields with annotation and jsonschema to reproduce string optional_string_field = 42; string required_string_field_1 = 43 [(google.api.field_behavior) = REQUIRED]; string required_string_field_2 = 44 [(google.api.field_behavior) = REQUIRED]; // Test openapiv2 handling of required json_name fields string required_field_behavior_json_name = 45 [ json_name = "required_field_behavior_json_name_custom", (google.api.field_behavior) = REQUIRED ]; string required_field_schema_json_name = 46 [json_name = "required_field_schema_json_name_custom"]; string trailing_only = 47; // Trailing only string trailing_only_dot = 48; // Trailing only dot. // Leading both string trailing_both = 49; // Trailing both. // Leading multiline // // This is an example of a multi-line comment. string trailing_multiline = 50; // Trailing multiline. // Specify a custom format of repeated field items repeated string uuids = 51 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {format: "uuid"}]; } // ABitOfEverythingRepeated is used to validate repeated path parameter functionality message ABitOfEverythingRepeated { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {example: "{\"path_repeated_bool_value\": [true, true, false, true], \"path_repeated_int32_value\": [1, 2, 3]}"}; // repeated values. they are comma-separated in path repeated float path_repeated_float_value = 1; repeated double path_repeated_double_value = 2; repeated int64 path_repeated_int64_value = 3; repeated uint64 path_repeated_uint64_value = 4; repeated int32 path_repeated_int32_value = 5; repeated fixed64 path_repeated_fixed64_value = 6; repeated fixed32 path_repeated_fixed32_value = 7; repeated bool path_repeated_bool_value = 8; repeated string path_repeated_string_value = 9; repeated bytes path_repeated_bytes_value = 10; repeated uint32 path_repeated_uint32_value = 11; repeated NumericEnum path_repeated_enum_value = 12; repeated sfixed32 path_repeated_sfixed32_value = 13; repeated sfixed64 path_repeated_sfixed64_value = 14; repeated sint32 path_repeated_sint32_value = 15; repeated sint64 path_repeated_sint64_value = 16; } message CheckStatusResponse { google.rpc.Status status = 1; } message Body { string name = 1; } message MessageWithBody { string id = 1; Body data = 2; } // NumericEnum is one or zero. enum NumericEnum { // ZERO means 0 ZERO = 0; // ONE means 1 ONE = 1; } // UpdateV2Request request for update includes the message and the update mask message UpdateV2Request { ABitOfEverything abe = 1; // The paths to update. google.protobuf.FieldMask update_mask = 2; } // An example resource type from AIP-123 used to test the behavior described in // the CreateBookRequest message. // // See: https://google.aip.dev/123 message Book { // The resource name of the book. // // Format: `publishers/{publisher}/books/{book}` // // Example: `publishers/1257894000000000000/books/my-book` string name = 1; // Output only. The book's ID. string id = 2; // Output only. Creation time of the book. google.protobuf.Timestamp create_time = 3; } // A standard Create message from AIP-133 with a user-specified ID. // The user-specified ID (the `book_id` field in this example) must become a // query parameter in the OpenAPI spec. // // See: https://google.aip.dev/133#user-specified-ids message CreateBookRequest { // The publisher in which to create the book. // // Format: `publishers/{publisher}` // // Example: `publishers/1257894000000000000` string parent = 1; // The book to create. Book book = 2; // The ID to use for the book. // // This must start with an alphanumeric character. string book_id = 3; } // A standard Update message from AIP-134 // // See: https://google.aip.dev/134#request-message message UpdateBookRequest { // The book to update. // // The book's `name` field is used to identify the book to be updated. // Format: publishers/{publisher}/books/{book} Book book = 1 [(google.api.field_behavior) = REQUIRED]; // The list of fields to be updated. google.protobuf.FieldMask update_mask = 2; // If set to true, and the book is not found, a new book will be created. // In this situation, `update_mask` is ignored. bool allow_missing = 3; } // ABitOfEverything service is used to validate that APIs with complicated // proto messages and URL templates are still processed correctly. service ABitOfEverythingService { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_tag) = { name: "ABitOfEverything" description: "ABitOfEverythingService description -- which should not be used in place of the documentation comment!" external_docs: { url: "https://github.com/grpc-ecosystem/grpc-gateway"; description: "Find out more about EchoService"; } }; // Create a new ABitOfEverything // // This API creates a new ABitOfEverything rpc Create(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = {post: "/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value=strprefix/*}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}/{nonConventionalNameValue}/{enum_value}/{path_enum_value}/{nested_path_enum_value}/{enum_value_annotation}"}; } rpc CreateBody(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = { post: "/v1/example/a_bit_of_everything" body: "*" }; } // Create a book. rpc CreateBook(CreateBookRequest) returns (Book) { option (google.api.http) = { post: "/v1/{parent=publishers/*}/books" body: "book" }; } rpc UpdateBook(UpdateBookRequest) returns (Book) { option (google.api.http) = { patch: "/v1/{book.name=publishers/*/books/*}" body: "book" }; } rpc Lookup(grpc.gateway.examples.internal.proto.sub2.IdMessage) returns (ABitOfEverything) { option (google.api.http) = {get: "/v1/example/a_bit_of_everything/{uuid}"}; } rpc Custom(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = {post: "/v1/example/a_bit_of_everything/{uuid}:custom"}; } rpc DoubleColon(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = {post: "/v1/example/a_bit_of_everything/{uuid}:custom:custom"}; } rpc Update(ABitOfEverything) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/v1/example/a_bit_of_everything/{uuid}" body: "*" }; } rpc UpdateV2(UpdateV2Request) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/v2/example/a_bit_of_everything/{abe.uuid}" body: "abe" additional_bindings: [ { patch: "/v2/example/a_bit_of_everything/{abe.uuid}" body: "abe" }, { patch: "/v2a/example/a_bit_of_everything/{abe.uuid}" body: "*" } ] }; } rpc Delete(grpc.gateway.examples.internal.proto.sub2.IdMessage) returns (google.protobuf.Empty) { option (google.api.http) = {delete: "/v1/example/a_bit_of_everything/{uuid}"}; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { security: { security_requirement: { key: "ApiKeyAuth"; value: {} } security_requirement: { key: "OAuth2"; value: { scope: "read"; scope: "write"; } } } extensions: { key: "x-irreversible"; value {bool_value: true} } }; } rpc GetQuery(ABitOfEverything) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v1/example/a_bit_of_everything/query/{uuid}"}; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { deprecated: true // For testing purposes. external_docs: { url: "https://github.com/grpc-ecosystem/grpc-gateway"; description: "Find out more about GetQuery"; } security: {} }; } rpc GetRepeatedQuery(ABitOfEverythingRepeated) returns (ABitOfEverythingRepeated) { option (google.api.http) = {get: "/v1/example/a_bit_of_everything_repeated/{path_repeated_float_value}/{path_repeated_double_value}/{path_repeated_int64_value}/{path_repeated_uint64_value}/{path_repeated_int32_value}/{path_repeated_fixed64_value}/{path_repeated_fixed32_value}/{path_repeated_bool_value}/{path_repeated_string_value}/{path_repeated_bytes_value}/{path_repeated_uint32_value}/{path_repeated_enum_value}/{path_repeated_sfixed32_value}/{path_repeated_sfixed64_value}/{path_repeated_sint32_value}/{path_repeated_sint64_value}"}; } // Echo allows posting a StringMessage value. // // It also exposes multiple bindings. // // This makes it useful when validating that the OpenAPI v2 API // description exposes documentation correctly on all paths // defined as additional_bindings in the proto. rpc Echo(grpc.gateway.examples.internal.proto.sub.StringMessage) returns (grpc.gateway.examples.internal.proto.sub.StringMessage) { option (google.api.http) = { get: "/v1/example/a_bit_of_everything/echo/{value}" additional_bindings { post: "/v2/example/echo" body: "value" } additional_bindings {get: "/v2/example/echo"} }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { description: "Description Echo"; summary: "Summary: Echo rpc"; tags: "echo rpc"; external_docs: { url: "https://github.com/grpc-ecosystem/grpc-gateway"; description: "Find out more Echo"; } responses: { key: "200" value: { examples: { key: "application/json" value: "{\"value\": \"the input value\"}" } } } responses: { key: "503"; value: { description: "Returned when the resource is temporarily unavailable."; extensions: { key: "x-number"; value {number_value: 100} } } } responses: { // Overwrites global definition. key: "404"; value: { description: "Returned when the resource does not exist."; schema: { json_schema: {type: INTEGER} } } } }; } rpc DeepPathEcho(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = { post: "/v1/example/deep_path/{single_nested.name}" body: "*" }; } rpc NoBindings(google.protobuf.Duration) returns (google.protobuf.Empty) {} rpc Timeout(google.protobuf.Empty) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v2/example/timeout"}; } rpc ErrorWithDetails(google.protobuf.Empty) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v2/example/errorwithdetails"}; } rpc GetMessageWithBody(MessageWithBody) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/v2/example/withbody/{id}", body: "data" }; } rpc PostWithEmptyBody(Body) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/v2/example/postwithemptybody/{name}", body: "*" }; } rpc CheckGetQueryParams(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = {get: "/v1/example/a_bit_of_everything/params/get/{single_nested.name}"}; } rpc CheckNestedEnumGetQueryParams(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = {get: "/v1/example/a_bit_of_everything/params/get/nested_enum/{single_nested.ok}"}; } rpc CheckPostQueryParams(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = { post: "/v1/example/a_bit_of_everything/params/post/{string_value}" body: "single_nested" }; } rpc OverwriteRequestContentType(Body) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/v2/example/overwriterequestcontenttype" body: "*" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {consumes: "application/x-bar-mime"}; } rpc OverwriteResponseContentType(google.protobuf.Empty) returns (google.protobuf.StringValue) { option (google.api.http) = {get: "/v2/example/overwriteresponsecontenttype"}; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {produces: "application/text"}; } rpc CheckExternalPathEnum(pathenum.MessageWithPathEnum) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v2/{value}:check"}; } rpc CheckExternalNestedPathEnum(pathenum.MessageWithNestedPathEnum) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v3/{value}:check"}; } rpc CheckStatus(google.protobuf.Empty) returns (CheckStatusResponse) { option (google.api.http) = {get: "/v1/example/checkStatus"}; } rpc Exists(ABitOfEverything) returns (google.protobuf.Empty) { option (google.api.http) = { custom: { kind: "HEAD", path: "/v1/example/a_bit_of_everything/{uuid}" } }; } rpc CustomOptionsRequest(ABitOfEverything) returns (google.protobuf.Empty) { option (google.api.http) = { custom: { kind: "OPTIONS", path: "/v1/example/a_bit_of_everything/{uuid}" } }; } rpc TraceRequest(ABitOfEverything) returns (ABitOfEverything) { option (google.api.http) = { custom: { kind: "TRACE", path: "/v1/example/a_bit_of_everything/{uuid}" } }; } rpc PostOneofEnum(grpc.gateway.examples.internal.proto.oneofenum.OneofEnumMessage) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/v1/example/oneofenum" body: "example_enum" }; } rpc PostRequiredMessageType(RequiredMessageTypeRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/v1/example/requiredmessagetype" body: "*" }; } } // camelCase and lowercase service names are valid but not recommended (use TitleCase instead) service camelCaseServiceName { rpc Empty(google.protobuf.Empty) returns (google.protobuf.Empty) { option (google.api.http) = {get: "/v2/example/empty"}; } } service AnotherServiceWithNoBindings { rpc NoBindings(google.protobuf.Empty) returns (google.protobuf.Empty) {} } service SnakeEnumService { rpc SnakeEnum(SnakeEnumRequest) returns (SnakeEnumResponse) { option (google.api.http) = {get: "/v1/example/snake/{who}/{what}/{where}"}; } } // Ignoring lint warnings as this enum type exist to validate proper functionality // for projects that don't follow these lint rules. // buf:lint:ignore ENUM_PASCAL_CASE enum snake_case_enum { // buf:lint:ignore ENUM_VALUE_UPPER_SNAKE_CASE value_c = 0; // buf:lint:ignore ENUM_VALUE_UPPER_SNAKE_CASE value_d = 1; } // Ignoring lint warnings as this enum type exist to validate proper functionality // for projects that don't follow these lint rules. // buf:lint:ignore ENUM_PASCAL_CASE enum snake_case_0_enum { // buf:lint:ignore ENUM_VALUE_UPPER_SNAKE_CASE value_e = 0; // buf:lint:ignore ENUM_VALUE_UPPER_SNAKE_CASE value_f = 1; } message SnakeEnumRequest { snake_case_enum what = 1; snake_case_0_enum who = 2; pathenum.snake_case_for_import where = 3; } message SnakeEnumResponse {} // Required message type -> OpenAPI // https://github.com/grpc-ecosystem/grpc-gateway/issues/2837 message RequiredMessageTypeRequest { string id = 1 [(google.api.field_behavior) = REQUIRED]; Foo foo = 2 [(google.api.field_behavior) = REQUIRED]; } message Foo { Bar bar = 1 [(google.api.field_behavior) = REQUIRED]; } message Bar { string id = 1 [(google.api.field_behavior) = REQUIRED]; }