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

Commit

Permalink
Adding a REQUIRED option (#78)
Browse files Browse the repository at this point in the history
* Adding a REQUIRED option

* Extra docs

Co-authored-by: chris <[email protected]>
  • Loading branch information
chrusty and chris authored Sep 18, 2021
1 parent 14ecdfb commit 6ac766b
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 9 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ protoc \
--proto_path=testdata/proto testdata/proto/ArrayOfPrimitives.proto
```

### Custom option to ignore specific fields

Use the custom 'ignore' option on the fields you'd like to omit from generated schemas, then use the "exclude_ignored_fields" flag with your protoc command.

### Custom option to mark fields as required

Use the custom 'required' option on the fields you'd like to mark as required in generated schemas.


## Sample protos (for testing)

* Proto with a simple (flat) structure: [samples.PayloadMessage](internal/converter/testdata/proto/PayloadMessage.proto)
Expand Down
21 changes: 12 additions & 9 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ import (
)

const (
ignoredFieldOption = "50505:1"
messageDelimiter = "+"
ignoredFieldOption = "50505:1"
messageDelimiter = "+"
requiredFieldOption = "50515:1"
)

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

// ConverterFlags control the behaviour of the converter:
Expand All @@ -46,8 +48,9 @@ type ConverterFlags struct {
// New returns a configured *Converter:
func New(logger *logrus.Logger) *Converter {
return &Converter{
ignoredFieldOption: ignoredFieldOption,
logger: logger,
ignoredFieldOption: ignoredFieldOption,
logger: logger,
requiredFieldOption: requiredFieldOption,
}
}

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 @@ -274,6 +274,11 @@ func configureSampleProtos() map[string]sampleProto {
FilesToGenerate: []string{"options.proto", "HiddenFields.proto"},
ProtoFileName: "HiddenFields.proto",
},
"Proto3Required": {
ExpectedJSONSchema: []string{testdata.Proto3Required},
FilesToGenerate: []string{"options.proto", "Proto3Required.proto"},
ProtoFileName: "Proto3Required.proto",
},
}
}

Expand Down
9 changes: 9 additions & 0 deletions internal/converter/testdata/proto/Proto3Required.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
syntax = "proto3";
package samples;
import "options.proto";

message Proto3Required {
string query = 1 [(protoc.gen.jsonschema.required) = true];
int32 page_number = 2 [deprecated = true, (protoc.gen.jsonschema.required) = true];
int32 result_per_page = 3;
}
22 changes: 22 additions & 0 deletions internal/converter/testdata/proto3_required.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package testdata

const Proto3Required = `{
"$schema": "http://json-schema.org/draft-04/schema#",
"required": [
"query",
"page_number"
],
"properties": {
"query": {
"type": "string"
},
"page_number": {
"type": "integer"
},
"result_per_page": {
"type": "integer"
}
},
"additionalProperties": true,
"type": "object"
}`
7 changes: 7 additions & 0 deletions internal/converter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msg *descr
continue
}

// Convert the field into a JSONSchema type:
recursedJSONSchemaType, err := c.convertField(curPkg, fieldDesc, msg, duplicatedMessages)
if err != nil {
c.logger.WithError(err).WithField("field_name", fieldDesc.GetName()).WithField("message_name", msg.GetName()).Error("Failed to convert field")
Expand Down Expand Up @@ -529,6 +530,12 @@ func (c *Converter) recursiveConvertMessageType(curPkg *ProtoPackage, msg *descr
if fieldDesc.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REQUIRED && fieldDesc.OneofIndex == nil {
jsonSchemaType.Required = append(jsonSchemaType.Required, fieldDesc.GetName())
}

// Look for our custom proto3 "required" field-option (and hope that nobody else happens to be using our number):
if strings.Contains(fieldDesc.GetOptions().String(), c.requiredFieldOption) {
c.logger.WithField("field_name", fieldDesc.GetName()).WithField("message_name", msg.GetName()).Debug("Marking required field")
jsonSchemaType.Required = append(jsonSchemaType.Required, fieldDesc.GetName())
}
}

// Remove empty properties to keep the final output as clean as possible:
Expand Down
1 change: 1 addition & 0 deletions options.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import "google/protobuf/descriptor.proto";

extend google.protobuf.FieldOptions {
bool ignore = 50505;
bool required = 50515;
}

0 comments on commit 6ac766b

Please sign in to comment.