Skip to content

Commit 0d7f6b0

Browse files
feat: support deprecated field for OpenAPI parameters (#6068)
* Add support for deprecated field in openapi params * Add support for 'deprecated' field annotation * Document how to deprecate a parameter * Implement support for deprecated field annotation * Add enable_field_deprecation plugin option * Implement enable_field_deprecation plugin option * Document enable_field_deprecation plugin option
1 parent 8c2b169 commit 0d7f6b0

File tree

10 files changed

+518
-8
lines changed

10 files changed

+518
-8
lines changed

docs/docs/mapping/customizing_openapi_output.md

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,88 @@ definitions:
11141114
type: string
11151115
```
11161116

1117+
### Deprecating parameters
11171118

1118-
{% endraw %}
1119+
With the `enable_field_deprecation` option, OpenAPI parameters will be marked as deprecated based on the protobuf `deprecated` field option. Allowed values are: `true`, `false`.
1120+
1121+
For example, if you are using `buf`:
11191122

1123+
```yaml
1124+
version: v1
1125+
plugins:
1126+
- name: openapiv2
1127+
out: .
1128+
opt:
1129+
- enable_field_deprecation=true
1130+
```
1131+
1132+
or with `protoc`
1133+
1134+
```sh
1135+
protoc --openapiv2_out=. --openapiv2_opt=enable_field_deprecation=true ./path/to/file.proto
1136+
```
1137+
1138+
Input example:
1139+
1140+
```protobuf
1141+
message SearchRequest {
1142+
string legacy_filter = 1 [deprecated = true];
1143+
string query = 2;
1144+
}
1145+
```
1146+
1147+
Output (excerpt):
1148+
1149+
```yaml
1150+
paths:
1151+
/v1/search:
1152+
get:
1153+
parameters:
1154+
- name: legacy_filter
1155+
in: query
1156+
required: false
1157+
type: string
1158+
deprecated: true
1159+
- name: query
1160+
in: query
1161+
required: false
1162+
type: string
1163+
```
1164+
1165+
If you prefer to leave the protobuf definition active, you can use the `field_configuration.deprecated` annotation on the `openapiv2_field` option instead:
1166+
1167+
```protobuf
1168+
message SearchRequest {
1169+
string legacy_filter = 1 [
1170+
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
1171+
description: "Legacy filter syntax. Prefer the new 'query' field."
1172+
field_configuration: { deprecated: true }
1173+
}
1174+
];
1175+
string query = 2;
1176+
}
1177+
```
1178+
1179+
This keeps the protobuf field untouched but still emits a deprecated parameter in the generated spec:
1180+
1181+
```yaml
1182+
paths:
1183+
/v1/search:
1184+
get:
1185+
parameters:
1186+
- name: legacy_filter
1187+
in: query
1188+
required: false
1189+
type: string
1190+
description: Legacy filter syntax. Prefer the new 'query' field.
1191+
deprecated: true
1192+
- name: query
1193+
in: query
1194+
required: false
1195+
type: string
1196+
```
1197+
1198+
If you set both the protobuf `deprecated = true` option and `field_configuration.deprecated`, the OpenAPI parameter is marked as deprecated regardless of the `enable_field_deprecation` option.
1199+
1200+
1201+
{% endraw %}

internal/descriptor/registry.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ type Registry struct {
167167
// enableRpcDeprecation whether to process grpc method's deprecated option
168168
enableRpcDeprecation bool
169169

170+
// enableFieldDeprecation whether to process proto field's deprecated option
171+
enableFieldDeprecation bool
172+
170173
// expandSlashedPathPatterns, if true, for a path parameter carrying a sub-path, described via parameter pattern (i.e.
171174
// the pattern contains forward slashes), this will expand the _pattern_ into the URI and will _replace_ the parameter
172175
// with new path parameters inferred from patterns wildcards.
@@ -915,6 +918,16 @@ func (r *Registry) GetEnableRpcDeprecation() bool {
915918
return r.enableRpcDeprecation
916919
}
917920

921+
// SetEnableFieldDeprecation sets enableFieldDeprecation
922+
func (r *Registry) SetEnableFieldDeprecation(enable bool) {
923+
r.enableFieldDeprecation = enable
924+
}
925+
926+
// GetEnableFieldDeprecation returns enableFieldDeprecation
927+
func (r *Registry) GetEnableFieldDeprecation() bool {
928+
return r.enableFieldDeprecation
929+
}
930+
918931
func (r *Registry) SetExpandSlashedPathPatterns(expandSlashedPathPatterns bool) {
919932
r.expandSlashedPathPatterns = expandSlashedPathPatterns
920933
}

protoc-gen-openapiv2/defs.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def _run_proto_gen_openapi(
7676
use_allof_for_refs,
7777
disable_default_responses,
7878
enable_rpc_deprecation,
79+
enable_field_deprecation,
7980
expand_slashed_path_patterns,
8081
preserve_rpc_order,
8182
generate_x_go_type):
@@ -155,6 +156,9 @@ def _run_proto_gen_openapi(
155156
if enable_rpc_deprecation:
156157
args.add("--openapiv2_opt", "enable_rpc_deprecation=true")
157158

159+
if enable_field_deprecation:
160+
args.add("--openapiv2_opt", "enable_field_deprecation=true")
161+
158162
if expand_slashed_path_patterns:
159163
args.add("--openapiv2_opt", "expand_slashed_path_patterns=true")
160164

@@ -271,6 +275,7 @@ def _proto_gen_openapi_impl(ctx):
271275
use_allof_for_refs = ctx.attr.use_allof_for_refs,
272276
disable_default_responses = ctx.attr.disable_default_responses,
273277
enable_rpc_deprecation = ctx.attr.enable_rpc_deprecation,
278+
enable_field_deprecation = ctx.attr.enable_field_deprecation,
274279
expand_slashed_path_patterns = ctx.attr.expand_slashed_path_patterns,
275280
preserve_rpc_order = ctx.attr.preserve_rpc_order,
276281
generate_x_go_type = ctx.attr.generate_x_go_type,
@@ -434,6 +439,11 @@ protoc_gen_openapiv2 = rule(
434439
mandatory = False,
435440
doc = "whether to process grpc method's deprecated option.",
436441
),
442+
"enable_field_deprecation": attr.bool(
443+
default = False,
444+
mandatory = False,
445+
doc = "whether to process proto field's deprecated option.",
446+
),
437447
"expand_slashed_path_patterns": attr.bool(
438448
default = False,
439449
mandatory = False,

protoc-gen-openapiv2/internal/genopenapi/template.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, pre
345345
}
346346
}
347347

348+
// verify if the field is deprecated, either via proto or annotation
349+
protoDeprecated := field.GetOptions().GetDeprecated() && reg.GetEnableFieldDeprecation()
350+
annotationDeprecated := getFieldConfiguration(reg, field).GetDeprecated()
351+
deprecated := protoDeprecated || annotationDeprecated
352+
348353
param := openapiParameterObject{
349354
Description: desc,
350355
In: "query",
@@ -354,6 +359,7 @@ func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, pre
354359
Format: schema.Format,
355360
Pattern: schema.Pattern,
356361
Required: required,
362+
Deprecated: deprecated,
357363
UniqueItems: schema.UniqueItems,
358364
extensions: schema.extensions,
359365
Enum: schema.Enum,
@@ -1454,18 +1460,26 @@ func renderServices(services []*descriptor.Service, paths *openapiPathsObject, r
14541460
if regExp, ok := pathParamRegexpMap[parameterString]; ok {
14551461
pattern = regExp
14561462
}
1457-
if fc := getFieldConfiguration(reg, parameter.Target); fc != nil {
1463+
fc := getFieldConfiguration(reg, parameter.Target)
1464+
if fc != nil {
14581465
pathParamName := fc.GetPathParamName()
14591466
if pathParamName != "" && pathParamName != parameterString {
14601467
pathParamNames["{"+parameterString+"}"] = "{" + pathParamName + "}"
14611468
parameterString, _, _ = strings.Cut(pathParamName, "=")
14621469
}
14631470
}
1471+
1472+
// verify if the parameter is deprecated, either via proto or annotation
1473+
protoDeprecated := parameter.Target.GetOptions().GetDeprecated() && reg.GetEnableFieldDeprecation()
1474+
annotationDeprecated := fc.GetDeprecated()
1475+
deprecated := protoDeprecated || annotationDeprecated
1476+
14641477
parameters = append(parameters, openapiParameterObject{
14651478
Name: parameterString,
14661479
Description: desc,
14671480
In: "path",
14681481
Required: true,
1482+
Deprecated: deprecated,
14691483
Default: defaultValue,
14701484
// Parameters in gRPC-Gateway can only be strings?
14711485
Type: paramType,

0 commit comments

Comments
 (0)