Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ For engine annotations and the model artifact, read [model/README.md](model/READ
Houses the `Options` data class (every plugin option lives here as a property with KDoc) and the `from(...)` factory that registers the `Annotations` + `google.api` extension registries before parsing the `CodeGeneratorRequest`.
- [`compile/Compiler.kt`](src/main/kotlin/com/engine/protoc/openapi/compile/Compiler.kt) — internal compiler.
`compile()` dispatches to `compileMerged()` or `compileUnmerged()` based on `options.merge`.
- [`compile/`](src/main/kotlin/com/engine/protoc/openapi/compile) — supporting builders and indices: `SchemaBuilder`, `PathsBuilder`, `MessageIndex`, `EnumIndex`, `RpcIndex`, `SchemaKeyResolver`, plus the `json/` serializers.
- [`compile/`](src/main/kotlin/com/engine/protoc/openapi/compile) — supporting builders and indices: `SchemaBuilder`, `PathsBuilder`, `Descriptions` (every emitted description/summary/title: reference-link resolution and plain-text scrubbing), `MessageIndex`, `EnumIndex`, `RpcIndex`, `SchemaKeyResolver`, plus the `json/` serializers.

## Working Reminders

Expand Down
58 changes: 30 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,33 +96,35 @@ Click the option name to jump to its KDoc for full semantics, precedence rules,

| name | type | default | summary |
|---|---|---|---|
| [`alwaysPrintPrimitiveFields`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L266) | boolean | `false` | Add every non-repeated, non-message field to `required`. Pair with Envoy's `always_print_primitive_fields`. |
| [`autoMapping`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L251) | boolean | `false` | Auto-map gRPC methods without a `google.api.http` annotation to `POST /<package>.<ServiceName>/<MethodName>`. Mirrors Envoy's `auto_mapping`. |
| [`autoTagServices`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L116) | boolean | `false` | Tag every operation with its enclosing service name and emit a top-level `tags` entry per service using the service's proto comment as the description. |
| [`convertGrpcStatus`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L309) | boolean | `false` | Add a `"default"` response entry to every operation whose JSON body is an inline `google.rpc.Status` schema. Mirrors Envoy's `convert_grpc_status`. |
| [`enumValueFormat`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L232) | enum | `CANONICAL` | How enum values are written into OAS `enum` arrays: `CANONICAL`, `NUMERIC_VALUE`, or `LOWER_CASE`. Pair with Envoy's `always_print_enums_as_ints` / `case_insensitive_enum_parsing`. |
| [`inlineEnums`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L180) | boolean | `false` | Emit enum values inline at every reference instead of as a shared `$ref` in `components/schemas`. |
| [`inlineRequestSchemas`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L198) | boolean | `true` | Global default for inlining each RPC's request body schema at the use site. Per-method `inline_request` annotation overrides. |
| [`inlineResponseSchemas`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L210) | boolean | `true` | Global default for inlining each RPC's response body schema at the use site. Per-method `inline_response` annotation overrides. |
| [`logFile`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L365) | string | — | File path the SLF4J binding writes records to. When unset, records go to standard error. |
| [`logLevel`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L353) | enum | `ERROR` | SLF4J threshold (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`) applied to every logger the plugin and its dependencies create. |
| [`merge`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L36) | boolean | `false` | Combine every service across every target file into a single OpenAPI document instead of one document per service. |
| [`outputFormat`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L99) | enum | `JSON` | Serialization format of generated documents: `JSON` (default) or `YAML`. |
| [`preserveProtoFieldNames`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L285) | boolean | `false` | Use raw proto field names (e.g. `my_field`) as schema property keys instead of `json_name` or lowerCamelCase. Pair with Envoy's `preserve_proto_field_names`. |
| [`referenceLinkTarget`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L439) | enum | `NONE` | Renderer dialect that CommonMark reference links (`[Type]`, `[Service.Method]`) in `description` fields resolve to: `NONE` (default — resolution off, brackets untouched), `SWAGGER_UI` (operations + tags), or `REDOC` (operations + tags + generated schema sections). When resolution is enabled, unresolved references are stripped of their brackets and rendered as an inline code span (`` `Property` ``), with a warning. |
| [`schemaNamespaceCasing`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L145) | enum | `NONE` | Case transformation applied to package segments of a namespaced schema key. `NONE`, `CAPITALIZED`, or `UPPER_CASE`. |
| [`schemaNamespaceSeparator`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L136) | enum | `NONE` | Separator placed between package segments of a namespaced schema key. `NONE`, `UNDERSCORE`, `DASH`, or `DOT`. |
| [`schemaNamespaceStrategy`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L129) | enum | `NONE` | Controls which package segments are prepended to schema keys in `components/schemas`. `NONE`, `FULL_PACKAGE`, or `SIMPLIFIED_PACKAGE`. |
| [`schemaNamespaceVersionExtraction`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L156) | boolean | `false` | Move package segments that look like proto API version identifiers (e.g. `v1`, `v2beta1`) to the end of the schema key. |
| [`serviceExclude`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L403) | regex | — | Exclude services whose fully-qualified name contains a match, even if they also matched `serviceInclude`. |
| [`serviceInclude`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L390) | regex | `^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*$` | Only include services whose fully-qualified name contains a match. Schemas referenced only by excluded services are also omitted. |
| [`setSchemaTitleToProtoSimpleName`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L169) | boolean | `false` | Add a `"title"` field to each schema in `components/schemas` set to the unqualified proto type name. |
| [`streamNewlineDelimited`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L324) | boolean | `false` | Document server-streaming responses with content-type `application/x-ndjson`. Mirrors Envoy's `stream_newline_delimited`. |
| [`streamSseStyleDelimited`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L339) | boolean | `false` | Document server-streaming responses with content-type `text/event-stream`. Mirrors Envoy's `stream_sse_style_delimited`. Takes precedence over `streamNewlineDelimited`. |
| [`suppressDefaultEnumValues`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L221) | boolean | `false` | Omit enum values whose proto number is `0` (the proto3 default value convention) from all OAS enum value lists. |
| [`validateOutput`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L68) | boolean | `false` | Validate each generated document against the official OAS 3.1.1 schema. Issues are logged at `WARN`. See [`validationErrorsAreFatal`](#L88) to also fail the compile. |
| [`validationErrorsAreFatal`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L88) | boolean | `false` | When `true`, validation issues are also written to the protoc error and fail the compile. No effect unless `validateOutput` is also `true`. |
| [`version`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L54) | string | — | Fallback `info.version` for documents whose annotations do not specify one. |
| [`alwaysPrintPrimitiveFields`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L269) | boolean | `false` | Add every non-repeated, non-message field to `required`. Pair with Envoy's `always_print_primitive_fields`. |
| [`autoMapping`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L254) | boolean | `false` | Auto-map gRPC methods without a `google.api.http` annotation to `POST /<package>.<ServiceName>/<MethodName>`. Mirrors Envoy's `auto_mapping`. |
| [`autoTagServices`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L119) | boolean | `false` | Tag every operation with its enclosing service name and emit a top-level `tags` entry per service using the service's proto comment as the description. |
| [`convertGrpcStatus`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L312) | boolean | `false` | Add a `"default"` response entry to every operation whose JSON body is an inline `google.rpc.Status` schema. Mirrors Envoy's `convert_grpc_status`. |
| [`enumValueFormat`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L235) | enum | `CANONICAL` | How enum values are written into OAS `enum` arrays: `CANONICAL`, `NUMERIC_VALUE`, or `LOWER_CASE`. Pair with Envoy's `always_print_enums_as_ints` / `case_insensitive_enum_parsing`. |
| [`inlineEnums`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L183) | boolean | `false` | Emit enum values inline at every reference instead of as a shared `$ref` in `components/schemas`. |
| [`inlineRequestSchemas`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L201) | boolean | `true` | Global default for inlining each RPC's request body schema at the use site. Per-method `inline_request` annotation overrides. |
| [`inlineResponseSchemas`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L213) | boolean | `true` | Global default for inlining each RPC's response body schema at the use site. Per-method `inline_response` annotation overrides. |
| [`logFile`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L372) | string | — | File path the SLF4J binding writes records to. When unset, records go to standard error. |
| [`logLevel`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L360) | enum | `ERROR` | SLF4J threshold (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`) applied to every logger the plugin and its dependencies create. |
| [`merge`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L39) | boolean | `false` | Combine every service across every target file into a single OpenAPI document instead of one document per service. |
| [`outputFormat`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L102) | enum | `JSON` | Serialization format of generated documents: `JSON` (default) or `YAML`. |
| [`preserveProtoFieldNames`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L288) | boolean | `false` | Use raw proto field names (e.g. `my_field`) as schema property keys instead of `json_name` or lowerCamelCase. Pair with Envoy's `preserve_proto_field_names`. |
| [`referenceLink`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L481) | `<label>=<URL>` (repeatable) | — | Links a reference-link label to an external URL, e.g. `referenceLink=google.rpc.Status=cloud.google.com/...` makes `[Status.details][google.rpc.Status]` a link. A URL with no scheme gets `https://`, since protoc reserves `:` in `--openapi_out`. |
| [`referenceLinkTarget`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L444) | enum | `NONE` | Renderer dialect that resolved reference links point at: `NONE` (default — no links; resolved references render as code), `SWAGGER_UI` (operations + tags), or `REDOC` (operations + tags + generated schema sections). References to elements with no anchor in the document render as code. See `resolveReferenceLinksMode`. |
| [`resolveReferenceLinksMode`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L466) | enum | `FAIL_ON_INVALID` | Resolution of CommonMark reference links (`[Type]`, `[Type.field]`, `[Service.Method]`, `[text][label]`) in every emitted description, summary, and title: `NONE` (text as written), `WARN` (resolve; log unresolved or ambiguous references and render them as code), or `FAIL_ON_INVALID` (as `WARN`, and fail the run listing every bad reference). |
| [`schemaNamespaceCasing`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L148) | enum | `NONE` | Case transformation applied to package segments of a namespaced schema key. `NONE`, `CAPITALIZED`, or `UPPER_CASE`. |
| [`schemaNamespaceSeparator`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L139) | enum | `NONE` | Separator placed between package segments of a namespaced schema key. `NONE`, `UNDERSCORE`, `DASH`, or `DOT`. |
| [`schemaNamespaceStrategy`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L132) | enum | `NONE` | Controls which package segments are prepended to schema keys in `components/schemas`. `NONE`, `FULL_PACKAGE`, or `SIMPLIFIED_PACKAGE`. |
| [`schemaNamespaceVersionExtraction`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L159) | boolean | `false` | Move package segments that look like proto API version identifiers (e.g. `v1`, `v2beta1`) to the end of the schema key. |
| [`serviceExclude`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L410) | regex | — | Exclude services whose fully-qualified name contains a match, even if they also matched `serviceInclude`. |
| [`serviceInclude`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L397) | regex | `^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*$` | Only include services whose fully-qualified name contains a match. Schemas referenced only by excluded services are also omitted. |
| [`setSchemaTitleToProtoSimpleName`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L172) | boolean | `false` | Add a `"title"` field to each schema in `components/schemas` set to the unqualified proto type name. |
| [`streamNewlineDelimited`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L331) | boolean | `false` | Document server-streaming responses with content-type `application/x-ndjson`. Mirrors Envoy's `stream_newline_delimited`. |
| [`streamSseStyleDelimited`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L346) | boolean | `false` | Document server-streaming responses with content-type `text/event-stream`. Mirrors Envoy's `stream_sse_style_delimited`. Takes precedence over `streamNewlineDelimited`. |
| [`suppressDefaultEnumValues`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L224) | boolean | `false` | Omit enum values whose proto number is `0` (the proto3 default value convention) from all OAS enum value lists. |
| [`validateOutput`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L71) | boolean | `false` | Validate each generated document against the official OAS 3.1.1 schema. Issues are logged at `WARN`. See [`validationErrorsAreFatal`](#L91) to also fail the compile. |
| [`validationErrorsAreFatal`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L91) | boolean | `false` | When `true`, validation issues are also written to the protoc error and fail the compile. No effect unless `validateOutput` is also `true`. |
| [`version`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L57) | string | — | Fallback `info.version` for documents whose annotations do not specify one. |

Enum-valued options accept their values case-insensitively.

Expand All @@ -140,7 +142,7 @@ specified in [`google/api/http.proto`](https://github.com/googleapis/googleapis/
| **`"<field_name>"`** | the value of that single top-level field | every other request field not bound to the URL template |

Auto-derived query parameters use the field's JSON name by default (or the snake_case proto name
when [`preserveProtoFieldNames`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L285)
when [`preserveProtoFieldNames`](src/main/kotlin/com/engine/protoc/openapi/ProtocGenOpenAPI.kt#L288)
is set), recurse into nested messages with dotted names (`?address.city=…`), and emit `style=form,
explode=true` for repeated scalars. Repeated message and map fields are skipped with a `WARN` log
since OpenAPI has no faithful query-string representation for them.
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dependencies {

implementation(libs.commonmark)
implementation(libs.engine.protoc.utils)
implementation(libs.engine.protoc.utils.markdown)
implementation(libs.jackson.databind)
implementation(libs.jackson.dataformat.yaml)
implementation(libs.kotlin.reflect)
Expand Down
5 changes: 5 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ Covers single error, multiple errors, optional `grpc_code` (emitted as `x-grpc-c
Demonstrates how the plugin emits inline OpenAPI schemas for structural protobuf well-known types — `Any`, `Struct`, `Value`, `ListValue`, `FieldMask`, and `Empty` — instead of dangling `$ref`s into `components/schemas`.
Covers both first-party fields that carry these types and the canonical regression case where `google.api.HttpBody` is returned by an RPC and its `extensions` field bottoms out in `google.protobuf.Any`.

### [referenceLinkResolution](src/referenceLinkResolution/README.md)

Demonstrates `resolveReferenceLinksMode` and `referenceLink`: reference links in proto comments and annotation text resolve to anchors, code spans, or external URLs, plain-text `summary` and `title` fields are scrubbed of Markdown, and unresolved or ambiguous references fail the run under `FAIL_ON_INVALID`.
Compiles one proto set under `FAIL_ON_INVALID`, `WARN` (Redoc and Swagger UI targets), and `NONE`.

### [referenceLinks](src/referenceLinks/README.md)

Demonstrates the `referenceLinkTarget` option, which rewrites CommonMark reference links (`[Widget]`, `[WidgetService.GetWidget]`) in proto comments into same-document anchors.
Expand Down
3 changes: 3 additions & 0 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ testing {
dependencies {
implementation(projects.protocGenOpenapi)
implementation(projects.protocGenOpenapiModel)
// Option types such as ReferenceLinkMode come from protoc-utils-markdown.
implementation(libs.engine.protoc.utils.markdown)

implementation(testFixtures(project()))

Expand Down Expand Up @@ -76,6 +78,7 @@ testing {
register<JvmTestSuite>("serviceOrdering")
register<JvmTestSuite>("errorResponses")
register<JvmTestSuite>("referenceLinks")
register<JvmTestSuite>("referenceLinkResolution")

/*
* Envoy integration suite: runs Envoy in a container (via testcontainers) and exercises
Expand Down
1 change: 1 addition & 0 deletions examples/src/complete/kotlin/CompleteTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CompleteTest :
val generatedFile = response.fileList
.find { it.name == "engine.protoc.openapi.example.complete.StorefrontService.openapi.yaml" }
.shouldNotBeNull()
GoldenFiles.maybeWriteGolden("complete", generatedFile.name, generatedFile.content)
val doc: JsonNode = mapper.readTree(generatedFile.content)
val expected = CompleteTest::class.java.getResourceAsStream("/engine.protoc.openapi.example.complete.StorefrontService.openapi.yaml").shouldNotBeNull().reader().readText()

Expand Down
Loading
Loading