Skip to content
This repository was archived by the owner on Jun 20, 2024. It is now read-only.

Commit 5493284

Browse files
schighSteve High
andauthored
added targetted message logic, updated tests and docs (#35)
* added targetted message logic, updated tests and docs * reversed make fmt Co-authored-by: Steve High <[email protected]>
1 parent 4038653 commit 5493284

14 files changed

+885
-25
lines changed

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
default: build
22

3+
.PHONY: build
34
build:
45
@echo "Generating binary (protoc-gen-jsonschema) ..."
56
@mkdir -p bin
67
@go build -o bin/protoc-gen-jsonschema cmd/protoc-gen-jsonschema/main.go
78

9+
.PHONY: fmt
10+
fmt:
11+
@gofmt -s -w .
12+
@goimports -w -local github.com/chrusty/protoc-gen-jsonschema .
13+
14+
.PHONY: install
815
install:
916
@GO111MODULE=on go get github.com/chrusty/protoc-gen-jsonschema/cmd/protoc-gen-jsonschema && go install github.com/chrusty/protoc-gen-jsonschema/cmd/protoc-gen-jsonschema
1017

18+
.PHONY: build_linux
1119
build_linux:
1220
@echo "Generating Linux-amd64 binary (protoc-gen-jsonschema.linux-amd64) ..."
1321
@GOOS=linux GOARCH=amd64 go build -o protoc-gen-jsonschema.linux-amd64
1422

1523
PROTO_PATH ?= "internal/converter/testdata/proto"
24+
.PHONY: samples
1625
samples:
1726
@echo "Generating sample JSON-Schemas ..."
1827
@mkdir -p jsonschemas
@@ -37,6 +46,8 @@ samples:
3746
@PATH=./bin:$$PATH; protoc --jsonschema_out=all_fields_required:jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/Proto2NestedObject.proto || echo "No messages found (Proto2NestedObject.proto)"
3847
@PATH=./bin:$$PATH; protoc -I /usr/include --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/WellKnown.proto || echo "No messages found (WellKnown.proto)"
3948
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/NoPackage.proto
49+
@PATH=./bin:$$PATH; protoc --jsonschema_out=messages=[MessageKind10+MessageKind11+MessageKind12]:jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/TwelveMessages.proto || echo "No messages found (TwelveMessages.proto)"
4050

51+
.PHONY: test
4152
test:
4253
@go test ./... -cover -v

README.md

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,49 @@ Usage
2424
protoc-gen-jsonschema is designed to run like any other proto generator. The following examples show how to use options flags to enable different generator behaviours (more examples in the Makefile too).
2525

2626
* Allow NULL values (by default, JSONSchemas will reject NULL values unless we explicitly allow them):
27-
`protoc --jsonschema_out=allow_null_values:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
27+
28+
```shell script
29+
protoc --jsonschema_out=allow_null_values:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
30+
```
31+
2832
* Disallow additional properties (JSONSchemas won't validate JSON containing extra parameters):
29-
`protoc --jsonschema_out=disallow_additional_properties:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
33+
34+
```shell script
35+
protoc --jsonschema_out=disallow_additional_properties:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
36+
```
37+
3038
* Disallow permissive validation of big-integers as strings (eg scientific notation):
31-
`protoc --jsonschema_out=disallow_bigints_as_strings:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
39+
40+
```shell script
41+
protoc --jsonschema_out=disallow_bigints_as_strings:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
42+
```
43+
3244
* Prefix generated schema files with their package name (as a directory):
33-
`protoc --jsonschema_out=prefix_schema_files_with_package:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
45+
46+
```shell script
47+
protoc --jsonschema_out=prefix_schema_files_with_package:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
48+
```
49+
3450
* Require all fields (because proto3 doesn't accommodate this):
35-
`protoc --jsonschema_out=all_fields_required:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
51+
52+
```shell script
53+
protoc --jsonschema_out=all_fields_required:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
54+
```
55+
3656
* Enable debug logging:
37-
`protoc --jsonschema_out=debug:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto`
3857

58+
```shell script
59+
protoc --jsonschema_out=debug:. --proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
60+
```
61+
62+
* Target _specific_ messages within a proto file:
63+
64+
```shell script
65+
# Generates MessageKind10.jsonschema and MessageKind11.jsonschema
66+
# Use this to generate json schema from proto files with multiple messages
67+
# Separate schema names with '+'
68+
protoc --jsonschema_out=messages=[MessageKind10+MessageKind11]:. --proto_path=testdata/proto testdata/proto/TwelveMessages.proto
69+
```
3970

4071
Sample protos (for testing)
4172
---------------------------
@@ -49,3 +80,4 @@ Sample protos (for testing)
4980
* Proto containing a stand-alone enum: [samples.ImportedEnum](internal/converter/testdata/proto/ImportedEnum.proto)
5081
* Proto containing 2 stand-alone enums: [samples.FirstEnum, samples.SecondEnum](internal/converter/testdata/proto/SeveralEnums.proto)
5182
* Proto containing 2 messages: [samples.FirstMessage, samples.SecondMessage](internal/converter/testdata/proto/SeveralMessages.proto)
83+
* Proto containing 12 messages: [samples.MessageKind1 - samples.MessageKind12](internal/converter/testdata/proto/TwelveMessages.proto)

internal/converter/converter.go

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"io/ioutil"
88
"path"
9+
"regexp"
910
"strings"
1011

1112
"github.com/alecthomas/jsonschema"
@@ -15,6 +16,10 @@ import (
1516
"github.com/sirupsen/logrus"
1617
)
1718

19+
const (
20+
messageDelimiter = "+"
21+
)
22+
1823
// Converter is everything you need to convert protos to JSONSchemas:
1924
type Converter struct {
2025
AllFieldsRequired bool
@@ -25,6 +30,7 @@ type Converter struct {
2530
UseProtoAndJSONFieldnames bool
2631
logger *logrus.Logger
2732
sourceInfo *sourceCodeInfo
33+
messageTargets []string
2834
}
2935

3036
// New returns a configured *Converter:
@@ -50,11 +56,8 @@ func (c *Converter) ConvertFrom(rd io.Reader) (*plugin.CodeGeneratorResponse, er
5056
return nil, err
5157
}
5258

53-
c.parseGeneratorParameters(req.GetParameter())
54-
5559
c.logger.Debug("Converting input")
5660
return c.convert(req)
57-
// return c.debugger(req)
5861
}
5962

6063
func (c *Converter) parseGeneratorParameters(parameters string) {
@@ -75,6 +78,16 @@ func (c *Converter) parseGeneratorParameters(parameters string) {
7578
case "proto_and_json_fieldnames":
7679
c.UseProtoAndJSONFieldnames = true
7780
}
81+
82+
// look for specific message targets
83+
// message types are separated by messageDelimiter "+"
84+
// examples:
85+
// messages=[foo+bar]
86+
// messages=[foo]
87+
rx := regexp.MustCompile(`messages=\[([^\]]+)\]`)
88+
if matches := rx.FindStringSubmatch(parameter); len(matches) == 2 {
89+
c.messageTargets = strings.Split(matches[1], messageDelimiter)
90+
}
7891
}
7992
}
8093

@@ -106,15 +119,17 @@ func (c *Converter) convertEnumType(enum *descriptor.EnumDescriptorProto) (jsons
106119

107120
// Converts a proto file into a JSON-Schema:
108121
func (c *Converter) convertFile(file *descriptor.FileDescriptorProto) ([]*plugin.CodeGeneratorResponse_File, error) {
109-
110122
// Input filename:
111123
protoFileName := path.Base(file.GetName())
112124

113125
// Prepare a list of responses:
114-
response := []*plugin.CodeGeneratorResponse_File{}
126+
var response []*plugin.CodeGeneratorResponse_File
127+
128+
// user wants specific messages
129+
genSpecificMessages := len(c.messageTargets) > 0
115130

116131
// Warn about multiple messages / enums in files:
117-
if len(file.GetMessageType()) > 1 {
132+
if !genSpecificMessages && len(file.GetMessageType()) > 1 {
118133
c.logger.WithField("schemas", len(file.GetMessageType())).WithField("proto_filename", protoFileName).Warn("protoc-gen-jsonschema will create multiple MESSAGE schemas from one proto file")
119134
}
120135
if len(file.GetEnumType()) > 1 {
@@ -155,6 +170,12 @@ func (c *Converter) convertFile(file *descriptor.FileDescriptorProto) ([]*plugin
155170
return nil, fmt.Errorf("no such package found: %s", file.GetPackage())
156171
}
157172
for _, msg := range file.GetMessageType() {
173+
174+
// skip if we are only generating schema for specific messages
175+
if genSpecificMessages && !contains(c.messageTargets, msg.GetName()) {
176+
continue
177+
}
178+
158179
jsonSchemaFileName := c.generateSchemaFilename(file, msg.GetName())
159180
c.logger.WithField("proto_filename", protoFileName).WithField("msg_name", msg.GetName()).WithField("jsonschema_filename", jsonSchemaFileName).Info("Generating JSON-schema for MESSAGE")
160181

@@ -185,6 +206,8 @@ func (c *Converter) convertFile(file *descriptor.FileDescriptorProto) ([]*plugin
185206
}
186207

187208
func (c *Converter) convert(req *plugin.CodeGeneratorRequest) (*plugin.CodeGeneratorResponse, error) {
209+
c.parseGeneratorParameters(req.GetParameter())
210+
188211
generateTargets := make(map[string]bool)
189212
for _, file := range req.GetFileToGenerate() {
190213
generateTargets[file] = true
@@ -197,16 +220,12 @@ func (c *Converter) convert(req *plugin.CodeGeneratorRequest) (*plugin.CodeGener
197220
c.logger.WithField("filename", file.GetName()).Warn("Proto file doesn't specify a package")
198221
continue
199222
}
223+
200224
for _, msg := range file.GetMessageType() {
201225
c.logger.WithField("msg_name", msg.GetName()).WithField("package_name", file.GetPackage()).Debug("Loading a message")
202226
c.registerType(file.Package, msg)
203227
}
204-
}
205-
for _, file := range req.GetProtoFile() {
206-
if file.GetPackage() == "" {
207-
c.logger.WithField("filename", file.GetName()).Warn("Proto file doesn't specify a package")
208-
continue
209-
}
228+
210229
if _, ok := generateTargets[file.GetName()]; ok {
211230
c.logger.WithField("filename", file.GetName()).Debug("Converting file")
212231
converted, err := c.convertFile(file)
@@ -222,7 +241,17 @@ func (c *Converter) convert(req *plugin.CodeGeneratorRequest) (*plugin.CodeGener
222241

223242
func (c *Converter) generateSchemaFilename(file *descriptor.FileDescriptorProto, protoName string) string {
224243
if c.PrefixSchemaFilesWithPackage {
225-
return (fmt.Sprintf("%s/%s.jsonschema", file.GetPackage(), protoName))
244+
return fmt.Sprintf("%s/%s.jsonschema", file.GetPackage(), protoName)
245+
}
246+
return fmt.Sprintf("%s.jsonschema", protoName)
247+
}
248+
249+
func contains(haystack []string, needle string) bool {
250+
for i := 0; i < len(haystack); i++ {
251+
if haystack[i] == needle {
252+
return true
253+
}
226254
}
227-
return (fmt.Sprintf("%s.jsonschema", protoName))
255+
256+
return false
228257
}

internal/converter/converter_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type sampleProto struct {
2828
PrefixSchemaFilesWithPackage bool
2929
ProtoFileName string
3030
UseProtoAndJSONFieldNames bool
31+
TargetedMessages []string
3132
}
3233

3334
func TestGenerateJsonSchema(t *testing.T) {
@@ -65,6 +66,11 @@ func testConvertSampleProto(t *testing.T, sampleProto sampleProto) {
6566
ProtoFile: fileDescriptorSet.GetFile(),
6667
}
6768

69+
if len(sampleProto.TargetedMessages) > 0 {
70+
arg := fmt.Sprintf("messages=[%s]", strings.Join(sampleProto.TargetedMessages, messageDelimiter))
71+
codeGeneratorRequest.Parameter = &arg
72+
}
73+
6874
// Perform the conversion:
6975
response, err := protoConverter.convert(&codeGeneratorRequest)
7076
assert.NoError(t, err, "Unable to convert sample proto file (%v)", sampleProtoFileName)
@@ -73,7 +79,7 @@ func testConvertSampleProto(t *testing.T, sampleProto sampleProto) {
7379
t.Fail()
7480
} else {
7581
for responseFileIndex, responseFile := range response.File {
76-
assert.Equal(t, sampleProto.ExpectedJSONSchema[responseFileIndex], *responseFile.Content, "Incorrect JSON-Schema returned for sample proto file (%v)", sampleProtoFileName)
82+
assert.Equal(t, strings.TrimSpace(sampleProto.ExpectedJSONSchema[responseFileIndex]), *responseFile.Content, "Incorrect JSON-Schema returned for sample proto file (%v)", sampleProtoFileName)
7783
}
7884
}
7985

@@ -233,6 +239,12 @@ func configureSampleProtos() map[string]sampleProto {
233239
FilesToGenerate: []string{"Proto2NestedObject.proto"},
234240
ProtoFileName: "Proto2NestedObject.proto",
235241
},
242+
"TargetedMessages": {
243+
TargetedMessages: []string{"MessageKind10", "MessageKind11", "MessageKind12"},
244+
ExpectedJSONSchema: []string{testdata.MessageKind10, testdata.MessageKind11, testdata.MessageKind12},
245+
FilesToGenerate: []string{"TwelveMessages.proto"},
246+
ProtoFileName: "TwelveMessages.proto",
247+
},
236248
"GoogleValue": {
237249
ExpectedJSONSchema: []string{testdata.GoogleValue},
238250
FilesToGenerate: []string{"GoogleValue.proto"},

internal/converter/proto_package.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type ProtoPackage struct {
1616

1717
func (c *Converter) lookupType(pkg *ProtoPackage, name string) (*descriptor.DescriptorProto, string, bool) {
1818
if strings.HasPrefix(name, ".") {
19-
return c.relativelyLookupType(globalPkg, name[1:len(name)])
19+
return c.relativelyLookupType(globalPkg, name[1:])
2020
}
2121

2222
for ; pkg != nil; pkg = pkg.parent {

internal/converter/testdata/array_of_primitives.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ const ArrayOfPrimitives = `{
101101
]
102102
}`
103103

104-
105104
const ArrayOfPrimitivesDouble = `{
106105
"$schema": "http://json-schema.org/draft-04/schema#",
107106
"properties": {
@@ -215,4 +214,3 @@ const ArrayOfPrimitivesDouble = `{
215214
}
216215
]
217216
}`
218-
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package testdata
2+
3+
const MessageKind10 = `{
4+
"$schema": "http://json-schema.org/draft-04/schema#",
5+
"properties": {
6+
"name": {
7+
"type": "string"
8+
},
9+
"timestamp": {
10+
"type": "string"
11+
},
12+
"id": {
13+
"type": "integer"
14+
},
15+
"rating": {
16+
"type": "number"
17+
},
18+
"complete": {
19+
"type": "boolean"
20+
}
21+
},
22+
"additionalProperties": true,
23+
"type": "object"
24+
}
25+
`

0 commit comments

Comments
 (0)