Skip to content

Commit 710631a

Browse files
author
Daniil Kukharau
authored
Minor refactoring/Add Readme (#9)
* Simplify interface of generated functionality * Add README
1 parent d9d2dfd commit 710631a

File tree

6 files changed

+241
-20
lines changed

6 files changed

+241
-20
lines changed

Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ SRCPATH := $(patsubst %/,%,$(GOPATH))/src
33

44
default: options install
55

6-
76
.PHONY: options
87
options:
98
protoc -I. -I$(SRCPATH) -I./vendor \
@@ -14,9 +13,11 @@ options:
1413
install:
1514
go install
1615

17-
test: ./example/*
18-
echo ${SRCPATH}
16+
.PHONY: example
17+
example: default
1918
protoc -I. -I${SRCPATH} -I./vendor -I./vendor/github.com/grpc-ecosystem/grpc-gateway --atlas-query-validate_out=. example/example.proto
19+
20+
test: example
2021
go test ./...
2122

2223
.PHONY: vendor

README.md

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,157 @@
1-
# protoc-gen-atlas-query-validate
1+
# protoc-gen-atlas-query-validate
2+
3+
### Purpose
4+
5+
A [protobuf](https://developers.google.com/protocol-buffers/) compiler plugin
6+
designed to simplify validation of [Atlas](https://github.com/infobloxopen/atlas-app-toolkit)
7+
[gRPC](https://grpc.io/) List [query](https://github.com/infobloxopen/atlas-app-toolkit/blob/master/query/collection_operators.proto) parameters
8+
by generating .pb.atlas.query.validate.go files with validation rules and functions.
9+
Currently query.Filtering and query.Sorting are supported for validation.
10+
11+
### Prerequisites
12+
13+
#### 1. Protobuf Compiler
14+
15+
The protobuf compiler (protoc) is required.
16+
17+
[Official instructions](https://github.com/google/protobuf#protocol-compiler-installation)
18+
19+
[Abbreviated version](https://github.com/grpc-ecosystem/grpc-gateway#installation)
20+
21+
#### 2. Golang Protobuf Code Generator
22+
23+
Get the golang protobuf code generator:
24+
25+
```
26+
go get -u github.com/golang/protobuf/protoc-gen-go
27+
```
28+
29+
#### 3. Vendored Dependencies
30+
31+
Retrieve and install the vendored dependencies for this project with [dep](https://github.com/golang/dep):
32+
33+
```
34+
dep ensure
35+
```
36+
37+
### Installation
38+
39+
To use this tool, install it from code with `make install`, `go install` directly,
40+
or `go get github.com/infobloxopen/protoc-gen-atlas-query-validate`.
41+
42+
### Usage
43+
44+
Once installed, the `atlas-query-validate_out=.` or `--atlas-query-validate_out=${GOPATH}src`
45+
option can be specified in a protoc command to generate the .pb.atlas.query.validate.go files.
46+
47+
#### Validation rules
48+
49+
Validation rules are generated for each gRPC method containing query.Filtering and/or query.Sorting in it's request message
50+
based on the message included in the method's response message(will call it *resource message* for the rest of the document).
51+
52+
By default all fields of *resource message` are allowed for filtering/sorting.
53+
54+
The list of allowed filtering operators and filtering value type/condition type depends on the *filter_type* of the field,
55+
which is either taken from `(atlas.query.validate).filter_type` proto field option or is computed by the
56+
plugin based on the field type if the option is not supplied. Currently *filter_type* can be either STRING or NUMBER.
57+
The following table shows what is allowed for each *filter_type*:
58+
59+
| | STRING | NUMBER |
60+
|-------------------------------------|--------|--------|
61+
| **Filtering operators** | EQ, MATCH, GT, GE, LT, LE | EQ, GT, GE, LT, LE |
62+
| **Filtering value type/condition type** | String, null/StringCondition, NullCondition | Number, null/NumberCondition, NullCondition |
63+
64+
The next table shows how *filter_type* is computed from a proto field type:
65+
66+
| Proto field type | filter_type |
67+
|-----------------------------|---------------|
68+
| enum | STRING |
69+
| string | STRING |
70+
| double | NUMBER |
71+
| float | NUMBER |
72+
| int32 | NUMBER |
73+
| int64 | NUMBER |
74+
| sint32 | NUMBER |
75+
| sint64 | NUMBER |
76+
| uint32 | NUMBER |
77+
| uint64 | NUMBER |
78+
| google.protobuf.StringValue | STRING |
79+
| google.protobuf.DoubleValue | NUMBER |
80+
| google.protobuf.FloatValue | NUMBER |
81+
| google.protobuf.Int32Value | NUMBER |
82+
| google.protobuf.Int64Value | NUMBER |
83+
| google.protobuf.UInt32Value | NUMBER |
84+
| google.protobuf.UInt64Value | NUMBER |
85+
| google.protobuf.Timestamp | STRING |
86+
| gorm.types.UUID | STRING |
87+
| gorm.types.UUIDValue | STRING |
88+
| atlas.rpc.Identifier | STRING |
89+
| gorm.types.InetValue | STRING |
90+
91+
#### Validation functions
92+
93+
Validation functions are the entry points for the generated functionality.
94+
The following validation functions are generated:
95+
96+
```golang
97+
func {Proto_file_name}ValidateFiltering(methodName string, f *query.Filtering) error
98+
```
99+
100+
```golang
101+
func {Proto_file_name}ValidateSorting(methodName string, s *query.Sorting) error
102+
```
103+
Not nil `error` is returned by the functions if validation is not passed.
104+
105+
106+
### Customization
107+
108+
Currently only field-level proto options are supported as customization means. We're planning to add method-level options which will override
109+
field-level options in order to support different validation rules for List methods having the same *resource message*.
110+
111+
* In order to disable sorting for a field set `(atlas.query.validate).disable_sorting` option to `true`.
112+
```golang
113+
bool on_vacation = 3 [(atlas.query.validate).disable_sorting = true];
114+
```
115+
116+
* In order to customize the list of allowed filtering operators pass either a set of `(atlas.query.validate).allow` or
117+
a set of `(atlas.query.validate).deny` options.
118+
- In case of using `(atlas.query.validate).allow` only specified filtering operators are allowed:
119+
```golang
120+
string first_name = 1 [(atlas.query.validate) = {allow: MATCH, allow: EQ}];
121+
```
122+
- In case of using `(atlas.query.validate).deny` all appropriate(for the field type) filtering operators except specified ones are allowed:
123+
```golang
124+
string first_name = 1 [(atlas.query.validate) = {deny: GT, deny: GE, deny: LT, deny: LE}];
125+
```
126+
* In order to change default *filter_type* for a field pass `(atlas.query.validate).filter_type` option.
127+
```golang
128+
CustomType custom_type_string = 10 [(atlas.query.validate) = {filter_type: STRING}];
129+
```
130+
* In order to enable filtering/sorting by nested fields set `(atlas.query.validate).enable_nested_fields` option to true
131+
on the field of a message type.
132+
133+
```golang
134+
message User {
135+
Address home_address = 11 [(atlas.query.validate) = {enable_nested_fields: true}];
136+
Address work_address = 12;
137+
}
138+
139+
message Address {
140+
string city = 1 [(atlas.query.validate) = {allow: EQ, disable_sorting: true}];
141+
string country = 2;
142+
}
143+
```
144+
145+
146+
### Examples
147+
148+
The best way to get started with the plugin is to check out our [example](example/example.proto).
149+
Example .proto files and generated .pb.atlas.query.validate.go demonstrate most of the use cases of the plugin.
150+
151+
Running `make example` will recompile all these test proto files, if you want
152+
to test the effects of changing the options and fields.
153+
154+
### Limitations
155+
156+
This project is currently in development, and is expected to undergo "breaking"
157+
(and fixing) changes

example/example.pb.atlas.query.validate.go

Lines changed: 34 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/example_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"testing"
55

66
"github.com/infobloxopen/atlas-app-toolkit/query"
7-
"github.com/infobloxopen/protoc-gen-atlas-query-validate/options"
87
)
98

109
func TestFilteringPermissionsValidation(t *testing.T) {
@@ -40,7 +39,7 @@ func TestFilteringPermissionsValidation(t *testing.T) {
4039
if err != nil {
4140
t.Fatalf("Invalid filtering data '%s'", test.Query)
4241
}
43-
err = options.ValidateFiltering(f, ExampleMessagesRequireQueryValidation["User"])
42+
err = ExampleValidateFiltering("/example.TestService/List", f)
4443
if err != nil {
4544
if test.Err == false {
4645
t.Errorf("Unexpected error for %s query: %s", test.Query, err)
@@ -76,7 +75,7 @@ func TestSortingPermissionsValidation(t *testing.T) {
7675
if err != nil {
7776
t.Fatalf("Invalid sorting data '%s'", test.Query)
7877
}
79-
err = options.ValidateSorting(s, ExampleMessagesRequireQueryValidation["User"])
78+
err = ExampleValidateSorting("/example.TestService/List", s)
8079
if err != nil {
8180
if test.Err == false {
8281
t.Errorf("Unexpected error for %s query: %s", test.Query, err)

plugin/imports.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ func CleanImports(pFileText *string) *string {
4141
// GenerateImports writes out required imports for the generated files
4242
func (p *QueryValidatePlugin) GenerateImports(file *generator.FileDescriptor) {
4343
p.PrintImport("options", "github.com/infobloxopen/protoc-gen-atlas-query-validate/options")
44+
p.PrintImport("query", "github.com/infobloxopen/atlas-app-toolkit/query")
4445
}

plugin/plugin.go

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ import (
1414
)
1515

1616
const (
17-
filtering = ".infoblox.api.Filtering"
18-
sorting = ".infoblox.api.Sorting"
19-
messagesValidationVarSuffix = "MessagesRequireQueryValidation"
20-
methodFilteringVarSuffix = "MethodsRequireFilteringValidation"
21-
methodSortingVarSuffix = "MethodsRequireSortingValidation"
17+
filtering = ".infoblox.api.Filtering"
18+
sorting = ".infoblox.api.Sorting"
19+
messagesValidationVarSuffix = "MessagesRequireQueryValidation"
20+
methodFilteringVarSuffix = "MethodsRequireFilteringValidation"
21+
methodSortingVarSuffix = "MethodsRequireSortingValidation"
22+
validateFilteringMethodSuffix = "ValidateFiltering"
23+
validateSortingMethodSuffix = "ValidateSorting"
2224

2325
protoTypeTimestamp = ".google.protobuf.Timestamp"
2426
protoTypeUUID = ".gorm.types.UUID"
@@ -41,6 +43,8 @@ type QueryValidatePlugin struct {
4143
messagesValidationVarName string
4244
requiredFilteringValidationVarName string
4345
requiredSortingValidationVarName string
46+
validateFilteringMethodName string
47+
validateSortingMethodName string
4448
}
4549

4650
func (p *QueryValidatePlugin) setFile(file *generator.FileDescriptor) {
@@ -51,6 +55,8 @@ func (p *QueryValidatePlugin) setFile(file *generator.FileDescriptor) {
5155
p.messagesValidationVarName = generator.CamelCase(strings.TrimSuffix(baseFileName, filepath.Ext(baseFileName)) + messagesValidationVarSuffix)
5256
p.requiredFilteringValidationVarName = generator.CamelCase(strings.TrimSuffix(baseFileName, filepath.Ext(baseFileName)) + methodFilteringVarSuffix)
5357
p.requiredSortingValidationVarName = generator.CamelCase(strings.TrimSuffix(baseFileName, filepath.Ext(baseFileName)) + methodSortingVarSuffix)
58+
p.validateFilteringMethodName = generator.CamelCase(strings.TrimSuffix(baseFileName, filepath.Ext(baseFileName)) + validateFilteringMethodSuffix)
59+
p.validateSortingMethodName = generator.CamelCase(strings.TrimSuffix(baseFileName, filepath.Ext(baseFileName)) + validateSortingMethodSuffix)
5460
}
5561

5662
// Name identifies the plugin
@@ -69,6 +75,8 @@ func (p *QueryValidatePlugin) Init(g *generator.Generator) {
6975
func (p *QueryValidatePlugin) Generate(file *generator.FileDescriptor) {
7076
p.setFile(file)
7177
p.genValidationData()
78+
p.genValidateFiltering()
79+
p.genValidateSorting()
7280
}
7381

7482
func (p *QueryValidatePlugin) genValidationData() {
@@ -323,6 +331,36 @@ func (p *QueryValidatePlugin) getDenyRules(field *descriptor.FieldDescriptorProt
323331
return res
324332
}
325333

334+
func (p *QueryValidatePlugin) genValidateFiltering() {
335+
p.P(`func `, p.validateFilteringMethodName, `(methodName string, f *query.Filtering) error {`)
336+
p.P(`objName, ok := `, p.requiredFilteringValidationVarName, `[methodName]`)
337+
p.P(`if !ok {`)
338+
p.P(`return nil`)
339+
p.P(`}`)
340+
p.P(`var info map[string]options.FilteringOption`)
341+
p.P(`info, ok = `, p.messagesValidationVarName, `[objName]`)
342+
p.P(`if !ok {`)
343+
p.P(`return nil`)
344+
p.P(`}`)
345+
p.P(`return options.ValidateFiltering(f, info)`)
346+
p.P(`}`)
347+
}
348+
349+
func (p *QueryValidatePlugin) genValidateSorting() {
350+
p.P(`func `, p.validateSortingMethodName, `(methodName string, s *query.Sorting) error {`)
351+
p.P(`objName, ok := `, p.requiredSortingValidationVarName, `[methodName]`)
352+
p.P(`if !ok {`)
353+
p.P(`return nil`)
354+
p.P(`}`)
355+
p.P(`var info map[string]options.FilteringOption`)
356+
p.P(`info, ok = `, p.messagesValidationVarName, `[objName]`)
357+
p.P(`if !ok {`)
358+
p.P(`return nil`)
359+
p.P(`}`)
360+
p.P(`return options.ValidateSorting(s, info)`)
361+
p.P(`}`)
362+
}
363+
326364
func getQueryValidationOptions(field *descriptor.FieldDescriptorProto) *options.QueryValidate {
327365
if field.Options == nil {
328366
return nil

0 commit comments

Comments
 (0)