Skip to content

Commit 74718aa

Browse files
committed
Update the method-options plugin making it configurable
With this change it is possible to configure the list of required method options. For our internal api, we can exclude the validation of `google.api.http` because we don't use it.
1 parent 20581c9 commit 74718aa

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

cmd/buf-plugin-method-options/main.go

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// Package main implements a plugin that checks that all rpc methods set the
2-
// required options (permissions, http).
2+
// required options. The list of options is configurable.
3+
// The default value is:
4+
// - "qdrant.cloud.common.v1.permissions"
5+
// - "google.api.http"
36
//
47
// To use this plugin:
58
//
@@ -11,6 +14,10 @@
1114
// - QDRANT_CLOUD_METHOD_OPTIONS
1215
// plugins:
1316
// - plugin: buf-plugin-method-options
17+
// # Uncomment in case you need to configure the list of method options to validate.
18+
// # options:
19+
// # required_method_options:
20+
// # - "qdrant.cloud.common.v1.permissions"
1421
package main
1522

1623
import (
@@ -19,16 +26,19 @@ import (
1926
"buf.build/go/bufplugin/check"
2027
"buf.build/go/bufplugin/check/checkutil"
2128
"buf.build/go/bufplugin/info"
29+
"buf.build/go/bufplugin/option"
30+
commonv1 "github.com/qdrant/qdrant-cloud-public-api/gen/go/qdrant/cloud/common/v1"
2231
googleann "google.golang.org/genproto/googleapis/api/annotations"
2332
"google.golang.org/protobuf/proto"
2433
"google.golang.org/protobuf/reflect/protoreflect"
2534
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
26-
27-
commonv1 "github.com/qdrant/qdrant-cloud-public-api/gen/go/qdrant/cloud/common/v1"
2835
)
2936

3037
const (
38+
// methodOptionsRuleID is the Rule ID of the methodOptions rule.
3139
methodOptionsRuleID = "QDRANT_CLOUD_METHOD_OPTIONS"
40+
// methodOptionsOptionKey is the option key to override the default list of required options.
41+
methodOptionsOptionKey = "required_method_options"
3242
)
3343

3444
var (
@@ -49,20 +59,37 @@ var (
4959
LicenseURL: "",
5060
},
5161
}
52-
requiredMethodOptionExtensions = []*protoimpl.ExtensionInfo{
53-
commonv1.E_Permissions,
54-
googleann.E_Http,
62+
extensionRegistry = map[string]*protoimpl.ExtensionInfo{
63+
"qdrant.cloud.common.v1.permissions": commonv1.E_Permissions,
64+
"google.api.http": googleann.E_Http,
5565
}
66+
requiredMethodOptionExtensions = []string{"qdrant.cloud.common.v1.permissions", "google.api.http"}
5667
)
5768

5869
func main() {
5970
check.Main(spec)
6071
}
6172

6273
func checkMethodOptions(ctx context.Context, responseWriter check.ResponseWriter, request check.Request, methodDescriptor protoreflect.MethodDescriptor) error {
74+
requiredOptions := requiredMethodOptionExtensions
75+
optionValue, err := option.GetStringSliceValue(request.Options(), methodOptionsOptionKey)
76+
if err != nil {
77+
return err
78+
}
79+
if len(optionValue) > 0 {
80+
requiredOptions = optionValue
81+
}
82+
6383
options := methodDescriptor.Options()
6484

65-
for _, extension := range requiredMethodOptionExtensions {
85+
for _, extensionKey := range requiredOptions {
86+
extension, found := extensionRegistry[extensionKey]
87+
if !found {
88+
responseWriter.AddAnnotation(
89+
check.WithMessagef("extension key %q does not exist", extensionKey),
90+
)
91+
return nil
92+
}
6693
if !proto.HasExtension(options, extension) {
6794
responseWriter.AddAnnotation(
6895
check.WithMessagef("Method %q does not define the %q option", methodDescriptor.FullName(), extension.TypeDescriptor().FullName()),

cmd/buf-plugin-method-options/main_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,60 @@ func TestSimpleFailure(t *testing.T) {
6262
},
6363
}.Run(t)
6464
}
65+
66+
func TestSimpleFailureWithOption(t *testing.T) {
67+
t.Parallel()
68+
checktest.CheckTest{
69+
Request: &checktest.RequestSpec{
70+
Files: &checktest.ProtoFileSpec{
71+
DirPaths: []string{"testdata/simple_failure"},
72+
FilePaths: []string{"simple.proto"},
73+
},
74+
Options: map[string]any{
75+
methodOptionsOptionKey: []string{"qdrant.cloud.common.v1.permissions", "unknown.extension"},
76+
},
77+
},
78+
Spec: spec,
79+
ExpectedAnnotations: []checktest.ExpectedAnnotation{
80+
{
81+
RuleID: methodOptionsRuleID,
82+
Message: "extension key \"unknown.extension\" does not exist",
83+
},
84+
{
85+
RuleID: methodOptionsRuleID,
86+
Message: "Method \"simple.GreeterService.HelloWorld\" does not define the \"qdrant.cloud.common.v1.permissions\" option",
87+
FileLocation: &checktest.ExpectedFileLocation{
88+
FileName: "simple.proto",
89+
StartLine: 9,
90+
StartColumn: 4,
91+
EndLine: 12,
92+
EndColumn: 5,
93+
},
94+
},
95+
},
96+
}.Run(t)
97+
98+
}
99+
100+
func TestSimpleFailureWithOptionWrongKey(t *testing.T) {
101+
t.Parallel()
102+
checktest.CheckTest{
103+
Request: &checktest.RequestSpec{
104+
Files: &checktest.ProtoFileSpec{
105+
DirPaths: []string{"testdata/simple_failure"},
106+
FilePaths: []string{"simple.proto"},
107+
},
108+
Options: map[string]any{
109+
methodOptionsOptionKey: []string{"unknown.extension"},
110+
},
111+
},
112+
Spec: spec,
113+
ExpectedAnnotations: []checktest.ExpectedAnnotation{
114+
{
115+
RuleID: methodOptionsRuleID,
116+
Message: "extension key \"unknown.extension\" does not exist",
117+
},
118+
},
119+
}.Run(t)
120+
121+
}

0 commit comments

Comments
 (0)