Skip to content

Commit

Permalink
feat: add trim-unused-types option
Browse files Browse the repository at this point in the history
  • Loading branch information
sudorandom committed Sep 28, 2024
1 parent 7e05d6a commit 197f4e4
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ protoc-gen-connect-openapi also has support for the [OpenAPI v3 annotations](htt
| include-number-enum-values | - | Include number enum values beside the string versions, defaults to only showing strings |
| path | `{filepath}` | Output filepath, defaults to per-protofile output if not given. |
| proto | - | Generate requests/repsonses with the protobuf content type |
| with-streaming | - | Generate OpenAPI for client/server/bidirectional streaming RPCs (can be messy). |
| trim-unused-types | - | Remove types that aren't references from any method request or response. |
| with-proto-names | - | Use protobuf field names instead of the camelCase JSON names for property names. |
| with-streaming | - | Generate OpenAPI for client/server/bidirectional streaming RPCs (can be messy). |
31 changes: 31 additions & 0 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ func ConvertWithOptions(req *pluginpb.CodeGeneratorRequest, opts options.Options
}

func specToFile(opts options.Options, spec *v3.Document) (string, error) {
if opts.TrimUnusedTypes {
if err := trimUnusedTypes(spec); err != nil {
return "", err
}
}
switch opts.Format {
case "yaml":
return string(spec.RenderWithIndention(2)), nil
Expand All @@ -184,6 +189,32 @@ func specToFile(opts options.Options, spec *v3.Document) (string, error) {
}
}

func trimUnusedTypes(spec *v3.Document) error {
slog.Debug("trimming unused types")
b, err := spec.Render()
if err != nil {
return err
}
doc, err := libopenapi.NewDocument(b)
if err != nil {
return err
}
model, errs := doc.BuildV3Model()
if errs != nil {
return errors.Join(errs...)
}
model.Index.BuildIndex()
references := model.Model.Rolodex.GetRootIndex().GetAllReferences()
for pair := spec.Components.Schemas.First(); pair != nil; pair = pair.Next() {
ref := fmt.Sprintf("#/components/schemas/%s", pair.Key())
if _, ok := references[ref]; !ok {
slog.Debug("trimming unused type", "name", pair.Key())
spec.Components.Schemas.Delete(pair.Key())
}
}
return nil
}

func appendToSpec(opts options.Options, spec *v3.Document, fd protoreflect.FileDescriptor) error {
gnostic.SpecWithFileAnnotations(spec, fd)
components, err := fileToComponents(opts, fd)
Expand Down
2 changes: 2 additions & 0 deletions internal/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
var scenarios = []Scenario{
{Name: "standard", Options: "allow-get,with-streaming"},
{Name: "proto_names", Options: "with-proto-names"},
{Name: "trim_unused_type", Options: "trim-unused-types"},
{Name: "with_base", Options: "base=testdata/with_base/base.yaml,trim-unused-types"},
}

type Scenario struct {
Expand Down
4 changes: 4 additions & 0 deletions internal/converter/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type Options struct {
WithProtoNames bool
// Path is the output OpenAPI path.
Path string
// TrimUnusedTypes
TrimUnusedTypes bool
}

func NewOptions() Options {
Expand Down Expand Up @@ -59,6 +61,8 @@ func FromString(s string) (Options, error) {
opts.WithStreaming = true
case param == "with-proto-names":
opts.WithProtoNames = true
case param == "trim-unused-types":
opts.TrimUnusedTypes = true
case strings.HasPrefix(param, "content-types="):
for _, contentType := range strings.Split(param[14:], ";") {
contentType = strings.TrimSpace(contentType)
Expand Down
Binary file modified internal/converter/testdata/fileset.binpb
Binary file not shown.
28 changes: 28 additions & 0 deletions internal/converter/testdata/trim_unused_type/has_unused.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Protobuf that flexes a lot of the types
syntax = "proto3";

package has_unused_types;

import "google/protobuf/empty.proto";

// This service tries to flex the different options
service FlexService {
// Normal RPC method
rpc NormalRPC(UsedRequest) returns (UsedReply) {}
}

message UsedRequest {
UsedType msg = 1;
}

message UsedReply {
UsedType msg = 1;
}

message UsedType {
string description = 1;
}

message Unused {
string unused_field = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
{
"openapi": "3.1.0",
"info": {
"title": "has_unused_types"
},
"paths": {
"/has_unused_types.FlexService/NormalRPC": {
"post": {
"tags": [
"has_unused_types.FlexService"
],
"summary": "NormalRPC",
"description": "Normal RPC method",
"operationId": "has_unused_types.FlexService.NormalRPC",
"parameters": [
{
"name": "Connect-Protocol-Version",
"in": "header",
"required": true,
"schema": {
"$ref": "#/components/schemas/connect-protocol-version"
}
},
{
"name": "Connect-Timeout-Ms",
"in": "header",
"schema": {
"$ref": "#/components/schemas/connect-timeout-header"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/has_unused_types.UsedRequest"
}
}
},
"required": true
},
"responses": {
"default": {
"description": "Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/connect.error"
}
}
}
},
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/has_unused_types.UsedReply"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"has_unused_types.UsedReply": {
"type": "object",
"properties": {
"msg": {
"$ref": "#/components/schemas/has_unused_types.UsedType"
}
},
"title": "UsedReply",
"additionalProperties": false
},
"has_unused_types.UsedRequest": {
"type": "object",
"properties": {
"msg": {
"$ref": "#/components/schemas/has_unused_types.UsedType"
}
},
"title": "UsedRequest",
"additionalProperties": false
},
"has_unused_types.UsedType": {
"type": "object",
"properties": {
"description": {
"type": "string",
"title": "description"
}
},
"title": "UsedType",
"additionalProperties": false
},
"connect-protocol-version": {
"type": "number",
"title": "Connect-Protocol-Version",
"enum": [
1
],
"description": "Define the version of the Connect protocol",
"const": 1
},
"connect-timeout-header": {
"type": "number",
"title": "Connect-Timeout-Ms",
"description": "Define the timeout, in ms"
},
"connect.error": {
"type": "object",
"properties": {
"code": {
"type": "string",
"examples": [
"CodeNotFound"
],
"enum": [
"CodeCanceled",
"CodeUnknown",
"CodeInvalidArgument",
"CodeDeadlineExceeded",
"CodeNotFound",
"CodeAlreadyExists",
"CodePermissionDenied",
"CodeResourceExhausted",
"CodeFailedPrecondition",
"CodeAborted",
"CodeOutOfRange",
"CodeInternal",
"CodeUnavailable",
"CodeDataLoss",
"CodeUnauthenticated"
],
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
},
"message": {
"type": "string",
"description": "A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
},
"detail": {
"$ref": "#/components/schemas/google.protobuf.Any"
}
},
"title": "Connect Error",
"additionalProperties": true,
"description": "Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation"
},
"google.protobuf.Any": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"value": {
"type": "string",
"format": "binary"
},
"debug": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true,
"description": "Contains an arbitrary serialized message along with a @type that describes the type of the serialized message."
}
}
},
"security": [],
"tags": [
{
"name": "has_unused_types.FlexService",
"description": "This service tries to flex the different options"
}
]
}
Loading

0 comments on commit 197f4e4

Please sign in to comment.