diff --git a/Makefile b/Makefile index fe2b3d97..12616e0e 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +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=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 diff --git a/internal/converter/converter_test.go b/internal/converter/converter_test.go index 65665544..c7aadd54 100644 --- a/internal/converter/converter_test.go +++ b/internal/converter/converter_test.go @@ -285,6 +285,11 @@ func configureSampleProtos() map[string]sampleProto { ProtoFileName: "JSONFields.proto", UseJSONFieldnamesOnly: true, }, + "OneOf": { + ExpectedJSONSchema: []string{testdata.OneOf}, + FilesToGenerate: []string{"OneOf.proto"}, + ProtoFileName: "OneOf.proto", + }, } } diff --git a/internal/converter/testdata/message_kind_11.go b/internal/converter/testdata/message_kind_11.go index 6529e54f..65616c3a 100644 --- a/internal/converter/testdata/message_kind_11.go +++ b/internal/converter/testdata/message_kind_11.go @@ -108,6 +108,23 @@ const MessageKind11 = `{ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind2" + ] + }, + { + "required": [ + "kind3" + ] + }, + { + "required": [ + "kind4" + ] + } + ] } ` diff --git a/internal/converter/testdata/message_kind_12.go b/internal/converter/testdata/message_kind_12.go index 0743a51d..3b3f4ada 100644 --- a/internal/converter/testdata/message_kind_12.go +++ b/internal/converter/testdata/message_kind_12.go @@ -7,6 +7,7 @@ const MessageKind12 = `{ "type": "string" }, "f": { + "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "name": { "type": "string" @@ -113,7 +114,24 @@ const MessageKind12 = `{ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind2" + ] + }, + { + "required": [ + "kind3" + ] + }, + { + "required": [ + "kind4" + ] + } + ] }, "kind5": { "properties": { @@ -189,6 +207,23 @@ const MessageKind12 = `{ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind5" + ] + }, + { + "required": [ + "kind6" + ] + }, + { + "required": [ + "kind7" + ] + } + ] } ` diff --git a/internal/converter/testdata/oneof.go b/internal/converter/testdata/oneof.go new file mode 100644 index 00000000..13f523c0 --- /dev/null +++ b/internal/converter/testdata/oneof.go @@ -0,0 +1,39 @@ +package testdata + +const OneOf = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "properties": { + "bar": { + "properties": { + "foo": { + "type": "integer" + } + }, + "additionalProperties": true, + "type": "object" + }, + "baz": { + "properties": { + "foo": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object", + "oneOf": [ + { + "required": [ + "bar" + ] + }, + { + "required": [ + "baz" + ] + } + ] +}` diff --git a/internal/converter/testdata/proto/OneOf.proto b/internal/converter/testdata/proto/OneOf.proto new file mode 100644 index 00000000..df8163a0 --- /dev/null +++ b/internal/converter/testdata/proto/OneOf.proto @@ -0,0 +1,17 @@ +syntax="proto3"; +package samples; + +message OneOf { + oneof choice { + Bar bar = 1; + Baz baz = 2; + } + + message Bar { + int32 foo = 1; + } + + message Baz { + string foo = 1; + } +} diff --git a/internal/converter/types.go b/internal/converter/types.go index 89d1eda2..24ba6985 100644 --- a/internal/converter/types.go +++ b/internal/converter/types.go @@ -491,6 +491,11 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msg *descr } c.logger.WithField("field_name", fieldDesc.GetName()).WithField("type", recursedJSONSchemaType.Type).Trace("Converted field") + // If this field is part of a OneOf declaration then build that here: + if fieldDesc.OneofIndex != nil { + jsonSchemaType.OneOf = append(jsonSchemaType.OneOf, &jsonschema.Type{Required: []string{fieldDesc.GetName()}}) + } + // Figure out which field names we want to use: switch { case c.UseJSONFieldnamesOnly: diff --git a/jsonschemas/MessageKind11.jsonschema b/jsonschemas/MessageKind11.jsonschema index 19dd2f0a..2db73213 100644 --- a/jsonschemas/MessageKind11.jsonschema +++ b/jsonschemas/MessageKind11.jsonschema @@ -106,5 +106,22 @@ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind2" + ] + }, + { + "required": [ + "kind3" + ] + }, + { + "required": [ + "kind4" + ] + } + ] } \ No newline at end of file diff --git a/jsonschemas/MessageKind12.jsonschema b/jsonschemas/MessageKind12.jsonschema index d2c9222d..51c1a7d8 100644 --- a/jsonschemas/MessageKind12.jsonschema +++ b/jsonschemas/MessageKind12.jsonschema @@ -5,6 +5,7 @@ "type": "string" }, "f": { + "$schema": "http://json-schema.org/draft-04/schema#", "properties": { "name": { "type": "string" @@ -111,7 +112,24 @@ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind2" + ] + }, + { + "required": [ + "kind3" + ] + }, + { + "required": [ + "kind4" + ] + } + ] }, "kind5": { "properties": { @@ -187,5 +205,22 @@ } }, "additionalProperties": true, - "type": "object" + "type": "object", + "oneOf": [ + { + "required": [ + "kind5" + ] + }, + { + "required": [ + "kind6" + ] + }, + { + "required": [ + "kind7" + ] + } + ] } \ No newline at end of file diff --git a/jsonschemas/OneOf.jsonschema b/jsonschemas/OneOf.jsonschema new file mode 100644 index 00000000..da8da4ad --- /dev/null +++ b/jsonschemas/OneOf.jsonschema @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "properties": { + "bar": { + "properties": { + "foo": { + "type": "integer" + } + }, + "additionalProperties": true, + "type": "object" + }, + "baz": { + "properties": { + "foo": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object", + "oneOf": [ + { + "required": [ + "bar" + ] + }, + { + "required": [ + "baz" + ] + } + ] +} \ No newline at end of file