diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/message.proto b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/message.proto index c0161a9e..6dba0b53 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/message.proto +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/message.proto @@ -22,6 +22,8 @@ import "google/api/annotations.proto"; import "google/api/httpbody.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; option go_package = "github.com/google/gnostic/apps/protoc-gen-openapi/examples/tests/protobuftypes/message/v1;message"; @@ -101,4 +103,14 @@ message Message { google.protobuf.Value value_type = 13; // Description of repeated value repeated google.protobuf.Value repeated_value_type = 14; + google.protobuf.BoolValue bool_value_type = 15; + google.protobuf.BytesValue bytes_value_type = 16; + google.protobuf.Int32Value int32_value_type = 17; + google.protobuf.UInt32Value uint32_value_type = 18; + google.protobuf.StringValue string_value_type = 19; + google.protobuf.Int64Value int64_value_type = 20; + google.protobuf.UInt64Value uint64_value_type = 21; + google.protobuf.FloatValue float_value_type = 22; + google.protobuf.DoubleValue double_value_type = 23; + google.protobuf.Timestamp timestamp_type = 24; } diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi.yaml b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi.yaml index 14b82b38..624104e9 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi.yaml +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi.yaml @@ -98,6 +98,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: bool_value_type + in: query + schema: + type: boolean + - name: bytes_value_type + in: query + schema: + type: string + format: bytes + - name: int32_value_type + in: query + schema: + type: integer + format: int32 + - name: uint32_value_type + in: query + schema: + type: integer + format: uint32 + - name: string_value_type + in: query + schema: + type: string + - name: int64_value_type + in: query + schema: + type: string + - name: uint64_value_type + in: query + schema: + type: string + - name: float_value_type + in: query + schema: + type: number + format: float + - name: double_value_type + in: query + schema: + type: number + format: double + - name: timestamp_type + in: query + schema: + type: string + format: date-time responses: "200": description: OK @@ -211,6 +257,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: bool_value_type + in: query + schema: + type: boolean + - name: bytes_value_type + in: query + schema: + type: string + format: bytes + - name: int32_value_type + in: query + schema: + type: integer + format: int32 + - name: uint32_value_type + in: query + schema: + type: integer + format: uint32 + - name: string_value_type + in: query + schema: + type: string + - name: int64_value_type + in: query + schema: + type: string + - name: uint64_value_type + in: query + schema: + type: string + - name: float_value_type + in: query + schema: + type: number + format: float + - name: double_value_type + in: query + schema: + type: number + format: double + - name: timestamp_type + in: query + schema: + type: string + format: date-time requestBody: content: application/json: @@ -326,6 +418,32 @@ components: items: $ref: '#/components/schemas/GoogleProtobufValue' description: Description of repeated value + bool_value_type: + type: boolean + bytes_value_type: + type: string + format: bytes + int32_value_type: + type: integer + format: int32 + uint32_value_type: + type: integer + format: uint32 + string_value_type: + type: string + int64_value_type: + type: string + uint64_value_type: + type: string + float_value_type: + type: number + format: float + double_value_type: + type: number + format: double + timestamp_type: + type: string + format: date-time Message_EmbMessage: type: object properties: diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_default_response.yaml b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_default_response.yaml index c60cbf26..f8594143 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_default_response.yaml +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_default_response.yaml @@ -98,6 +98,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time responses: "200": description: OK @@ -211,6 +257,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time requestBody: content: application/json: @@ -326,6 +418,32 @@ components: items: $ref: '#/components/schemas/GoogleProtobufValue' description: Description of repeated value + boolValueType: + type: boolean + bytesValueType: + type: string + format: bytes + int32ValueType: + type: integer + format: int32 + uint32ValueType: + type: integer + format: uint32 + stringValueType: + type: string + int64ValueType: + type: string + uint64ValueType: + type: string + floatValueType: + type: number + format: float + doubleValueType: + type: number + format: double + timestampType: + type: string + format: date-time Message_EmbMessage: type: object properties: diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_fq_schema_naming.yaml b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_fq_schema_naming.yaml index 187bba79..1a674700 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_fq_schema_naming.yaml +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_fq_schema_naming.yaml @@ -98,6 +98,52 @@ paths: type: array items: $ref: '#/components/schemas/google.protobuf.Value' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time responses: "200": description: OK @@ -211,6 +257,52 @@ paths: type: array items: $ref: '#/components/schemas/google.protobuf.Value' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time requestBody: content: application/json: @@ -342,6 +434,32 @@ components: items: $ref: '#/components/schemas/google.protobuf.Value' description: Description of repeated value + boolValueType: + type: boolean + bytesValueType: + type: string + format: bytes + int32ValueType: + type: integer + format: int32 + uint32ValueType: + type: integer + format: uint32 + stringValueType: + type: string + int64ValueType: + type: string + uint64ValueType: + type: string + floatValueType: + type: number + format: float + doubleValueType: + type: number + format: double + timestampType: + type: string + format: date-time tests.protobuftypes.message.v1.Message_EmbMessage: type: object properties: diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_json.yaml b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_json.yaml index c73847e1..c23150a1 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_json.yaml +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_json.yaml @@ -98,6 +98,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time responses: "200": description: OK @@ -211,6 +257,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time requestBody: content: application/json: @@ -326,6 +418,32 @@ components: items: $ref: '#/components/schemas/GoogleProtobufValue' description: Description of repeated value + boolValueType: + type: boolean + bytesValueType: + type: string + format: bytes + int32ValueType: + type: integer + format: int32 + uint32ValueType: + type: integer + format: uint32 + stringValueType: + type: string + int64ValueType: + type: string + uint64ValueType: + type: string + floatValueType: + type: number + format: float + doubleValueType: + type: number + format: double + timestampType: + type: string + format: date-time Message_EmbMessage: type: object properties: diff --git a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_string_enum.yaml b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_string_enum.yaml index c60cbf26..f8594143 100644 --- a/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_string_enum.yaml +++ b/cmd/protoc-gen-openapi/examples/tests/protobuftypes/openapi_string_enum.yaml @@ -98,6 +98,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time responses: "200": description: OK @@ -211,6 +257,52 @@ paths: type: array items: $ref: '#/components/schemas/GoogleProtobufValue' + - name: boolValueType + in: query + schema: + type: boolean + - name: bytesValueType + in: query + schema: + type: string + format: bytes + - name: int32ValueType + in: query + schema: + type: integer + format: int32 + - name: uint32ValueType + in: query + schema: + type: integer + format: uint32 + - name: stringValueType + in: query + schema: + type: string + - name: int64ValueType + in: query + schema: + type: string + - name: uint64ValueType + in: query + schema: + type: string + - name: floatValueType + in: query + schema: + type: number + format: float + - name: doubleValueType + in: query + schema: + type: number + format: double + - name: timestampType + in: query + schema: + type: string + format: date-time requestBody: content: application/json: @@ -326,6 +418,32 @@ components: items: $ref: '#/components/schemas/GoogleProtobufValue' description: Description of repeated value + boolValueType: + type: boolean + bytesValueType: + type: string + format: bytes + int32ValueType: + type: integer + format: int32 + uint32ValueType: + type: integer + format: uint32 + stringValueType: + type: string + int64ValueType: + type: string + uint64ValueType: + type: string + floatValueType: + type: number + format: float + doubleValueType: + type: number + format: double + timestampType: + type: string + format: date-time Message_EmbMessage: type: object properties: diff --git a/cmd/protoc-gen-openapi/generator/generator.go b/cmd/protoc-gen-openapi/generator/generator.go index bce4e28b..f9a4acd9 100644 --- a/cmd/protoc-gen-openapi/generator/generator.go +++ b/cmd/protoc-gen-openapi/generator/generator.go @@ -269,6 +269,9 @@ func (g *OpenAPIv3Generator) findAndFormatFieldName(name string, inMessage *prot // 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. +// There are exceptions: +// - for wrapper types it will use the same representation as the wrapped primitive type in JSON +// - for google.protobuf.timestamp type it will be serialized as a string // // maps, Struct and Empty can NOT be used // messages can have any number of sub messages - including circular (e.g. sub.subsub.sub.subsub.id) @@ -293,7 +296,8 @@ func (g *OpenAPIv3Generator) _buildQueryParamsV3(field *protogen.Field, depths m } else if field.Desc.Kind() == protoreflect.MessageKind { typeName := g.reflect.fullMessageTypeName(field.Desc.Message()) - if typeName == ".google.protobuf.Value" { + switch typeName { + case ".google.protobuf.Value": fieldSchema := g.reflect.schemaOrReferenceForField(field.Desc) parameters = append(parameters, &v3.ParameterOrReference{ @@ -308,7 +312,44 @@ func (g *OpenAPIv3Generator) _buildQueryParamsV3(field *protogen.Field, depths m }, }) return parameters - } else if field.Desc.IsList() { + + case ".google.protobuf.BoolValue", ".google.protobuf.BytesValue", ".google.protobuf.Int32Value", ".google.protobuf.UInt32Value", + ".google.protobuf.StringValue", ".google.protobuf.Int64Value", ".google.protobuf.UInt64Value", ".google.protobuf.FloatValue", + ".google.protobuf.DoubleValue": + valueField := getValueField(field.Message.Desc) + fieldSchema := g.reflect.schemaOrReferenceForField(valueField) + parameters = append(parameters, + &v3.ParameterOrReference{ + Oneof: &v3.ParameterOrReference_Parameter{ + Parameter: &v3.Parameter{ + Name: queryFieldName, + In: "query", + Description: fieldDescription, + Required: false, + Schema: fieldSchema, + }, + }, + }) + return parameters + + case ".google.protobuf.Timestamp": + fieldSchema := g.reflect.schemaOrReferenceForMessage(field.Message.Desc) + parameters = append(parameters, + &v3.ParameterOrReference{ + Oneof: &v3.ParameterOrReference_Parameter{ + Parameter: &v3.Parameter{ + Name: queryFieldName, + In: "query", + Description: fieldDescription, + Required: false, + Schema: fieldSchema, + }, + }, + }) + return parameters + } + + if field.Desc.IsList() { // Only non-repeated message types are valid return parameters } diff --git a/cmd/protoc-gen-openapi/generator/reflector.go b/cmd/protoc-gen-openapi/generator/reflector.go index 87d18cc9..97754f4e 100644 --- a/cmd/protoc-gen-openapi/generator/reflector.go +++ b/cmd/protoc-gen-openapi/generator/reflector.go @@ -126,6 +126,7 @@ func (r *OpenAPIv3Reflector) schemaReferenceForMessage(message protoreflect.Mess // the definition in `#/components/schemas/` func (r *OpenAPIv3Reflector) schemaOrReferenceForMessage(message protoreflect.MessageDescriptor) *v3.SchemaOrReference { typeName := r.fullMessageTypeName(message) + switch typeName { case ".google.api.HttpBody": @@ -150,6 +151,21 @@ func (r *OpenAPIv3Reflector) schemaOrReferenceForMessage(message protoreflect.Me // Empty is closer to JSON undefined than null, so ignore this field return nil //&v3.SchemaOrReference{Oneof: &v3.SchemaOrReference_Schema{Schema: &v3.Schema{Type: "null"}}} + case ".google.protobuf.BoolValue": + return wk.NewBooleanSchema() + + case ".google.protobuf.BytesValue": + return wk.NewBytesSchema() + + case ".google.protobuf.Int32Value", ".google.protobuf.UInt32Value": + return wk.NewIntegerSchema(getValueKind(message)) + + case ".google.protobuf.StringValue", ".google.protobuf.Int64Value", ".google.protobuf.UInt64Value": + return wk.NewStringSchema() + + case ".google.protobuf.FloatValue", ".google.protobuf.DoubleValue": + return wk.NewNumberSchema(getValueKind(message)) + default: ref := r.schemaReferenceForMessage(message) return &v3.SchemaOrReference{ diff --git a/cmd/protoc-gen-openapi/generator/utils.go b/cmd/protoc-gen-openapi/generator/utils.go index 35671289..ca7d47dd 100644 --- a/cmd/protoc-gen-openapi/generator/utils.go +++ b/cmd/protoc-gen-openapi/generator/utils.go @@ -17,6 +17,8 @@ package generator import ( "strings" + + "google.golang.org/protobuf/reflect/protoreflect" ) // contains returns true if an array contains a specified string. @@ -50,3 +52,13 @@ func singular(plural string) string { } return plural } + +func getValueKind(message protoreflect.MessageDescriptor) string { + valueField := getValueField(message) + return valueField.Kind().String() +} + +func getValueField(message protoreflect.MessageDescriptor) protoreflect.FieldDescriptor { + fields := message.Fields() + return fields.ByName("value") +}