Skip to content

Commit ed47525

Browse files
committed
Examples are not breaking changes, not ever.
1 parent bf3556a commit ed47525

12 files changed

Lines changed: 184 additions & 6 deletions

what-changed/model/breaking_rules.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ func buildDefaultRules() *BreakingRulesConfig {
234234
Explode: rule(false, false, false),
235235
Deprecated: rule(false, false, false),
236236
Example: rule(false, false, false),
237+
Examples: rule(false, false, false),
237238
Schema: rule(true, false, true),
238239
Items: rule(true, false, true),
239240
},
@@ -257,6 +258,7 @@ func buildDefaultRules() *BreakingRulesConfig {
257258

258259
MediaType: &MediaTypeRules{
259260
Example: rule(false, false, false),
261+
Examples: rule(false, false, false),
260262
Schema: rule(true, false, true),
261263
ItemSchema: rule(true, false, true),
262264
ItemEncoding: rule(false, false, true),
@@ -276,6 +278,7 @@ func buildDefaultRules() *BreakingRulesConfig {
276278
AllowEmptyValue: rule(true, true, true),
277279
Explode: rule(false, false, false),
278280
Example: rule(false, false, false),
281+
Examples: rule(false, false, false),
279282
Deprecated: rule(false, false, false),
280283
Required: rule(true, true, true),
281284
Schema: rule(true, false, true),

what-changed/model/breaking_rules_model.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ type ParameterRules struct {
8787
Explode *BreakingChangeRule `json:"explode,omitempty" yaml:"explode,omitempty"`
8888
Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
8989
Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"`
90+
Examples *BreakingChangeRule `json:"examples,omitempty" yaml:"examples,omitempty"`
9091
Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"`
9192
Items *BreakingChangeRule `json:"items,omitempty" yaml:"items,omitempty"`
9293
}
@@ -114,6 +115,7 @@ type ResponseRules struct {
114115
// MediaTypeRules defines breaking rules for the Media Type object properties.
115116
type MediaTypeRules struct {
116117
Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"`
118+
Examples *BreakingChangeRule `json:"examples,omitempty" yaml:"examples,omitempty"`
117119
Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"`
118120
ItemSchema *BreakingChangeRule `json:"itemSchema,omitempty" yaml:"itemSchema,omitempty"`
119121
ItemEncoding *BreakingChangeRule `json:"itemEncoding,omitempty" yaml:"itemEncoding,omitempty"`
@@ -135,6 +137,7 @@ type HeaderRules struct {
135137
AllowEmptyValue *BreakingChangeRule `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
136138
Explode *BreakingChangeRule `json:"explode,omitempty" yaml:"explode,omitempty"`
137139
Example *BreakingChangeRule `json:"example,omitempty" yaml:"example,omitempty"`
140+
Examples *BreakingChangeRule `json:"examples,omitempty" yaml:"examples,omitempty"`
138141
Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
139142
Required *BreakingChangeRule `json:"required,omitempty" yaml:"required,omitempty"`
140143
Schema *BreakingChangeRule `json:"schema,omitempty" yaml:"schema,omitempty"`

what-changed/model/breaking_rules_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,25 @@ func TestDefaultBreakingRules_Operation(t *testing.T) {
9696
assert.True(t, *config.Operation.RequestBody.Removed)
9797
}
9898

99+
func TestDefaultBreakingRules_Examples(t *testing.T) {
100+
config := GenerateDefaultBreakingRules()
101+
102+
assert.NotNil(t, config.Parameter.Examples)
103+
assert.False(t, *config.Parameter.Examples.Added)
104+
assert.False(t, *config.Parameter.Examples.Modified)
105+
assert.False(t, *config.Parameter.Examples.Removed)
106+
107+
assert.NotNil(t, config.Header.Examples)
108+
assert.False(t, *config.Header.Examples.Added)
109+
assert.False(t, *config.Header.Examples.Modified)
110+
assert.False(t, *config.Header.Examples.Removed)
111+
112+
assert.NotNil(t, config.MediaType.Examples)
113+
assert.False(t, *config.MediaType.Examples.Added)
114+
assert.False(t, *config.MediaType.Examples.Modified)
115+
assert.False(t, *config.MediaType.Examples.Removed)
116+
}
117+
99118
func TestMerge_NilOverride(t *testing.T) {
100119
ResetDefaultBreakingRules()
101120
defer ResetDefaultBreakingRules()
@@ -280,12 +299,15 @@ func TestGetRule_AllComponents(t *testing.T) {
280299
{"pathItem", "get"},
281300
{"operation", "operationId"},
282301
{"parameter", "name"},
302+
{"parameter", "examples"},
283303
{"requestBody", "required"},
284304
{"responses", "default"},
285305
{"response", "description"},
286306
{"mediaType", "schema"},
307+
{"mediaType", "examples"},
287308
{"encoding", "contentType"},
288309
{"header", "required"},
310+
{"header", "examples"},
289311
{"schema", "type"},
290312
{"discriminator", "propertyName"},
291313
{"xml", "name"},

what-changed/model/comparison_functions.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,16 @@ func CheckMapForChangesWithNilSupport[T any, R any](expLeft, expRight *orderedma
488488
return checkMapForChangesWithNilSupportInternal(expLeft, expRight, changes, label, compareFunc, false, true)
489489
}
490490

491+
// CheckMapForChangesWithNilSupportAndRules checks a left and right low level map for any additions, subtractions
492+
// or modifications, calling compareFunc with nil for added/removed values and using the configured breaking rules
493+
// for the supplied component and property.
494+
func CheckMapForChangesWithNilSupportAndRules[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
495+
changes *[]*Change, label string, compareFunc func(l, r T) R, component, property string,
496+
) map[string]R {
497+
return checkMapForChangesWithNilSupportInternal(expLeft, expRight, changes, label, compareFunc,
498+
BreakingAdded(component, property), BreakingRemoved(component, property))
499+
}
500+
491501
// checkMapForChangesWithNilSupportInternal is the core implementation that calls compareFunc with nil for added/removed items.
492502
func checkMapForChangesWithNilSupportInternal[T any, R any](expLeft, expRight *orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
493503
changes *[]*Change, label string, compareFunc func(l, r T) R,

what-changed/model/components.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,13 @@ func runComparison[T any, R any](l, r *orderedmap.Map[low.KeyReference[string],
228228
result: CheckMapForChanges(l, r, changes, label, compareFunc),
229229
}
230230
return
231+
}
232+
if label == v3.ExamplesLabel {
233+
doneChan <- componentComparison{
234+
prop: label,
235+
result: checkMapForChangesInternal(l, r, changes, label, compareFunc, false, false, false),
236+
}
237+
return
231238
} else {
232239
doneChan <- componentComparison{
233240
prop: label,

what-changed/model/components_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,36 @@ func TestCompareComponents_OpenAPI_Responses_Remove(t *testing.T) {
790790
assert.Equal(t, "indifferent", extChanges.Changes[0].Original)
791791
}
792792

793+
func TestCompareComponents_OpenAPI_Examples_Removed(t *testing.T) {
794+
low.ClearHashCache()
795+
left := `examples:
796+
something:
797+
value: nice example
798+
extra:
799+
value: another example`
800+
801+
right := `examples:
802+
something:
803+
value: nice example`
804+
805+
var lNode, rNode yaml.Node
806+
_ = yaml.Unmarshal([]byte(left), &lNode)
807+
_ = yaml.Unmarshal([]byte(right), &rNode)
808+
809+
var lDoc v3.Components
810+
var rDoc v3.Components
811+
_ = low.BuildModel(lNode.Content[0], &lDoc)
812+
_ = low.BuildModel(rNode.Content[0], &rDoc)
813+
_ = lDoc.Build(context.Background(), lNode.Content[0], nil)
814+
_ = rDoc.Build(context.Background(), rNode.Content[0], nil)
815+
816+
extChanges := CompareComponents(&lDoc, &rDoc)
817+
assert.Equal(t, 1, extChanges.TotalChanges())
818+
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
819+
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
820+
assert.Equal(t, "extra", extChanges.Changes[0].Original)
821+
}
822+
793823
func TestCompareComponents_OpenAPI_Parameters_Equal(t *testing.T) {
794824
low.ClearHashCache()
795825
left := `parameters:

what-changed/model/header.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ func CompareHeaders(l, r any) *HeaderChanges {
282282
}
283283

284284
// examples
285-
hc.ExamplesChanges = CheckMapForChanges(lHeader.Examples.Value, rHeader.Examples.Value,
286-
&changes, v3.ExamplesLabel, CompareExamples)
285+
hc.ExamplesChanges = CheckMapForChangesWithRules(lHeader.Examples.Value, rHeader.Examples.Value,
286+
&changes, v3.ExamplesLabel, CompareExamples, CompHeader, PropExamples)
287287

288288
// content
289289
hc.ContentChanges = CheckMapForChanges(lHeader.Content.Value, rHeader.Content.Value,

what-changed/model/header_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,34 @@ x-beer: yummy`
315315
assert.Len(t, extChanges.GetAllChanges(), 5)
316316
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
317317
}
318+
319+
func TestCompareHeaders_v3_ExamplesRemoved(t *testing.T) {
320+
low.ClearHashCache()
321+
left := `examples:
322+
something:
323+
description: some example
324+
value: nice example
325+
extra:
326+
value: another example`
327+
right := `examples:
328+
something:
329+
description: some example
330+
value: nice example`
331+
332+
var lNode, rNode yaml.Node
333+
_ = yaml.Unmarshal([]byte(left), &lNode)
334+
_ = yaml.Unmarshal([]byte(right), &rNode)
335+
336+
var lDoc v3.Header
337+
var rDoc v3.Header
338+
_ = low.BuildModel(lNode.Content[0], &lDoc)
339+
_ = low.BuildModel(rNode.Content[0], &rDoc)
340+
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
341+
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
342+
343+
extChanges := CompareHeadersV3(&lDoc, &rDoc)
344+
assert.Equal(t, 1, extChanges.TotalChanges())
345+
assert.Len(t, extChanges.GetAllChanges(), 1)
346+
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
347+
assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
348+
}

what-changed/model/media_type.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ func CompareMediaTypes(l, r *v3.MediaType) *MediaTypeChanges {
140140
}
141141

142142
// examples - use nil-aware version so added/removed examples appear in the map for tree rendering
143-
mc.ExampleChanges = CheckMapForChangesWithNilSupport(l.Examples.Value, r.Examples.Value,
144-
&changes, v3.ExamplesLabel, CompareExamples)
143+
mc.ExampleChanges = CheckMapForChangesWithNilSupportAndRules(l.Examples.Value, r.Examples.Value,
144+
&changes, v3.ExamplesLabel, CompareExamples, CompMediaType, PropExamples)
145145

146146
// encoding
147147
mc.EncodingChanges = CheckMapForChanges(l.Encoding.Value, r.Encoding.Value,

what-changed/model/media_type_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,39 @@ example:
138138
assert.Equal(t, v3.ExampleLabel, extChanges.Changes[0].Property)
139139
}
140140

141+
func TestCompareMediaTypes_ExamplesRemoved(t *testing.T) {
142+
left := `schema:
143+
type: string
144+
examples:
145+
exampleOne:
146+
value: yummy coffee
147+
exampleTwo:
148+
value: yummy tea`
149+
150+
right := `schema:
151+
type: string
152+
examples:
153+
exampleOne:
154+
value: yummy coffee`
155+
156+
var lNode, rNode yaml.Node
157+
_ = yaml.Unmarshal([]byte(left), &lNode)
158+
_ = yaml.Unmarshal([]byte(right), &rNode)
159+
160+
var lDoc v3.MediaType
161+
var rDoc v3.MediaType
162+
_ = low.BuildModel(lNode.Content[0], &lDoc)
163+
_ = low.BuildModel(rNode.Content[0], &rDoc)
164+
_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
165+
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
166+
167+
extChanges := CompareMediaTypes(&lDoc, &rDoc)
168+
assert.NotNil(t, extChanges)
169+
assert.Equal(t, 1, extChanges.TotalChanges())
170+
assert.Len(t, extChanges.GetAllChanges(), 1)
171+
assert.Equal(t, 0, extChanges.TotalBreakingChanges())
172+
}
173+
141174
func TestCompareMediaTypes_ExampleChangedToMap(t *testing.T) {
142175
left := `schema:
143176
type: string`

0 commit comments

Comments
 (0)