Skip to content

Commit

Permalink
Add type_names_with_no_package option to generate schemas without f…
Browse files Browse the repository at this point in the history
…ully qualified package names. (chrusty#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.
  • Loading branch information
blakesmith authored Feb 12, 2024
1 parent 11e14e6 commit 73d5723
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 4 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
---------------------------
Expand Down
3 changes: 3 additions & 0 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions internal/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
85 changes: 85 additions & 0 deletions internal/converter/testdata/type_names_with_no_package.go
Original file line number Diff line number Diff line change
@@ -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"}
]
}`
20 changes: 16 additions & 4 deletions internal/converter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
}

Expand Down

0 comments on commit 73d5723

Please sign in to comment.