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

Add vendor extensions support for fields. #190

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ These apply to specifically marked fields, giving you more finely-grained contro

- [ignore](internal/converter/testdata/proto/OptionIgnoredField.proto): Ignore (omit) a specific field
- [required](internal/converter/testdata/proto/OptionRequiredField.proto): Mark a specific field as being REQUIRED
- [vendor_ext](internal/converter/testdata/proto/OptionVendorExtension.proto): Add custom "vendor extension" alongside field definition

### File Options

Expand Down Expand Up @@ -152,6 +153,13 @@ At the moment the following are supported (but export more in the future):
- Pattern


Vendor Extensions
-----------------
JSON schemas support the addition of user-defined fields, commonly referred to as "vendor extensions." These extensions allow for the inclusion of custom keys that JSON schema processors can handle specifically. This feature is particularly beneficial when used in conjunction with OpenAPI specifications. Tools like the openapi-generator can recognize these vendor extensions and incorporate them into the generated code, depending on the capabilities of the specific code generator.

Currently, the option to include these extensions is supported only at the field level.


Examples
--------

Expand Down
5 changes: 5 additions & 0 deletions internal/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ func configureSampleProtos() map[string]sampleProto {
ObjectsToValidateFail: []string{testdata.OptionRequiredMessageFail},
ObjectsToValidatePass: []string{testdata.OptionRequiredMessagePass},
},
"OptionVendorExtension": {
ExpectedJSONSchema: []string{testdata.OptionVendorExtension},
FilesToGenerate: []string{"OptionVendorExtension.proto"},
ProtoFileName: "OptionVendorExtension.proto",
},
"PackagePrefix": {
Flags: ConverterFlags{PrefixSchemaFilesWithPackage: true},
ExpectedJSONSchema: []string{testdata.Timestamp},
Expand Down
22 changes: 22 additions & 0 deletions internal/converter/testdata/option_vendor_extension.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package testdata

const OptionVendorExtension = `{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/OptionVendorExtension",
"definitions": {
"OptionVendorExtension": {
"properties": {
"query": {
"type": "string",
"x-go-custom-tag": "validate:\"required\""
},
"result_per_page": {
"type": "integer"
}
},
"additionalProperties": true,
"type": "object",
"title": "Option Vendor Extension"
}
}
}`
8 changes: 8 additions & 0 deletions internal/converter/testdata/proto/OptionVendorExtension.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";
package samples;
import "options.proto";

message OptionVendorExtension {
string query = 1 [(protoc.gen.jsonschema.field_options).vendor_ext = {key: "x-go-custom-tag" value: 'validate:"required"'}];
int32 result_per_page = 3;
}
26 changes: 22 additions & 4 deletions internal/converter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,9 @@ func (c *Converter) convertMessageType(curPkg *ProtoPackage, msgDesc *descriptor
for refmsgDesc, nameWithPackage := range duplicatedMessages {
var typeName string
if c.Flags.TypeNamesWithNoPackage {
typeName = refmsgDesc.GetName();
typeName = refmsgDesc.GetName()
} else {
typeName = nameWithPackage;
typeName = nameWithPackage
}
refType, err := c.recursiveConvertMessageType(curPkg, refmsgDesc, "", duplicatedMessages, true)
if err != nil {
Expand Down Expand Up @@ -577,9 +577,9 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d
if nameWithPackage, ok := duplicatedMessages[msgDesc]; ok && !ignoreDuplicatedMessages {
var typeName string
if c.Flags.TypeNamesWithNoPackage {
typeName = msgDesc.GetName();
typeName = msgDesc.GetName()
} else {
typeName = nameWithPackage;
typeName = nameWithPackage
}
return &jsonschema.Type{
Ref: fmt.Sprintf("%s%s", c.refPrefix, typeName),
Expand All @@ -606,6 +606,7 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d
c.logger.WithField("message_str", msgDesc.String()).Trace("Converting message")
for _, fieldDesc := range msgDesc.GetField() {

var vendorExtension map[string]interface{}
// Custom field options from protoc-gen-jsonschema:
if opt := proto.GetExtension(fieldDesc.GetOptions(), protoc_gen_jsonschema.E_FieldOptions); opt != nil {
if fieldOptions, ok := opt.(*protoc_gen_jsonschema.FieldOptions); ok {
Expand All @@ -625,6 +626,13 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d
jsonSchemaType.Required = append(jsonSchemaType.Required, fieldDesc.GetName())
}
}

// "Vendor Extension" is passed to the generated json schema:
if fieldOptions.GetVendorExt() != nil {
vendorExt := fieldOptions.GetVendorExt()
c.logger.WithField("field_name", fieldDesc.GetName()).WithField("message_name", msgDesc.GetName()).Debug("Adding vendor extension: {}", vendorExt)
vendorExtension = vendorExtensionToExtras(vendorExt)
}
}
}

Expand All @@ -634,6 +642,7 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d
c.logger.WithError(err).WithField("field_name", fieldDesc.GetName()).WithField("message_name", msgDesc.GetName()).Error("Failed to convert field")
return nil, err
}
recursedJSONSchemaType.Extras = vendorExtension
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:
Expand Down Expand Up @@ -697,6 +706,15 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msgDesc *d
return jsonSchemaType, nil
}

func vendorExtensionToExtras(vendorExt *protoc_gen_jsonschema.VendorExtension) map[string]interface{} {
if vendorExt == nil {
return nil
}
extras := make(map[string]interface{})
extras[vendorExt.GetKey()] = vendorExt.GetValue()
return extras
}

func dedupe(inputStrings []string) []string {
appended := make(map[string]bool)
outputStrings := []string{}
Expand Down
Loading
Loading