mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 12:37:48 +00:00
Adding some edge cases to what-changed code
Some specs were blowing things up, some deeper logic was required. Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -5,6 +5,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -160,8 +161,15 @@ func CheckForRemoval[T any](l, r *yaml.Node, label string, changes *[]*Change, b
|
|||||||
//
|
//
|
||||||
// The Change is then added to the slice of []Change[T] instances provided as a pointer.
|
// The Change is then added to the slice of []Change[T] instances provided as a pointer.
|
||||||
func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) {
|
func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) {
|
||||||
if (l == nil || l.Value == "") && r != nil && r.Value != "" {
|
if (l == nil || l.Value == "") && (r != nil && (r.Value != "" || utils.IsNodeArray(r)) || utils.IsNodeMap(r)) {
|
||||||
CreateChange(changes, PropertyAdded, label, l, r, breaking, orig, new)
|
if r != nil {
|
||||||
|
if l != nil && len(l.Content) < len(r.Content) {
|
||||||
|
CreateChange(changes, PropertyAdded, label, l, r, breaking, orig, new)
|
||||||
|
}
|
||||||
|
if l == nil {
|
||||||
|
CreateChange(changes, PropertyAdded, label, l, r, breaking, orig, new)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +182,46 @@ func CheckForAddition[T any](l, r *yaml.Node, label string, changes *[]*Change,
|
|||||||
func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) {
|
func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Change, breaking bool, orig, new T) {
|
||||||
if l != nil && l.Value != "" && r != nil && r.Value != "" && (r.Value != l.Value || r.Tag != l.Tag) {
|
if l != nil && l.Value != "" && r != nil && r.Value != "" && (r.Value != l.Value || r.Tag != l.Tag) {
|
||||||
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l != nil && utils.IsNodeArray(l) && r != nil && !utils.IsNodeArray(r) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l != nil && utils.IsNodeMap(l) && r != nil && !utils.IsNodeMap(r) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l != nil && utils.IsNodeArray(l) && r != nil && utils.IsNodeArray(r) {
|
||||||
|
if len(l.Content) != len(r.Content) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is no way to know how to compare the content of the array, without
|
||||||
|
// rendering the yaml.Node to a string and comparing the string.
|
||||||
|
leftBytes, _ := yaml.Marshal(l)
|
||||||
|
rightBytes, _ := yaml.Marshal(r)
|
||||||
|
|
||||||
|
if string(leftBytes) != string(rightBytes) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l != nil && utils.IsNodeMap(l) && r != nil && utils.IsNodeMap(r) {
|
||||||
|
if len(l.Content) != len(r.Content) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// there is no way to know how to compare the content of the array, without
|
||||||
|
// rendering the yaml.Node to a string and comparing the string.
|
||||||
|
leftBytes, _ := yaml.Marshal(l)
|
||||||
|
rightBytes, _ := yaml.Marshal(r)
|
||||||
|
|
||||||
|
if string(leftBytes) != string(rightBytes) {
|
||||||
|
CreateChange(changes, Modified, label, l, r, breaking, orig, new)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2468,3 +2468,234 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_Schema_AddExamplesArray_AllOf(t *testing.T) {
|
||||||
|
left := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
allOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: [ "a", "b", "c" ]`
|
||||||
|
right := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
allOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: [ "a", "b", "c","d","e"]`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
changes := CompareDocuments(leftDoc, rightDoc)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_Schema_AddExampleMap_AllOf(t *testing.T) {
|
||||||
|
left := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
description: payload thing
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
description: allOf thing
|
||||||
|
example:
|
||||||
|
- name: chicken`
|
||||||
|
right := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
description: payload thing
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
description: allOf thing
|
||||||
|
example:
|
||||||
|
- name: nuggets`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
changes := CompareDocuments(leftDoc, rightDoc)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_Schema_AddExamplesArray(t *testing.T) {
|
||||||
|
left := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: [ "a", "b", "c" ]`
|
||||||
|
right := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: [ "a", "b", "c","d","e"]`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
changes := CompareDocuments(leftDoc, rightDoc)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_Schema_AddExamplesMap(t *testing.T) {
|
||||||
|
left := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
oh: my`
|
||||||
|
right := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SomePayload:
|
||||||
|
type: object
|
||||||
|
oneOf:
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
oh: why`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
changes := CompareDocuments(leftDoc, rightDoc)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_Schema_AddExamples(t *testing.T) {
|
||||||
|
left := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
containerShared:
|
||||||
|
description: Shared properties by request payload and response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
close_time:
|
||||||
|
example: '2020-07-09T00:17:55Z'
|
||||||
|
container_type:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- default
|
||||||
|
- case
|
||||||
|
example: default
|
||||||
|
custom_fields:
|
||||||
|
type:
|
||||||
|
- array
|
||||||
|
- 'null'
|
||||||
|
items:
|
||||||
|
type: object`
|
||||||
|
right := `openapi: 3.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
containerShared:
|
||||||
|
description: Shared properties by request payload and response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
close_time:
|
||||||
|
example: '2020-07-09T00:17:55Z'
|
||||||
|
container_type:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- default
|
||||||
|
- case
|
||||||
|
example: default
|
||||||
|
custom_fields:
|
||||||
|
type:
|
||||||
|
- array
|
||||||
|
- 'null'
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
example:
|
||||||
|
- name: auditedAt
|
||||||
|
source: global
|
||||||
|
dataType: text
|
||||||
|
requiredToResolve: false`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
changes := CompareDocuments(leftDoc, rightDoc)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
containerShared:
|
||||||
|
description: Shared properties by request payload and response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
close_time:
|
||||||
|
description: Date and time (in UTC) when the container was closed.
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
example: '2020-07-09T00:17:55Z'
|
||||||
|
container_type:
|
||||||
|
description: |-
|
||||||
|
The container type. Valid values are 'default' or 'case'. Containers with the 'default'
|
||||||
|
type are events in the user interface.
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- default
|
||||||
|
- case
|
||||||
|
example: default
|
||||||
|
custom_fields:
|
||||||
|
type:
|
||||||
|
- array
|
||||||
|
- 'null'
|
||||||
|
description: |-
|
||||||
|
JSON objects contains key/value pairs for custom container fields. There may be
|
||||||
|
required fields defined in the administration settings. See the Administrator's Guide for
|
||||||
|
details.
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/custom-field'
|
||||||
|
example:
|
||||||
|
- name: auditedAt
|
||||||
|
source: global
|
||||||
|
dataType: text
|
||||||
|
requiredToResolve: false
|
||||||
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user