Skip to content

Commit 774c912

Browse files
erraggyclaude
andcommitted
fix(converter): convert exclusiveMaximum/exclusiveMinimum to numeric semantics for OAS 3.1 targets
OAS 2.0 and 3.0 use boolean exclusiveMaximum/exclusiveMinimum to modify the adjacent maximum/minimum bound. OAS 3.1 (JSON Schema 2020-12) changed these to standalone numeric values, dropping maximum/minimum entirely. The converter previously copied the boolean form regardless of target version, producing invalid OAS 3.1 output. Fix all four conversion sites: - convertOAS2ParameterToOAS3: inline parameter type/format schema - convertOAS2FormDataToRequestBody: formData parameter properties - convertOAS2ItemsToSchema: nested items arrays (new c/result/path params) - convertOAS2SchemaToOAS3: component definitions via new recursive fixSchemaExclusiveMinMaxForOAS31 walker (mirrors walkSchemaFeatures) For OAS 3.1 targets, each site now sets exclusiveMaximum to *maximum and clears maximum. For OAS 3.0 targets, boolean form is preserved. Edge case: exclusiveMaximum:true with no maximum (malformed OAS 2.0) emits a SeverityWarning to ConversionResult at all four sites rather than silently dropping the constraint. Closes #358 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e92b2ec commit 774c912

4 files changed

Lines changed: 588 additions & 23 deletions

File tree

converter/helpers.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (c *Converter) convertOAS2ParameterToOAS3(param *parser.Parameter, result *
4949

5050
// Handle schema
5151
if param.Schema != nil {
52-
converted.Schema = c.convertOAS2SchemaToOAS3(param.Schema)
52+
converted.Schema = c.convertOAS2SchemaToOAS3(param.Schema, result.TargetOASVersion, result, path)
5353
} else if param.Type != "" {
5454
// Convert type/format to schema, transferring all OAS 2.0 validation keywords
5555
converted.Schema = &parser.Schema{
@@ -68,13 +68,35 @@ func (c *Converter) convertOAS2ParameterToOAS3(param *parser.Parameter, result *
6868
MultipleOf: param.MultipleOf,
6969
}
7070
if param.ExclusiveMaximum {
71-
converted.Schema.ExclusiveMaximum = true
71+
if c.isOAS31OrLater(result.TargetOASVersion) {
72+
if param.Maximum != nil {
73+
converted.Schema.ExclusiveMaximum = *param.Maximum
74+
converted.Schema.Maximum = nil
75+
} else {
76+
c.addIssueWithContext(result, path,
77+
"Parameter has 'exclusiveMaximum: true' but no 'maximum' value; constraint dropped in OAS 3.1 conversion",
78+
"Add a 'maximum' value to preserve this exclusive boundary in OAS 3.1")
79+
}
80+
} else {
81+
converted.Schema.ExclusiveMaximum = true
82+
}
7283
}
7384
if param.ExclusiveMinimum {
74-
converted.Schema.ExclusiveMinimum = true
85+
if c.isOAS31OrLater(result.TargetOASVersion) {
86+
if param.Minimum != nil {
87+
converted.Schema.ExclusiveMinimum = *param.Minimum
88+
converted.Schema.Minimum = nil
89+
} else {
90+
c.addIssueWithContext(result, path,
91+
"Parameter has 'exclusiveMinimum: true' but no 'minimum' value; constraint dropped in OAS 3.1 conversion",
92+
"Add a 'minimum' value to preserve this exclusive boundary in OAS 3.1")
93+
}
94+
} else {
95+
converted.Schema.ExclusiveMinimum = true
96+
}
7597
}
7698
if param.Items != nil {
77-
converted.Schema.Items = convertOAS2ItemsToSchema(param.Items)
99+
converted.Schema.Items = convertOAS2ItemsToSchema(c, param.Items, result, path+".items")
78100
if param.Items.CollectionFormat != "" && param.Items.CollectionFormat != "csv" {
79101
c.addIssueWithContext(result, path,
80102
fmt.Sprintf("Parameter items uses collectionFormat '%s'", param.Items.CollectionFormat),
@@ -206,7 +228,7 @@ func (c *Converter) resolveHeaderRef(ref string, result *ConversionResult, path
206228
}
207229

208230
// convertOAS2ResponseToOAS3Old converts an OAS 2.0 response to OAS 3.x format
209-
func (c *Converter) convertOAS2ResponseToOAS3Old(response *parser.Response, produces []string) *parser.Response {
231+
func (c *Converter) convertOAS2ResponseToOAS3Old(response *parser.Response, produces []string, targetVersion parser.OASVersion, result *ConversionResult, path string) *parser.Response {
210232
if response == nil {
211233
return nil
212234
}
@@ -228,7 +250,7 @@ func (c *Converter) convertOAS2ResponseToOAS3Old(response *parser.Response, prod
228250

229251
for _, mediaType := range mediaTypes {
230252
converted.Content[mediaType] = &parser.MediaType{
231-
Schema: c.convertOAS2SchemaToOAS3(response.Schema),
253+
Schema: c.convertOAS2SchemaToOAS3(response.Schema, targetVersion, result, path+".schema"),
232254
}
233255
}
234256
}

0 commit comments

Comments
 (0)