Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/fullpackage #325

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cmd/protoc-gen-openapi/examples/tests/fullpackage/a.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";

package example.fullpackage.a;

option go_package = "github.com/google/gnostic/cmd/protoc-gen-openapi/examples/tests/fullpackage/a";

import "google/api/annotations.proto";

enum Enum {
ENUM_UNSPECIFIED = 0;
ENUM_1 = 1;
}

message Message {
Enum enum = 1;
}

service Service {
rpc Method(Message) returns (Message) {
option (google.api.http) = {
post : "/v1/a/message"
body : "*"
};
}
}
19 changes: 19 additions & 0 deletions cmd/protoc-gen-openapi/examples/tests/fullpackage/b.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";

package example.fullpackage.b;

option go_package = "github.com/google/gnostic/cmd/protoc-gen-openapi/examples/tests/fullpackage/b";

import "google/api/annotations.proto";

message Message {
string text = 1;
}

service Service {
rpc Method(Message) returns (Message) {
option (google.api.http) = {
get : "/v1/b/message"
};
}
}
22 changes: 22 additions & 0 deletions cmd/protoc-gen-openapi/examples/tests/fullpackage/c.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto3";

package example.fullpackage.c;

option go_package = "github.com/google/gnostic/cmd/protoc-gen-openapi/examples/tests/fullpackage/c";

import "google/api/annotations.proto";

message Message {
message SubMessage {
int32 value = 1;
}
SubMessage sub_message = 1;
}

service Service {
rpc Method(Message) returns (Message) {
option (google.api.http) = {
get : "/v1/c/message"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Generated with protoc-gen-openapi
# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi

openapi: 3.0.3
info:
title: ""
version: 0.0.1
paths:
/v1/a/message:
post:
tags:
- example.fullpackage.a.Service
operationId: example.fullpackage.a.Service_Method
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/example.fullpackage.a.Message'
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/example.fullpackage.a.Message'
/v1/b/message:
get:
tags:
- example.fullpackage.b.Service
operationId: example.fullpackage.b.Service_Method
parameters:
- name: text
in: query
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/example.fullpackage.b.Message'
/v1/c/message:
get:
tags:
- example.fullpackage.c.Service
operationId: example.fullpackage.c.Service_Method
parameters:
- name: subMessage.value
in: query
schema:
type: integer
format: int32
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/example.fullpackage.c.Message'
components:
schemas:
example.fullpackage.a.Message:
type: object
properties:
enum:
type: integer
format: enum
example.fullpackage.b.Message:
type: object
properties:
text:
type: string
example.fullpackage.c.Message:
type: object
properties:
subMessage:
$ref: '#/components/schemas/example.fullpackage.c.Message_SubMessage'
example.fullpackage.c.Message_SubMessage:
type: object
properties:
value:
type: integer
format: int32
tags:
- name: example.fullpackage.a.Service
- name: example.fullpackage.b.Service
- name: example.fullpackage.c.Service
19 changes: 16 additions & 3 deletions cmd/protoc-gen-openapi/generator/openapi-v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Configuration struct {
Naming *string
EnumType *string
CircularDepth *int
FullPackage *bool
}

const (
Expand Down Expand Up @@ -233,11 +234,16 @@ func (g *OpenAPIv3Generator) addPathsToDocumentV3(d *v3.Document, file *protogen
for _, service := range file.Services {
annotationsCount := 0

serviceName := service.GoName
if *g.conf.FullPackage {
serviceName = string(file.Desc.FullName()) + "." + serviceName
}

for _, method := range service.Methods {
comment := g.filterCommentString(method.Comments.Leading, false)
inputMessage := method.Input
outputMessage := method.Output
operationID := service.GoName + "_" + method.GoName
operationID := serviceName + "_" + method.GoName

var path string
var methodName string
Expand Down Expand Up @@ -276,14 +282,14 @@ func (g *OpenAPIv3Generator) addPathsToDocumentV3(d *v3.Document, file *protogen
defaultHost := proto.GetExtension(service.Desc.Options(), annotations.E_DefaultHost).(string)

op, path2 := g.buildOperationV3(
file, operationID, service.GoName, comment, defaultHost, path, body, inputMessage, outputMessage)
file, operationID, serviceName, comment, defaultHost, path, body, inputMessage, outputMessage)
g.addOperationV3(d, op, path2, methodName)
}
}

if annotationsCount > 0 {
comment := g.filterCommentString(service.Comments.Leading, false)
d.Tags = append(d.Tags, &v3.Tag{Name: service.GoName, Description: comment})
d.Tags = append(d.Tags, &v3.Tag{Name: serviceName, Description: comment})
}
}
}
Expand Down Expand Up @@ -319,6 +325,10 @@ func getMessageName(message protoreflect.MessageDescriptor) string {
func (g *OpenAPIv3Generator) formatMessageName(message *protogen.Message) string {
name := getMessageName(message.Desc)

if *g.conf.FullPackage {
return string(message.Desc.ParentFile().Package()) + "." + name
}

if *g.conf.Naming == "proto" {
return name
}
Expand Down Expand Up @@ -713,6 +723,9 @@ func (g *OpenAPIv3Generator) schemaReferenceForTypeName(typeName string) string
return "#/components/schemas/" + protobufValueName
}

if *g.conf.FullPackage {
return "#/components/schemas/" + strings.TrimLeft(typeName, ".")
}
parts := strings.Split(typeName, ".")
lastPart := parts[len(parts)-1]
return "#/components/schemas/" + g.formatMessageRef(lastPart)
Expand Down
1 change: 1 addition & 0 deletions cmd/protoc-gen-openapi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func main() {
Naming: flags.String("naming", "json", `naming convention. Use "proto" for passing names directly from the proto files`),
EnumType: flags.String("enum_type", "integer", `type for enum serialization. Use "string" for string-based serialization`),
CircularDepth: flags.Int("depth", 2, "depth of recursion for circular messages"),
FullPackage: flags.Bool("full_package", false, `use full package name of message, service and so on, e.g. Foo -> package.Foo`),
}

opts := protogen.Options{
Expand Down
41 changes: 38 additions & 3 deletions cmd/protoc-gen-openapi/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import (
)

var openapiTests = []struct {
name string
path string
protofile string
name string
path string
protofile string
protofiles []string
}{
{name: "Google Library example", path: "examples/google/example/library/v1/", protofile: "library.proto"},
{name: "Body mapping", path: "examples/tests/bodymapping/", protofile: "message.proto"},
Expand All @@ -35,6 +36,7 @@ var openapiTests = []struct {
{name: "JSON options", path: "examples/tests/jsonoptions/", protofile: "message.proto"},
{name: "Ignore services without annotations", path: "examples/tests/noannotations/", protofile: "message.proto"},
{name: "Enum Options", path: "examples/tests/enumoptions/", protofile: "message.proto"},
{name: "Full Package", path: "examples/tests/fullpackage/", protofiles: []string{"a.proto", "b.proto", "c.proto"}},
}

func TestOpenAPIProtobufNaming(t *testing.T) {
Expand Down Expand Up @@ -120,3 +122,36 @@ func TestOpenAPIStringEnums(t *testing.T) {
})
}
}

func TestOpenAPIFullPackage(t *testing.T) {
for _, tt := range openapiTests {
fixture := path.Join(tt.path, "openapi_fullpackage.yaml")
if _, err := os.Stat(fixture); errors.Is(err, os.ErrNotExist) {
continue
}
t.Run(tt.name, func(t *testing.T) {
// merge all files
args := []string{
"-I", "../../",
"-I", "../../third_party",
"-I", "examples",
"--openapi_out=full_package=true:.",
}
for _, protofile := range tt.protofiles {
args = append(args, path.Join(tt.path, protofile))
}
// Run protoc and the protoc-gen-openapi plugin to generate an OpenAPI spec with string Enums.
err := exec.Command("protoc", args...).Run()
if err != nil {
t.Fatalf("protoc failed: %+v", err)
}
// Verify that the generated spec matches our expected version.
err = exec.Command("diff", "openapi.yaml", fixture).Run()
if err != nil {
t.Fatalf("diff failed: %+v", err)
}
// if the test succeeded, clean up
os.Remove("openapi.yaml")
})
}
}