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

Commit

Permalink
Hiding "oneOf" support behind a flag (#61)
Browse files Browse the repository at this point in the history
* Explicit config struct for flags

* Handling the OneOf flag

* Reverting test-cases protected by the oneOf flag

Co-authored-by: Chrusty <>
  • Loading branch information
chrusty authored Mar 30, 2021
1 parent 4e6ee35 commit dc9f108
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 191 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ samples:
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/Proto2Required.proto || echo "No messages found (Proto2Required.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/Proto2NestedMessage.proto || echo "No messages found (Proto2NestedMessage.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/GoogleValue.proto || echo "No messages found (GoogleValue.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/OneOf.proto || echo "No messages found (OneOf.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=enforce_oneof:jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/OneOf.proto || echo "No messages found (OneOf.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=all_fields_required:jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/Proto2NestedObject.proto || echo "No messages found (Proto2NestedObject.proto)"
@PATH=./bin:$$PATH; protoc -I /usr/include --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/WellKnown.proto || echo "No messages found (WellKnown.proto)"
@PATH=./bin:$$PATH; protoc --jsonschema_out=jsonschemas --proto_path=${PROTO_PATH} ${PROTO_PATH}/NoPackage.proto
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ protoc \ # The protobuf compiler
|`debug`| Enable debug logging |
|`disallow_additional_properties`| Disallow additional properties in schema |
|`disallow_bigints_as_strings`| Disallow big integers as strings |
|`enforce_oneof` | Interpret Proto "oneOf" clauses |
|`json_fieldnames` | Use JSON field names only |
|`prefix_schema_files_with_package`| Prefix the output filename with package |
|`proto_and_json_fieldnames`| Use proto and JSON field names |
Expand Down
32 changes: 20 additions & 12 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ const (

// Converter is everything you need to convert protos to JSONSchemas:
type Converter struct {
Flags ConverterFlags
logger *logrus.Logger
sourceInfo *sourceCodeInfo
messageTargets []string
}

// ConverterFlags control the behaviour of the converter:
type ConverterFlags struct {
AllFieldsRequired bool
AllowNullValues bool
DisallowAdditionalProperties bool
DisallowBigIntsAsStrings bool
EnforceOneOf bool
PrefixSchemaFilesWithPackage bool
UseJSONFieldnamesOnly bool
UseProtoAndJSONFieldnames bool
logger *logrus.Logger
sourceInfo *sourceCodeInfo
messageTargets []string
UseProtoAndJSONFieldNames bool
}

// New returns a configured *Converter:
Expand Down Expand Up @@ -65,21 +71,23 @@ func (c *Converter) parseGeneratorParameters(parameters string) {
for _, parameter := range strings.Split(parameters, ",") {
switch parameter {
case "all_fields_required":
c.AllFieldsRequired = true
c.Flags.AllFieldsRequired = true
case "allow_null_values":
c.AllowNullValues = true
c.Flags.AllowNullValues = true
case "debug":
c.logger.SetLevel(logrus.DebugLevel)
case "disallow_additional_properties":
c.DisallowAdditionalProperties = true
c.Flags.DisallowAdditionalProperties = true
case "disallow_bigints_as_strings":
c.DisallowBigIntsAsStrings = true
c.Flags.DisallowBigIntsAsStrings = true
case "enforce_oneof":
c.Flags.EnforceOneOf = true
case "json_fieldnames":
c.UseJSONFieldnamesOnly = true
c.Flags.UseJSONFieldnamesOnly = true
case "prefix_schema_files_with_package":
c.PrefixSchemaFilesWithPackage = true
c.Flags.PrefixSchemaFilesWithPackage = true
case "proto_and_json_fieldnames":
c.UseProtoAndJSONFieldnames = true
c.Flags.UseProtoAndJSONFieldNames = true
}

// look for specific message targets
Expand Down Expand Up @@ -249,7 +257,7 @@ func (c *Converter) convert(req *plugin.CodeGeneratorRequest) (*plugin.CodeGener
}

func (c *Converter) generateSchemaFilename(file *descriptor.FileDescriptorProto, protoName string) string {
if c.PrefixSchemaFilesWithPackage {
if c.Flags.PrefixSchemaFilesWithPackage {
return fmt.Sprintf("%s/%s.jsonschema", file.GetPackage(), protoName)
}
return fmt.Sprintf("%s.jsonschema", protoName)
Expand Down
76 changes: 27 additions & 49 deletions internal/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,11 @@ const (
)

type sampleProto struct {
AllFieldsRequired bool
AllowNullValues bool
ExpectedJSONSchema []string
FilesToGenerate []string
PrefixSchemaFilesWithPackage bool
ProtoFileName string
UseJSONFieldnamesOnly bool
UseProtoAndJSONFieldNames bool
TargetedMessages []string
Flags ConverterFlags
ExpectedJSONSchema []string
FilesToGenerate []string
ProtoFileName string
TargetedMessages []string
}

func TestGenerateJsonSchema(t *testing.T) {
Expand All @@ -56,11 +52,7 @@ func testConvertSampleProto(t *testing.T, sampleProto sampleProto) {

// Use the logger to make a Converter:
protoConverter := New(logger)
protoConverter.AllFieldsRequired = sampleProto.AllFieldsRequired
protoConverter.AllowNullValues = sampleProto.AllowNullValues
protoConverter.UseJSONFieldnamesOnly = sampleProto.UseJSONFieldnamesOnly
protoConverter.UseProtoAndJSONFieldnames = sampleProto.UseProtoAndJSONFieldNames
protoConverter.PrefixSchemaFilesWithPackage = sampleProto.PrefixSchemaFilesWithPackage
protoConverter.Flags = sampleProto.Flags

// Open the sample proto file:
sampleProtoFileName := fmt.Sprintf("%v/%v", sampleProtoDirectory, sampleProto.ProtoFileName)
Expand Down Expand Up @@ -95,7 +87,7 @@ func testConvertSampleProto(t *testing.T, sampleProto sampleProto) {
}

// Check for the correct prefix:
if protoConverter.PrefixSchemaFilesWithPackage {
if protoConverter.Flags.PrefixSchemaFilesWithPackage {
assert.Contains(t, response.File[0].GetName(), "samples")
} else {
assert.NotContains(t, response.File[0].GetName(), "samples")
Expand All @@ -105,19 +97,18 @@ func testConvertSampleProto(t *testing.T, sampleProto sampleProto) {
func configureSampleProtos() map[string]sampleProto {
return map[string]sampleProto{
"ArrayOfMessages": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.PayloadMessage, testdata.ArrayOfMessages},
FilesToGenerate: []string{"ArrayOfMessages.proto", "PayloadMessage.proto"},
ProtoFileName: "ArrayOfMessages.proto",
},
"ArrayOfObjects": {
AllowNullValues: true,
Flags: ConverterFlags{AllowNullValues: true},
ExpectedJSONSchema: []string{testdata.ArrayOfObjects},
FilesToGenerate: []string{"ArrayOfObjects.proto"},
ProtoFileName: "ArrayOfObjects.proto",
},
"ArrayOfPrimitives": {
AllowNullValues: true,
Flags: ConverterFlags{AllowNullValues: true},
ExpectedJSONSchema: []string{testdata.ArrayOfPrimitives},
FilesToGenerate: []string{"ArrayOfPrimitives.proto"},
ProtoFileName: "ArrayOfPrimitives.proto",
Expand All @@ -128,86 +119,75 @@ func configureSampleProtos() map[string]sampleProto {
ProtoFileName: "BytesPayload.proto",
},
"ArrayOfPrimitivesDouble": {
AllowNullValues: true,
ExpectedJSONSchema: []string{testdata.ArrayOfPrimitivesDouble},
FilesToGenerate: []string{"ArrayOfPrimitives.proto"},
ProtoFileName: "ArrayOfPrimitives.proto",
UseProtoAndJSONFieldNames: true,
Flags: ConverterFlags{
AllowNullValues: true,
UseProtoAndJSONFieldNames: true,
},
ExpectedJSONSchema: []string{testdata.ArrayOfPrimitivesDouble},
FilesToGenerate: []string{"ArrayOfPrimitives.proto"},
ProtoFileName: "ArrayOfPrimitives.proto",
},
"EnumNestedReference": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.EnumNestedReference},
FilesToGenerate: []string{"EnumNestedReference.proto"},
ProtoFileName: "EnumNestedReference.proto",
},
"EnumWithMessage": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.EnumWithMessage},
FilesToGenerate: []string{"EnumWithMessage.proto"},
ProtoFileName: "EnumWithMessage.proto",
},
"EnumImport": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.EnumImport},
FilesToGenerate: []string{"ImportEnum.proto"},
ProtoFileName: "ImportEnum.proto",
},
"EnumCeption": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.PayloadMessage, testdata.ImportedEnum, testdata.EnumCeption},
FilesToGenerate: []string{"Enumception.proto", "PayloadMessage.proto", "ImportedEnum.proto"},
ProtoFileName: "Enumception.proto",
},
"ImportedEnum": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.ImportedEnum},
FilesToGenerate: []string{"ImportedEnum.proto"},
ProtoFileName: "ImportedEnum.proto",
},
"NestedMessage": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.PayloadMessage, testdata.NestedMessage},
FilesToGenerate: []string{"NestedMessage.proto", "PayloadMessage.proto"},
ProtoFileName: "NestedMessage.proto",
},
"NestedObject": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.NestedObject},
FilesToGenerate: []string{"NestedObject.proto"},
ProtoFileName: "NestedObject.proto",
},
"PayloadMessage": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.PayloadMessage},
FilesToGenerate: []string{"PayloadMessage.proto"},
ProtoFileName: "PayloadMessage.proto",
},
"SeveralEnums": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.FirstEnum, testdata.SecondEnum},
FilesToGenerate: []string{"SeveralEnums.proto"},
ProtoFileName: "SeveralEnums.proto",
},
"SeveralMessages": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.FirstMessage, testdata.SecondMessage},
FilesToGenerate: []string{"SeveralMessages.proto"},
ProtoFileName: "SeveralMessages.proto",
},
"ArrayOfEnums": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.ArrayOfEnums},
FilesToGenerate: []string{"ArrayOfEnums.proto"},
ProtoFileName: "ArrayOfEnums.proto",
},
"Maps": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.Maps},
FilesToGenerate: []string{"Maps.proto"},
ProtoFileName: "Maps.proto",
},
"Comments": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.MessageWithComments},
FilesToGenerate: []string{"MessageWithComments.proto"},
ProtoFileName: "MessageWithComments.proto",
Expand Down Expand Up @@ -238,32 +218,29 @@ func configureSampleProtos() map[string]sampleProto {
ProtoFileName: "NoPackage.proto",
},
"PackagePrefix": {
ExpectedJSONSchema: []string{testdata.Timestamp},
FilesToGenerate: []string{"Timestamp.proto"},
ProtoFileName: "Timestamp.proto",
PrefixSchemaFilesWithPackage: true,
Flags: ConverterFlags{PrefixSchemaFilesWithPackage: true},
ExpectedJSONSchema: []string{testdata.Timestamp},
FilesToGenerate: []string{"Timestamp.proto"},
ProtoFileName: "Timestamp.proto",
},
"Proto2Required": {
ExpectedJSONSchema: []string{testdata.Proto2Required},
FilesToGenerate: []string{"Proto2Required.proto"},
ProtoFileName: "Proto2Required.proto",
},
"AllRequired": {
AllFieldsRequired: true,
AllowNullValues: false,
Flags: ConverterFlags{AllFieldsRequired: true},
ExpectedJSONSchema: []string{testdata.PayloadMessage2},
FilesToGenerate: []string{"PayloadMessage2.proto"},
ProtoFileName: "PayloadMessage2.proto",
},
"Proto2NestedMessage": {
AllowNullValues: false,
ExpectedJSONSchema: []string{testdata.Proto2PayloadMessage, testdata.Proto2NestedMessage},
FilesToGenerate: []string{"Proto2PayloadMessage.proto", "Proto2NestedMessage.proto"},
ProtoFileName: "Proto2NestedMessage.proto",
},
"Proto2NestedObject": {
AllFieldsRequired: true,
AllowNullValues: false,
Flags: ConverterFlags{AllFieldsRequired: true},
ExpectedJSONSchema: []string{testdata.Proto2NestedObject},
FilesToGenerate: []string{"Proto2NestedObject.proto"},
ProtoFileName: "Proto2NestedObject.proto",
Expand All @@ -280,12 +257,13 @@ func configureSampleProtos() map[string]sampleProto {
ProtoFileName: "GoogleValue.proto",
},
"JSONFields": {
ExpectedJSONSchema: []string{testdata.JSONFields},
FilesToGenerate: []string{"JSONFields.proto"},
ProtoFileName: "JSONFields.proto",
UseJSONFieldnamesOnly: true,
Flags: ConverterFlags{UseJSONFieldnamesOnly: true},
ExpectedJSONSchema: []string{testdata.JSONFields},
FilesToGenerate: []string{"JSONFields.proto"},
ProtoFileName: "JSONFields.proto",
},
"OneOf": {
Flags: ConverterFlags{EnforceOneOf: true},
ExpectedJSONSchema: []string{testdata.OneOf},
FilesToGenerate: []string{"OneOf.proto"},
ProtoFileName: "OneOf.proto",
Expand Down
19 changes: 1 addition & 18 deletions internal/converter/testdata/message_kind_11.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,6 @@ const MessageKind11 = `{
}
},
"additionalProperties": true,
"type": "object",
"oneOf": [
{
"required": [
"kind2"
]
},
{
"required": [
"kind3"
]
},
{
"required": [
"kind4"
]
}
]
"type": "object"
}
`
39 changes: 2 additions & 37 deletions internal/converter/testdata/message_kind_12.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const MessageKind12 = `{
"type": "string"
},
"f": {
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"name": {
"type": "string"
Expand Down Expand Up @@ -114,24 +113,7 @@ const MessageKind12 = `{
}
},
"additionalProperties": true,
"type": "object",
"oneOf": [
{
"required": [
"kind2"
]
},
{
"required": [
"kind3"
]
},
{
"required": [
"kind4"
]
}
]
"type": "object"
},
"kind5": {
"properties": {
Expand Down Expand Up @@ -207,23 +189,6 @@ const MessageKind12 = `{
}
},
"additionalProperties": true,
"type": "object",
"oneOf": [
{
"required": [
"kind5"
]
},
{
"required": [
"kind6"
]
},
{
"required": [
"kind7"
]
}
]
"type": "object"
}
`
Loading

0 comments on commit dc9f108

Please sign in to comment.