Skip to content

Merging swagger specs fails to use rpc comments #664

Closed
@lukasmalkmus

Description

@lukasmalkmus

I have two proto files:

a.proto:

syntax = "proto3";

package api.v1;

import "google/api/annotations.proto";

// A is a service for handling Foo stuff.
service A {
  // List all Foos
  //
  // Returns a (possibly paginated) list of all foo resources on success.
  rpc ListFoos (ListFoosRequest) returns (ListFoosResponse) {
    option (google.api.http) = { get: "/v1/foos" };
  }
}

// The ListFoos request message.
message ListFoosRequest {}

// The ListFoos response message.
message ListFoosResponse {}

b.proto:

syntax = "proto3";

package api.v1;

import "google/api/annotations.proto";

// B is a service for handling Bar stuff.
service B {
  // List all Bars
  //
  // Returns a (possibly paginated) list of all bar resources on success.
  rpc ListBars (ListBarsRequest) returns (ListBarsResponse) {
    option (google.api.http) = { get: "/v1/bars" };
  }
}

// The ListBars request message.
message ListBarsRequest {}

// The ListBars response message.
message ListBarsResponse {}

I tried to create a single swagger spec by using the allow_merge option. However, the generator removes some of the protobuf comments which get transformed into the endpoint description.

Steps you follow to reproduce the error:

  1. Grab the provided protobuf file a.proto and b.proto
  2. Generate the swagger spec without merging:
    protoc -I=. \
            -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway \
            -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
            --swagger_out=logtostderr=true:. \
            a.proto b.proto
  3. Generate the swagger spec with merging:
    protoc -I=. \
            -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway \
            -I=$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
            --swagger_out=logtostderr=true,allow_merge=true:. \
            a.proto b.proto

What did you expect to happen instead:

Take a look at the separate specifications, the protobuf comments got transformed into endpoint descriptions:

a.swagger.json:

{
  "swagger": "2.0",
  "info": {
    "title": "a.proto",
    "version": "version not set"
  },
  "schemes": [
    "http",
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/v1/foos": {
      "get": {
        "summary": "List all Foos",
        "description": "Returns a (possibly paginated) list of all foo resources on success.",
        "operationId": "ListFoos",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/v1ListFoosResponse"
            }
          }
        },
        "tags": [
          "A"
        ]
      }
    }
  },
  "definitions": {
    "v1ListFoosResponse": {
      "type": "object",
      "description": "The ListFoos response message."
    }
  }
}

b.swagger.json:

{
  "swagger": "2.0",
  "info": {
    "title": "b.proto",
    "version": "version not set"
  },
  "schemes": [
    "http",
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/v1/bars": {
      "get": {
        "summary": "List all Bars",
        "description": "Returns a (possibly paginated) list of all bar resources on success.",
        "operationId": "ListBars",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/v1ListBarsResponse"
            }
          }
        },
        "tags": [
          "B"
        ]
      }
    }
  },
  "definitions": {
    "v1ListBarsResponse": {
      "type": "object",
      "description": "The ListBars response message."
    }
  }
}

This isn't true for the merged specification:

apidocs.swagger.json:

{
  "swagger": "2.0",
  "info": {
    "title": "a.proto",
    "version": "version not set"
  },
  "schemes": [
    "http",
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/v1/bars": {
      "get": {
        "operationId": "ListBars",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/v1ListBarsResponse"
            }
          }
        },
        "tags": [
          "B"
        ]
      }
    },
    "/v1/foos": {
      "get": {
        "summary": "List all Foos",
        "description": "Returns a (possibly paginated) list of all foo resources on success.",
        "operationId": "ListFoos",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/v1ListFoosResponse"
            }
          }
        },
        "tags": [
          "A"
        ]
      }
    }
  },
  "definitions": {
    "v1ListBarsResponse": {
      "type": "object",
      "description": "The ListBars response message."
    },
    "v1ListFoosResponse": {
      "type": "object",
      "description": "The ListFoos response message."
    }
  }
}

This spec misses the description of the /v1/bars endpoint.

What's your theory on why it isn't working:

There seems to be a bug in the code which merges the two specs. I this its located around here:

} else {
mergedTarget.Enums = append(mergedTarget.Enums, f.Enums...)
mergedTarget.Messages = append(mergedTarget.Messages, f.Messages...)
mergedTarget.Services = append(mergedTarget.Services, f.Services...)
}

If merged to the leader, the code does not copy the comments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions