From 73d5723042b812758310ea5ef661a273f0faa9ed Mon Sep 17 00:00:00 2001 From: Blake Smith Date: Mon, 12 Feb 2024 00:44:13 -0600 Subject: [PATCH] Add `type_names_with_no_package` option to generate schemas without fully qualified package names. (#160) Some automated tools that generate type names, such as jsonschema2pojo need package unqualified type names to generate proper class names. Some JSON schema tools also treat dots as ref fragment path delimiters. --- README.md | 11 +++ internal/converter/converter.go | 3 + internal/converter/converter_test.go | 8 ++ .../testdata/type_names_with_no_package.go | 85 +++++++++++++++++++ internal/converter/types.go | 20 ++++- 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 internal/converter/testdata/type_names_with_no_package.go diff --git a/README.md b/README.md index 84a6eccd..c7a1abbc 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ protoc \ |`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 | +|`type_names_with_no_package`| When generating type names and refs, do not include the full package in the type name | Custom Proto Options @@ -239,6 +240,16 @@ protoc \ --proto_path=internal/converter/testdata/proto internal/converter/testdata/proto/ArrayOfPrimitives.proto ``` +### Generate type names without fully qualified package + +By default, referenced type names will be generated using the fully qualified package and type name. e.g `packageName.TypeName`. +Setting this option will generate type names and their references only as `TypeName` + +```sh +protoc \ +--jsonschema_out=type_names_with_no_package:. \ +--proto_path=internal/converter/testdata/proto internal/converter/testdata/proto/ArrayOfPrimitives.proto +``` Sample protos (for testing) --------------------------- diff --git a/internal/converter/converter.go b/internal/converter/converter.go index d3fd05a7..04064d2f 100644 --- a/internal/converter/converter.go +++ b/internal/converter/converter.go @@ -59,6 +59,7 @@ type ConverterFlags struct { PrefixSchemaFilesWithPackage bool UseJSONFieldnamesOnly bool UseProtoAndJSONFieldNames bool + TypeNamesWithNoPackage bool } // New returns a configured *Converter (defaulting to draft-04 version): @@ -118,6 +119,8 @@ func (c *Converter) parseGeneratorParameters(parameters string) { c.Flags.PrefixSchemaFilesWithPackage = true case "proto_and_json_fieldnames": c.Flags.UseProtoAndJSONFieldNames = true + case "type_names_with_no_package": + c.Flags.TypeNamesWithNoPackage = true } // look for specific message targets diff --git a/internal/converter/converter_test.go b/internal/converter/converter_test.go index 5ad31c6b..02c9df4e 100644 --- a/internal/converter/converter_test.go +++ b/internal/converter/converter_test.go @@ -144,6 +144,14 @@ func configureSampleProtos() map[string]sampleProto { ObjectsToValidateFail: []string{testdata.PayloadMessageFail, testdata.ArrayOfMessagesFail}, ObjectsToValidatePass: []string{testdata.PayloadMessagePass, testdata.ArrayOfMessagesPass}, }, + "TypeNamesWithNoPackage": { + Flags: ConverterFlags{TypeNamesWithNoPackage: true}, + ExpectedJSONSchema: []string{testdata.PayloadMessage, testdata.TypeNamesWithNoPackage}, + FilesToGenerate: []string{"ArrayOfMessages.proto", "PayloadMessage.proto"}, + ProtoFileName: "ArrayOfMessages.proto", + ObjectsToValidateFail: []string{testdata.PayloadMessageFail, testdata.TypeNamesWithNoPackageFail}, + ObjectsToValidatePass: []string{testdata.PayloadMessagePass, testdata.TypeNamesWithNoPackagePass}, + }, "ArrayOfObjects": { Flags: ConverterFlags{AllowNullValues: true}, ExpectedJSONSchema: []string{testdata.ArrayOfObjects}, diff --git a/internal/converter/testdata/type_names_with_no_package.go b/internal/converter/testdata/type_names_with_no_package.go new file mode 100644 index 00000000..73dcdc15 --- /dev/null +++ b/internal/converter/testdata/type_names_with_no_package.go @@ -0,0 +1,85 @@ +package testdata + +const TypeNamesWithNoPackage = `{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/ArrayOfMessages", + "definitions": { + "ArrayOfMessages": { + "properties": { + "description": { + "type": "string" + }, + "payload": { + "items": { + "$ref": "#/definitions/PayloadMessage" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object", + "title": "Array Of Messages" + }, + "PayloadMessage": { + "properties": { + "name": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "rating": { + "type": "number" + }, + "complete": { + "type": "boolean" + }, + "topology": { + "enum": [ + "FLAT", + 0, + "NESTED_OBJECT", + 1, + "NESTED_MESSAGE", + 2, + "ARRAY_OF_TYPE", + 3, + "ARRAY_OF_OBJECT", + 4, + "ARRAY_OF_MESSAGE", + 5 + ], + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "title": "Topology" + } + }, + "additionalProperties": true, + "type": "object", + "title": "Payload Message" + } + } +}` + +const TypeNamesWithNoPackageFail = `{ + "description": "something", + "payload": [ + {"topology": "cruft"} + ] +}` + +const TypeNamesWithNoPackagePass = `{ + "description": "something", + "payload": [ + {"topology": "ARRAY_OF_MESSAGE"} + ] +}` diff --git a/internal/converter/types.go b/internal/converter/types.go index 0b137087..f4a63a72 100644 --- a/internal/converter/types.go +++ b/internal/converter/types.go @@ -405,14 +405,20 @@ func (c *Converter) convertMessageType(curPkg *ProtoPackage, msgDesc *descriptor // Build up a list of JSONSchema type definitions for every message: definitions := jsonschema.Definitions{} - for refmsgDesc, name := range duplicatedMessages { + for refmsgDesc, nameWithPackage := range duplicatedMessages { + var typeName string + if c.Flags.TypeNamesWithNoPackage { + typeName = refmsgDesc.GetName(); + } else { + typeName = nameWithPackage; + } refType, err := c.recursiveConvertMessageType(curPkg, refmsgDesc, "", duplicatedMessages, true) if err != nil { return nil, err } // Add the schema to our definitions: - definitions[name] = refType + definitions[typeName] = refType } // Put together a JSON schema with our discovered definitions, and a $ref for the root type: @@ -568,9 +574,15 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d jsonSchemaType.Properties = orderedmap.New() // Look up references: - if refName, ok := duplicatedMessages[msgDesc]; ok && !ignoreDuplicatedMessages { + if nameWithPackage, ok := duplicatedMessages[msgDesc]; ok && !ignoreDuplicatedMessages { + var typeName string + if c.Flags.TypeNamesWithNoPackage { + typeName = msgDesc.GetName(); + } else { + typeName = nameWithPackage; + } return &jsonschema.Type{ - Ref: fmt.Sprintf("%s%s", c.refPrefix, refName), + Ref: fmt.Sprintf("%s%s", c.refPrefix, typeName), }, nil }