diff --git a/internal/converter/fixtures/output/protovalidate.openapi.json b/internal/converter/fixtures/output/protovalidate.openapi.json index 1a30257..2a737c6 100644 --- a/internal/converter/fixtures/output/protovalidate.openapi.json +++ b/internal/converter/fixtures/output/protovalidate.openapi.json @@ -1947,21 +1947,24 @@ "items": { "type": "string", "title": "repeated_min_items" - } + }, + "minItems": 2 }, "repeatedMaxItems": { "type": "array", "items": { "type": "string", "title": "repeated_max_items" - } + }, + "maxItems": 3 }, "repeatedUnique": { "type": "array", "items": { "type": "string", "title": "repeated_unique" - } + }, + "uniqueItems": true }, "repeatedItems": { "type": "array", @@ -2001,11 +2004,11 @@ "mapValues": { "type": "object", "title": "map_values", - "maxLength": 20, - "minLength": 5, "additionalProperties": { "type": "string", - "title": "value" + "title": "value", + "maxLength": 20, + "minLength": 5 } }, "anyIn": { @@ -2070,11 +2073,6 @@ } }, "title": "LotsOfValidationRules", - "maxLength": 10, - "minLength": 3, - "maxItems": 3, - "minItems": 2, - "uniqueItems": true, "required": [ "requiredField" ], diff --git a/internal/converter/fixtures/output/protovalidate.openapi.yaml b/internal/converter/fixtures/output/protovalidate.openapi.yaml index 7a8aa27..eeb7384 100644 --- a/internal/converter/fixtures/output/protovalidate.openapi.yaml +++ b/internal/converter/fixtures/output/protovalidate.openapi.yaml @@ -1309,16 +1309,19 @@ components: items: type: string title: repeated_min_items + minItems: 2 repeatedMaxItems: type: array items: type: string title: repeated_max_items + maxItems: 3 repeatedUnique: type: array items: type: string title: repeated_unique + uniqueItems: true repeatedItems: type: array items: @@ -1349,11 +1352,11 @@ components: mapValues: type: object title: map_values - maxLength: 20 - minLength: 5 additionalProperties: type: string title: value + maxLength: 20 + minLength: 5 anyIn: $ref: '#/components/schemas/google.protobuf.Any' anyNotIn: @@ -1395,11 +1398,6 @@ components: timestampGtNow: $ref: '#/components/schemas/google.protobuf.Timestamp' title: LotsOfValidationRules - maxLength: 10 - minLength: 3 - maxItems: 3 - minItems: 2 - uniqueItems: true required: - requiredField additionalProperties: false diff --git a/internal/converter/googleapi/paths.go b/internal/converter/googleapi/paths.go index 5489766..1f9d1d3 100644 --- a/internal/converter/googleapi/paths.go +++ b/internal/converter/googleapi/paths.go @@ -128,8 +128,6 @@ func httpRuleToPathMap(opts options.Options, md protoreflect.MethodDescriptor, r } } - slog.Info("partsToParameter", "tokens", tokens, "variables", partsToParameter(tokens)) - // Responses codeMap := orderedmap.New[string, *v3.Response]() diff --git a/internal/converter/protovalidate/schema.go b/internal/converter/protovalidate/schema.go index 0ac8572..651a3a5 100644 --- a/internal/converter/protovalidate/schema.go +++ b/internal/converter/protovalidate/schema.go @@ -1,6 +1,7 @@ package protovalidate import ( + "log/slog" "strconv" "strings" @@ -22,7 +23,7 @@ func SchemaWithMessageAnnotations(schema *base.Schema, desc protoreflect.Message return schema } -func SchemaWithFieldAnnotations(schema *base.Schema, desc protoreflect.FieldDescriptor) *base.Schema { +func SchemaWithFieldAnnotations(schema *base.Schema, desc protoreflect.FieldDescriptor, onlyScalar bool) *base.Schema { r := resolver.DefaultResolver{} constraints := r.ResolveFieldConstraints(desc) if constraints == nil { @@ -35,7 +36,7 @@ func SchemaWithFieldAnnotations(schema *base.Schema, desc protoreflect.FieldDesc parent.Required = append(parent.Required, desc.JSONName()) } } - updateSchemaWithFieldConstraints(schema, constraints) + updateSchemaWithFieldConstraints(schema, constraints, onlyScalar) return schema } @@ -54,7 +55,7 @@ func PopulateParentProperties(parent *base.Schema, desc protoreflect.FieldDescri } //gocyclo:ignore -func updateSchemaWithFieldConstraints(schema *base.Schema, constraints *validate.FieldConstraints) { +func updateSchemaWithFieldConstraints(schema *base.Schema, constraints *validate.FieldConstraints, onlyScalar bool) { if constraints == nil { return } @@ -91,10 +92,6 @@ func updateSchemaWithFieldConstraints(schema *base.Schema, constraints *validate updateSchemaBytes(schema, t.Bytes) case *validate.FieldConstraints_Enum: updateSchemaEnum(schema, t.Enum) - case *validate.FieldConstraints_Repeated: - updateSchemaRepeated(schema, t.Repeated) - case *validate.FieldConstraints_Map: - updateSchemaMap(schema, t.Map) case *validate.FieldConstraints_Any: updateSchemaAny(schema, t.Any) case *validate.FieldConstraints_Duration: @@ -102,6 +99,15 @@ func updateSchemaWithFieldConstraints(schema *base.Schema, constraints *validate case *validate.FieldConstraints_Timestamp: updateSchemaTimestamp(schema, t.Timestamp) } + + if !onlyScalar { + switch t := constraints.Type.(type) { + case *validate.FieldConstraints_Repeated: + updateSchemaRepeated(schema, t.Repeated) + case *validate.FieldConstraints_Map: + updateSchemaMap(schema, t.Map) + } + } } func updateWithCEL(schema *base.Schema, constraints []*validate.Constraint) { @@ -724,22 +730,23 @@ func updateSchemaEnum(schema *base.Schema, constraint *validate.EnumRules) { func updateSchemaRepeated(schema *base.Schema, constraint *validate.RepeatedRules) { if constraint.Unique != nil { - schema.ParentProxy.Schema().UniqueItems = constraint.Unique + schema.UniqueItems = constraint.Unique } if constraint.MaxItems != nil { v := int64(*constraint.MaxItems) - schema.ParentProxy.Schema().MaxItems = &v + schema.MaxItems = &v } if constraint.MinItems != nil { v := int64(*constraint.MinItems) - schema.ParentProxy.Schema().MinItems = &v + schema.MinItems = &v } if constraint.MaxItems != nil { v := int64(*constraint.MaxItems) - schema.ParentProxy.Schema().MaxItems = &v + schema.MaxItems = &v } - if constraint.Items != nil { - updateSchemaWithFieldConstraints(schema, constraint.Items) + slog.Info(schema.Description, "A", schema.Items) + if constraint.Items != nil && schema.Items != nil && schema.Items.A != nil { + updateSchemaWithFieldConstraints(schema.Items.A.Schema(), constraint.Items, false) } } @@ -752,9 +759,10 @@ func updateSchemaMap(schema *base.Schema, constraint *validate.MapRules) { v := int64(*constraint.MaxPairs) schema.MaxProperties = &v } - updateSchemaWithFieldConstraints(schema.ParentProxy.Schema(), constraint.Keys) - if constraint.Values != nil { - updateSchemaWithFieldConstraints(schema, constraint.Values) + // NOTE: Most of these properties don't make sense for object keys + // updateSchemaWithFieldConstraints(schema, constraint.Keys) + if schema.AdditionalProperties != nil && constraint.Values != nil { + updateSchemaWithFieldConstraints(schema.AdditionalProperties.A.Schema(), constraint.Values, false) } } diff --git a/internal/converter/schema/schema.go b/internal/converter/schema/schema.go index 88e6897..6b74d9b 100644 --- a/internal/converter/schema/schema.go +++ b/internal/converter/schema/schema.go @@ -79,6 +79,7 @@ func FieldToSchema(parent *base.SchemaProxy, tt protoreflect.FieldDescriptor) *b root.Title = string(tt.Name()) root.Type = []string{"object"} root.Description = util.FormatComments(tt.ParentFile().SourceLocations().ByDescriptor(tt)) + root = protovalidate.SchemaWithFieldAnnotations(root, tt, false) return base.CreateSchemaProxy(root) } else if tt.IsList() { var itemSchema *base.SchemaProxy @@ -90,11 +91,12 @@ func FieldToSchema(parent *base.SchemaProxy, tt protoreflect.FieldDescriptor) *b default: itemSchema = base.CreateSchemaProxy(ScalarFieldToSchema(parent, tt)) } - s := &base.Schema{ - Type: []string{"array"}, - Items: &base.DynamicValue[*base.SchemaProxy, bool]{A: itemSchema}, + ParentProxy: parent, + Type: []string{"array"}, + Items: &base.DynamicValue[*base.SchemaProxy, bool]{A: itemSchema}, } + s = protovalidate.SchemaWithFieldAnnotations(s, tt, false) return base.CreateSchemaProxy(s) } else { switch tt.Kind() { @@ -142,7 +144,7 @@ func ScalarFieldToSchema(parent *base.SchemaProxy, tt protoreflect.FieldDescript s.Format = "byte" } // Apply Updates from Options - s = protovalidate.SchemaWithFieldAnnotations(s, tt) + s = protovalidate.SchemaWithFieldAnnotations(s, tt, true) s = gnostic.SchemaWithPropertyAnnotations(s, tt) return s }