mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-09 12:37:49 +00:00
fix!: fixed handling of additionalProperties to handle the bool/json-schema nature better
This commit is contained in:
committed by
quobix
parent
8b90e9a808
commit
8531113e17
@@ -63,7 +63,7 @@ type Schema struct {
|
|||||||
|
|
||||||
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
|
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
|
||||||
// https://github.com/pb33f/libopenapi/issues/118
|
// https://github.com/pb33f/libopenapi/issues/118
|
||||||
UnevaluatedProperties *DynamicValue[*SchemaProxy, *bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
|
UnevaluatedProperties *DynamicValue[*SchemaProxy, bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
|
||||||
|
|
||||||
// in 3.1 Items can be a Schema or a boolean
|
// in 3.1 Items can be a Schema or a boolean
|
||||||
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
@@ -89,7 +89,7 @@ type Schema struct {
|
|||||||
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
||||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
|
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||||
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
AdditionalProperties *DynamicValue[*SchemaProxy, bool] `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
|
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
|
||||||
Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
|
Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
|
||||||
@@ -211,29 +211,22 @@ func NewSchema(schema *base.Schema) *Schema {
|
|||||||
Value: schema.UnevaluatedItems.Value,
|
Value: schema.UnevaluatedItems.Value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// check if unevaluated properties is a schema
|
|
||||||
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() {
|
var unevaluatedProperties *DynamicValue[*SchemaProxy, bool]
|
||||||
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
|
if !schema.UnevaluatedProperties.IsEmpty() {
|
||||||
A: NewSchemaProxy(
|
if schema.UnevaluatedProperties.Value.IsA() {
|
||||||
&lowmodel.NodeReference[*base.SchemaProxy]{
|
unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{
|
||||||
|
A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
||||||
Value: schema.UnevaluatedProperties.Value.A,
|
Value: schema.UnevaluatedProperties.Value.A,
|
||||||
},
|
KeyNode: schema.UnevaluatedProperties.KeyNode,
|
||||||
),
|
}),
|
||||||
N: 0,
|
}
|
||||||
|
} else {
|
||||||
|
unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.UnevaluatedProperties.Value.B}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.UnevaluatedProperties = unevaluatedProperties
|
||||||
// check if unevaluated properties is a bool
|
|
||||||
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsB() {
|
|
||||||
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
|
|
||||||
B: schema.UnevaluatedProperties.Value.B,
|
|
||||||
N: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !schema.UnevaluatedProperties.IsEmpty() {
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Pattern = schema.Pattern.Value
|
s.Pattern = schema.Pattern.Value
|
||||||
s.Format = schema.Format.Value
|
s.Format = schema.Format.Value
|
||||||
@@ -248,19 +241,23 @@ func NewSchema(schema *base.Schema) *Schema {
|
|||||||
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
|
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if schema.AdditionalProperties.Value != nil {
|
|
||||||
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
|
|
||||||
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
|
||||||
KeyNode: schema.AdditionalProperties.KeyNode,
|
|
||||||
ValueNode: schema.AdditionalProperties.ValueNode,
|
|
||||||
Value: addPropSchema,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// TODO: check for slice and map types and unpack correctly.
|
|
||||||
|
|
||||||
s.AdditionalProperties = schema.AdditionalProperties.Value
|
var additionalProperties *DynamicValue[*SchemaProxy, bool]
|
||||||
|
if !schema.AdditionalProperties.IsEmpty() {
|
||||||
|
if schema.AdditionalProperties.Value.IsA() {
|
||||||
|
additionalProperties = &DynamicValue[*SchemaProxy, bool]{
|
||||||
|
A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
|
ValueNode: schema.AdditionalProperties.ValueNode,
|
||||||
|
Value: schema.AdditionalProperties.Value.A,
|
||||||
|
KeyNode: schema.AdditionalProperties.KeyNode,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
additionalProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.AdditionalProperties.Value.B}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.AdditionalProperties = additionalProperties
|
||||||
|
|
||||||
s.Description = schema.Description.Value
|
s.Description = schema.Description.Value
|
||||||
s.Default = schema.Default.Value
|
s.Default = schema.Default.Value
|
||||||
s.Const = schema.Const.Value
|
s.Const = schema.Const.Value
|
||||||
@@ -423,7 +420,8 @@ func NewSchema(schema *base.Schema) *Schema {
|
|||||||
Value: schema.Items.Value.A,
|
Value: schema.Items.Value.A,
|
||||||
KeyNode: schema.Items.KeyNode,
|
KeyNode: schema.Items.KeyNode,
|
||||||
},
|
},
|
||||||
)}
|
),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
|
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ func TestNewSchemaProxyRender(t *testing.T) {
|
|||||||
rice:
|
rice:
|
||||||
$ref: '#/components/schemas/rice'`
|
$ref: '#/components/schemas/rice'`
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewSchemaProxy_WithObject(t *testing.T) {
|
func TestNewSchemaProxy_WithObject(t *testing.T) {
|
||||||
@@ -217,10 +216,7 @@ properties:
|
|||||||
type: number
|
type: number
|
||||||
description: a number
|
description: a number
|
||||||
example: "2"
|
example: "2"
|
||||||
additionalProperties:
|
additionalProperties: false
|
||||||
- chicken
|
|
||||||
- nugget
|
|
||||||
- soup
|
|
||||||
somethingB:
|
somethingB:
|
||||||
type: object
|
type: object
|
||||||
exclusiveMinimum: true
|
exclusiveMinimum: true
|
||||||
@@ -241,8 +237,7 @@ properties:
|
|||||||
attribute: true
|
attribute: true
|
||||||
x-pizza: love
|
x-pizza: love
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
why: yes
|
type: string
|
||||||
thatIs: true
|
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
required:
|
required:
|
||||||
- them
|
- them
|
||||||
@@ -315,12 +310,12 @@ $anchor: anchor`
|
|||||||
assert.Equal(t, "anchor", compiled.Anchor)
|
assert.Equal(t, "anchor", compiled.Anchor)
|
||||||
|
|
||||||
wentLow := compiled.GoLow()
|
wentLow := compiled.GoLow()
|
||||||
assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line)
|
assert.Equal(t, 125, wentLow.AdditionalProperties.ValueNode.Line)
|
||||||
assert.NotNil(t, compiled.GoLowUntyped())
|
assert.NotNil(t, compiled.GoLowUntyped())
|
||||||
|
|
||||||
// now render it out!
|
// now render it out!
|
||||||
schemaBytes, _ := compiled.Render()
|
schemaBytes, _ := compiled.Render()
|
||||||
assert.Len(t, schemaBytes, 3494)
|
assert.Len(t, schemaBytes, 3417)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
|
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
|
||||||
@@ -504,7 +499,7 @@ required: [cake, fish]`
|
|||||||
assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B)
|
assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B)
|
||||||
assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2)
|
assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2)
|
||||||
|
|
||||||
assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description)
|
assert.Equal(t, "nice", compiled.AdditionalProperties.A.Schema().Description)
|
||||||
|
|
||||||
wentLow := compiled.GoLow()
|
wentLow := compiled.GoLow()
|
||||||
assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line)
|
assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line)
|
||||||
@@ -556,7 +551,6 @@ func TestSchemaProxy_GoLow(t *testing.T) {
|
|||||||
spNil := NewSchemaProxy(nil)
|
spNil := NewSchemaProxy(nil)
|
||||||
assert.Nil(t, spNil.GoLow())
|
assert.Nil(t, spNil.GoLow())
|
||||||
assert.Nil(t, spNil.GoLowUntyped())
|
assert.Nil(t, spNil.GoLowUntyped())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHighSchema(t *testing.T, yml string) *Schema {
|
func getHighSchema(t *testing.T, yml string) *Schema {
|
||||||
@@ -836,7 +830,6 @@ allOf:
|
|||||||
// now render it out, it should be identical.
|
// now render it out, it should be identical.
|
||||||
schemaBytes, _ := compiled.Render()
|
schemaBytes, _ := compiled.Render()
|
||||||
assert.Equal(t, testSpec, string(schemaBytes))
|
assert.Equal(t, testSpec, string(schemaBytes))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) {
|
func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) {
|
||||||
@@ -934,8 +927,7 @@ func TestNewSchemaProxy_RenderSchemaEnsurePropertyOrdering(t *testing.T) {
|
|||||||
attribute: true
|
attribute: true
|
||||||
x-pizza: love
|
x-pizza: love
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
why: yes
|
type: string
|
||||||
thatIs: true
|
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
xml:
|
xml:
|
||||||
name: XML Thing`
|
name: XML Thing`
|
||||||
@@ -989,60 +981,6 @@ func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T)
|
|||||||
assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes)))
|
assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T) {
|
|
||||||
testSpec := `additionalProperties:
|
|
||||||
- one
|
|
||||||
- two
|
|
||||||
- miss a few
|
|
||||||
- ninety nine
|
|
||||||
- hundred`
|
|
||||||
|
|
||||||
var compNode yaml.Node
|
|
||||||
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
|
|
||||||
|
|
||||||
sp := new(lowbase.SchemaProxy)
|
|
||||||
err := sp.Build(nil, compNode.Content[0], nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
|
|
||||||
Value: sp,
|
|
||||||
ValueNode: compNode.Content[0],
|
|
||||||
}
|
|
||||||
|
|
||||||
schemaProxy := NewSchemaProxy(&lowproxy)
|
|
||||||
compiled := schemaProxy.Schema()
|
|
||||||
|
|
||||||
// now render it out, it should be identical.
|
|
||||||
schemaBytes, _ := compiled.Render()
|
|
||||||
assert.Len(t, schemaBytes, 91)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSliceMap(t *testing.T) {
|
|
||||||
testSpec := `additionalProperties:
|
|
||||||
- nice: cake
|
|
||||||
- yummy: beer
|
|
||||||
- hot: coffee`
|
|
||||||
|
|
||||||
var compNode yaml.Node
|
|
||||||
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
|
|
||||||
|
|
||||||
sp := new(lowbase.SchemaProxy)
|
|
||||||
err := sp.Build(nil, compNode.Content[0], nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
|
|
||||||
Value: sp,
|
|
||||||
ValueNode: compNode.Content[0],
|
|
||||||
}
|
|
||||||
|
|
||||||
schemaProxy := NewSchemaProxy(&lowproxy)
|
|
||||||
compiled := schemaProxy.Schema()
|
|
||||||
|
|
||||||
// now render it out, it should be identical.
|
|
||||||
schemaBytes, _ := compiled.Render()
|
|
||||||
assert.Len(t, schemaBytes, 75)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) {
|
func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) {
|
||||||
testSpec := `default: false`
|
testSpec := `default: false`
|
||||||
|
|
||||||
@@ -1192,8 +1130,7 @@ unevaluatedProperties: true
|
|||||||
`
|
`
|
||||||
highSchema := getHighSchema(t, yml)
|
highSchema := getHighSchema(t, yml)
|
||||||
|
|
||||||
value := true
|
assert.True(t, highSchema.UnevaluatedProperties.B)
|
||||||
assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnevaluatedPropertiesBoolean_False(t *testing.T) {
|
func TestUnevaluatedPropertiesBoolean_False(t *testing.T) {
|
||||||
@@ -1203,6 +1140,54 @@ unevaluatedProperties: false
|
|||||||
`
|
`
|
||||||
highSchema := getHighSchema(t, yml)
|
highSchema := getHighSchema(t, yml)
|
||||||
|
|
||||||
value := false
|
assert.False(t, highSchema.UnevaluatedProperties.B)
|
||||||
assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B)
|
}
|
||||||
|
|
||||||
|
func TestUnevaluatedPropertiesBoolean_Unset(t *testing.T) {
|
||||||
|
yml := `
|
||||||
|
type: number
|
||||||
|
`
|
||||||
|
highSchema := getHighSchema(t, yml)
|
||||||
|
|
||||||
|
assert.Nil(t, highSchema.UnevaluatedProperties)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdditionalProperties(t *testing.T) {
|
||||||
|
testSpec := `type: object
|
||||||
|
properties:
|
||||||
|
additionalPropertiesSimpleSchema:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
additionalPropertiesBool:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
additionalPropertiesAnyOf:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
anyOf:
|
||||||
|
- type: string
|
||||||
|
- type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
`
|
||||||
|
|
||||||
|
var compNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
|
||||||
|
|
||||||
|
sp := new(lowbase.SchemaProxy)
|
||||||
|
err := sp.Build(nil, compNode.Content[0], nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
|
Value: sp,
|
||||||
|
ValueNode: compNode.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaProxy := NewSchemaProxy(&lowproxy)
|
||||||
|
compiled := schemaProxy.Schema()
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"string"}, compiled.Properties["additionalPropertiesSimpleSchema"].Schema().AdditionalProperties.A.Schema().Type)
|
||||||
|
assert.Equal(t, true, compiled.Properties["additionalPropertiesBool"].Schema().AdditionalProperties.B)
|
||||||
|
assert.Equal(t, []string{"string"}, compiled.Properties["additionalPropertiesAnyOf"].Schema().AdditionalProperties.A.Schema().AnyOf[0].Schema().Type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
|
|||||||
|
|
||||||
d := h.Components.Schemas["Drink"]
|
d := h.Components.Schemas["Drink"]
|
||||||
assert.Len(t, d.Schema().Required, 2)
|
assert.Len(t, d.Schema().Required, 2)
|
||||||
assert.True(t, d.Schema().AdditionalProperties.(bool))
|
assert.True(t, d.Schema().AdditionalProperties.B)
|
||||||
assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName)
|
assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName)
|
||||||
assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"])
|
assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"])
|
||||||
assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line)
|
assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line)
|
||||||
@@ -377,7 +377,6 @@ func testBurgerShop(t *testing.T, h *Document, checkLines bool) {
|
|||||||
assert.Equal(t, 310, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line)
|
assert.Equal(t, 310, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line)
|
||||||
assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line)
|
assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStripeAsDoc(t *testing.T) {
|
func TestStripeAsDoc(t *testing.T) {
|
||||||
@@ -435,7 +434,6 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) {
|
|||||||
d := NewDocument(lowDoc)
|
d := NewDocument(lowDoc)
|
||||||
assert.NotNil(t, d)
|
assert.NotNil(t, d)
|
||||||
assert.Equal(t, 183, len(d.Paths.PathItems))
|
assert.Equal(t, 183, len(d.Paths.PathItems))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPetstoreAsDoc(t *testing.T) {
|
func TestPetstoreAsDoc(t *testing.T) {
|
||||||
@@ -463,7 +461,6 @@ func TestCircularReferencesDoc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalYAML(t *testing.T) {
|
func TestDocument_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
// create a new document
|
// create a new document
|
||||||
initTest()
|
initTest()
|
||||||
h := NewDocument(lowDoc)
|
h := NewDocument(lowDoc)
|
||||||
@@ -477,11 +474,9 @@ func TestDocument_MarshalYAML(t *testing.T) {
|
|||||||
|
|
||||||
highDoc := NewDocument(lDoc)
|
highDoc := NewDocument(lDoc)
|
||||||
testBurgerShop(t, highDoc, false)
|
testBurgerShop(t, highDoc, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalIndention(t *testing.T) {
|
func TestDocument_MarshalIndention(t *testing.T) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
|
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
|
||||||
@@ -495,11 +490,9 @@ func TestDocument_MarshalIndention(t *testing.T) {
|
|||||||
rendered = highDoc.RenderWithIndention(4)
|
rendered = highDoc.RenderWithIndention(4)
|
||||||
|
|
||||||
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
|
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalIndention_Error(t *testing.T) {
|
func TestDocument_MarshalIndention_Error(t *testing.T) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
|
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
|
||||||
@@ -513,11 +506,9 @@ func TestDocument_MarshalIndention_Error(t *testing.T) {
|
|||||||
rendered = highDoc.RenderWithIndention(4)
|
rendered = highDoc.RenderWithIndention(4)
|
||||||
|
|
||||||
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
|
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalJSON(t *testing.T) {
|
func TestDocument_MarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/petstorev3.json")
|
data, _ := os.ReadFile("../../../test_specs/petstorev3.json")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
|
||||||
@@ -537,7 +528,6 @@ func TestDocument_MarshalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalYAMLInline(t *testing.T) {
|
func TestDocument_MarshalYAMLInline(t *testing.T) {
|
||||||
|
|
||||||
// create a new document
|
// create a new document
|
||||||
initTest()
|
initTest()
|
||||||
h := NewDocument(lowDoc)
|
h := NewDocument(lowDoc)
|
||||||
@@ -551,11 +541,9 @@ func TestDocument_MarshalYAMLInline(t *testing.T) {
|
|||||||
|
|
||||||
highDoc := NewDocument(lDoc)
|
highDoc := NewDocument(lDoc)
|
||||||
testBurgerShop(t, highDoc, false)
|
testBurgerShop(t, highDoc, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalYAML_TestRefs(t *testing.T) {
|
func TestDocument_MarshalYAML_TestRefs(t *testing.T) {
|
||||||
|
|
||||||
// create a new document
|
// create a new document
|
||||||
yml := `openapi: 3.1.0
|
yml := `openapi: 3.1.0
|
||||||
paths:
|
paths:
|
||||||
@@ -633,7 +621,6 @@ components:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) {
|
func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) {
|
||||||
|
|
||||||
// create a new document
|
// create a new document
|
||||||
yml := `openapi: 3.1.0
|
yml := `openapi: 3.1.0
|
||||||
paths:
|
paths:
|
||||||
@@ -686,7 +673,6 @@ components:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) {
|
func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) {
|
||||||
|
|
||||||
// create a new document
|
// create a new document
|
||||||
yml := `openapi: 3.1.0
|
yml := `openapi: 3.1.0
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package base
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -100,7 +99,7 @@ type Schema struct {
|
|||||||
PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
||||||
PropertyNames low.NodeReference[*SchemaProxy]
|
PropertyNames low.NodeReference[*SchemaProxy]
|
||||||
UnevaluatedItems low.NodeReference[*SchemaProxy]
|
UnevaluatedItems low.NodeReference[*SchemaProxy]
|
||||||
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]
|
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]
|
||||||
Anchor low.NodeReference[string]
|
Anchor low.NodeReference[string]
|
||||||
|
|
||||||
// Compatible with all versions
|
// Compatible with all versions
|
||||||
@@ -121,7 +120,7 @@ type Schema struct {
|
|||||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||||
Not low.NodeReference[*SchemaProxy]
|
Not low.NodeReference[*SchemaProxy]
|
||||||
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
||||||
AdditionalProperties low.NodeReference[any]
|
AdditionalProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
ContentEncoding low.NodeReference[string]
|
ContentEncoding low.NodeReference[string]
|
||||||
ContentMediaType low.NodeReference[string]
|
ContentMediaType low.NodeReference[string]
|
||||||
@@ -189,54 +188,8 @@ func (s *Schema) Hash() [32]byte {
|
|||||||
d = append(d, fmt.Sprint(s.MinProperties.Value))
|
d = append(d, fmt.Sprint(s.MinProperties.Value))
|
||||||
}
|
}
|
||||||
if !s.AdditionalProperties.IsEmpty() {
|
if !s.AdditionalProperties.IsEmpty() {
|
||||||
|
|
||||||
// check type of properties, if we have a low level map, we need to hash the values in a repeatable
|
|
||||||
// order.
|
|
||||||
to := reflect.TypeOf(s.AdditionalProperties.Value)
|
|
||||||
vo := reflect.ValueOf(s.AdditionalProperties.Value)
|
|
||||||
var values []string
|
|
||||||
switch to.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < vo.Len(); i++ {
|
|
||||||
vn := vo.Index(i).Interface()
|
|
||||||
|
|
||||||
if jh, ok := vn.(low.HasValueUnTyped); ok {
|
|
||||||
vn = jh.GetValueUntyped()
|
|
||||||
fg := reflect.TypeOf(vn)
|
|
||||||
gf := reflect.ValueOf(vn)
|
|
||||||
|
|
||||||
if fg.Kind() == reflect.Map {
|
|
||||||
for _, ky := range gf.MapKeys() {
|
|
||||||
hu := ky.Interface()
|
|
||||||
values = append(values, fmt.Sprintf("%s:%s", hu, low.GenerateHashString(gf.MapIndex(ky).Interface())))
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
values = append(values, fmt.Sprintf("%d:%s", i, low.GenerateHashString(vn)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(values)
|
|
||||||
d = append(d, strings.Join(values, "||"))
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
for _, k := range vo.MapKeys() {
|
|
||||||
var x string
|
|
||||||
var l int
|
|
||||||
var v any
|
|
||||||
// extract key
|
|
||||||
if o, ok := k.Interface().(low.HasKeyNode); ok {
|
|
||||||
x = o.GetKeyNode().Value
|
|
||||||
l = o.GetKeyNode().Line
|
|
||||||
v = vo.MapIndex(k).Interface().(low.HasValueNodeUntyped).GetValueNode().Value
|
|
||||||
}
|
|
||||||
values = append(values, fmt.Sprintf("%d:%s:%s", l, x, low.GenerateHashString(v)))
|
|
||||||
}
|
|
||||||
sort.Strings(values)
|
|
||||||
d = append(d, strings.Join(values, "||"))
|
|
||||||
default:
|
|
||||||
d = append(d, low.GenerateHashString(s.AdditionalProperties.Value))
|
d = append(d, low.GenerateHashString(s.AdditionalProperties.Value))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !s.Description.IsEmpty() {
|
if !s.Description.IsEmpty() {
|
||||||
d = append(d, fmt.Sprint(s.Description.Value))
|
d = append(d, fmt.Sprint(s.Description.Value))
|
||||||
}
|
}
|
||||||
@@ -667,77 +620,24 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
|
// check additionalProperties type for schema or bool
|
||||||
if addPNode != nil {
|
addPropsIsBool := false
|
||||||
if utils.IsNodeMap(addPNode) || utils.IsNodeArray(addPNode) {
|
addPropsBoolValue := true
|
||||||
// check if this is a reference, or an inline schema.
|
_, addPLabel, addPValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
|
||||||
isRef, _, _ := utils.IsNodeRefValue(addPNode)
|
if addPValue != nil {
|
||||||
var sp *SchemaProxy
|
if utils.IsNodeBoolValue(addPValue) {
|
||||||
// now check if this object has a 'type' if so, it's a schema, if not... it's a random
|
addPropsIsBool = true
|
||||||
// object, and we should treat it as a raw map.
|
addPropsBoolValue, _ = strconv.ParseBool(addPValue.Value)
|
||||||
if _, v := utils.FindKeyNodeTop(TypeLabel, addPNode.Content); v != nil {
|
|
||||||
sp = &SchemaProxy{
|
|
||||||
kn: addPLabel,
|
|
||||||
vn: addPNode,
|
|
||||||
idx: idx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isRef {
|
if addPropsIsBool {
|
||||||
_, vn := utils.FindKeyNodeTop("$ref", addPNode.Content)
|
s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
|
||||||
sp = &SchemaProxy{
|
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
|
||||||
kn: addPLabel,
|
B: addPropsBoolValue,
|
||||||
vn: addPNode,
|
N: 1,
|
||||||
idx: idx,
|
},
|
||||||
isReference: true,
|
KeyNode: addPLabel,
|
||||||
referenceLookup: vn.Value,
|
ValueNode: addPValue,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a reference, or a schema, we're done.
|
|
||||||
if sp != nil {
|
|
||||||
s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// if this is a map, collect all the keys and values.
|
|
||||||
if utils.IsNodeMap(addPNode) {
|
|
||||||
|
|
||||||
addProps := make(map[low.KeyReference[string]]low.ValueReference[any])
|
|
||||||
var label string
|
|
||||||
for g := range addPNode.Content {
|
|
||||||
if g%2 == 0 {
|
|
||||||
label = addPNode.Content[g].Value
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
addProps[low.KeyReference[string]{Value: label, KeyNode: addPNode.Content[g-1]}] = low.ValueReference[any]{Value: addPNode.Content[g].Value, ValueNode: addPNode.Content[g]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the node is an array, extract everything into a trackable structure
|
|
||||||
if utils.IsNodeArray(addPNode) {
|
|
||||||
var addProps []low.ValueReference[any]
|
|
||||||
|
|
||||||
// if this is an array or maps, encode the map items correctly.
|
|
||||||
for i := range addPNode.Content {
|
|
||||||
if utils.IsNodeMap(addPNode.Content[i]) {
|
|
||||||
var prop map[string]any
|
|
||||||
_ = addPNode.Content[i].Decode(&prop)
|
|
||||||
addProps = append(addProps,
|
|
||||||
low.ValueReference[any]{Value: prop, ValueNode: addPNode.Content[i]})
|
|
||||||
} else {
|
|
||||||
addProps = append(addProps,
|
|
||||||
low.ValueReference[any]{Value: addPNode.Content[i].Value, ValueNode: addPNode.Content[i]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if utils.IsNodeBoolValue(addPNode) {
|
|
||||||
b, _ := strconv.ParseBool(addPNode.Value)
|
|
||||||
s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,9 +727,9 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if unevalIsBool {
|
if unevalIsBool {
|
||||||
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{
|
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
|
||||||
Value: &SchemaDynamicValue[*SchemaProxy, *bool]{
|
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
|
||||||
B: &unevalBoolValue,
|
B: unevalBoolValue,
|
||||||
N: 1,
|
N: 1,
|
||||||
},
|
},
|
||||||
KeyNode: unevalLabel,
|
KeyNode: unevalLabel,
|
||||||
@@ -838,7 +738,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy]
|
var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy]
|
||||||
var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties low.ValueReference[*SchemaProxy]
|
var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties, addProperties low.ValueReference[*SchemaProxy]
|
||||||
|
|
||||||
_, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content)
|
_, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content)
|
||||||
_, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content)
|
_, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content)
|
||||||
@@ -852,6 +752,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
_, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content)
|
_, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content)
|
||||||
_, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content)
|
_, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content)
|
||||||
_, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content)
|
_, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content)
|
||||||
|
_, addPropsLabel, addPropsValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
|
||||||
|
|
||||||
errorChan := make(chan error)
|
errorChan := make(chan error)
|
||||||
allOfChan := make(chan schemaProxyBuildResult)
|
allOfChan := make(chan schemaProxyBuildResult)
|
||||||
@@ -867,6 +768,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
propNamesChan := make(chan schemaProxyBuildResult)
|
propNamesChan := make(chan schemaProxyBuildResult)
|
||||||
unevalItemsChan := make(chan schemaProxyBuildResult)
|
unevalItemsChan := make(chan schemaProxyBuildResult)
|
||||||
unevalPropsChan := make(chan schemaProxyBuildResult)
|
unevalPropsChan := make(chan schemaProxyBuildResult)
|
||||||
|
addPropsChan := make(chan schemaProxyBuildResult)
|
||||||
|
|
||||||
totalBuilds := countSubSchemaItems(allOfValue) +
|
totalBuilds := countSubSchemaItems(allOfValue) +
|
||||||
countSubSchemaItems(anyOfValue) +
|
countSubSchemaItems(anyOfValue) +
|
||||||
@@ -921,6 +823,10 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
totalBuilds++
|
totalBuilds++
|
||||||
go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx)
|
go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx)
|
||||||
}
|
}
|
||||||
|
if !addPropsIsBool && addPropsValue != nil {
|
||||||
|
totalBuilds++
|
||||||
|
go buildSchema(addPropsChan, addPropsLabel, addPropsValue, errorChan, idx)
|
||||||
|
}
|
||||||
|
|
||||||
completeCount := 0
|
completeCount := 0
|
||||||
for completeCount < totalBuilds {
|
for completeCount < totalBuilds {
|
||||||
@@ -966,6 +872,9 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
case r := <-unevalPropsChan:
|
case r := <-unevalPropsChan:
|
||||||
completeCount++
|
completeCount++
|
||||||
unevalProperties = r.v
|
unevalProperties = r.v
|
||||||
|
case r := <-addPropsChan:
|
||||||
|
completeCount++
|
||||||
|
addProperties = r.v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,14 +965,23 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !unevalIsBool && !unevalProperties.IsEmpty() {
|
if !unevalIsBool && !unevalProperties.IsEmpty() {
|
||||||
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{
|
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
|
||||||
Value: &SchemaDynamicValue[*SchemaProxy, *bool]{
|
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
|
||||||
A: unevalProperties.Value,
|
A: unevalProperties.Value,
|
||||||
},
|
},
|
||||||
KeyNode: unevalPropsLabel,
|
KeyNode: unevalPropsLabel,
|
||||||
ValueNode: unevalPropsValue,
|
ValueNode: unevalPropsValue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !addPropsIsBool && !addProperties.IsEmpty() {
|
||||||
|
s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
|
||||||
|
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
|
||||||
|
A: addProperties.Value,
|
||||||
|
},
|
||||||
|
KeyNode: addPropsLabel,
|
||||||
|
ValueNode: addPropsValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1219,8 +1137,7 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
v: *r.res,
|
v: *r.res,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if utils.IsNodeArray(valueNode) {
|
||||||
if utils.IsNodeArray(valueNode) {
|
|
||||||
refBuilds := 0
|
refBuilds := 0
|
||||||
results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content))
|
results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content))
|
||||||
|
|
||||||
@@ -1261,6 +1178,8 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
v: *r,
|
v: *r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
errors <- fmt.Errorf("build schema failed: unexpected node type: %s, line %d, col %d", valueNode.Tag, valueNode.Line, valueNode.Column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ func Test_Schema(t *testing.T) {
|
|||||||
schErr := sch.Build(rootNode.Content[0], nil)
|
schErr := sch.Build(rootNode.Content[0], nil)
|
||||||
assert.NoError(t, schErr)
|
assert.NoError(t, schErr)
|
||||||
assert.Equal(t, "something object", sch.Description.Value)
|
assert.Equal(t, "something object", sch.Description.Value)
|
||||||
assert.True(t, sch.AdditionalProperties.Value.(bool))
|
assert.True(t, sch.AdditionalProperties.Value.B)
|
||||||
|
|
||||||
assert.Len(t, sch.Properties.Value, 2)
|
assert.Len(t, sch.Properties.Value, 2)
|
||||||
v := sch.FindProperty("somethingB")
|
v := sch.FindProperty("somethingB")
|
||||||
@@ -1172,30 +1172,7 @@ func TestExtractSchema_AdditionalPropertiesAsSchema(t *testing.T) {
|
|||||||
|
|
||||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema())
|
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.A.Schema())
|
||||||
assert.Nil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtractSchema_AdditionalPropertiesAsSchemaSlice(t *testing.T) {
|
|
||||||
yml := `components:
|
|
||||||
schemas:
|
|
||||||
Something:
|
|
||||||
additionalProperties:
|
|
||||||
- nice: rice`
|
|
||||||
|
|
||||||
var iNode yaml.Node
|
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &iNode)
|
|
||||||
assert.NoError(t, mErr)
|
|
||||||
idx := index.NewSpecIndex(&iNode)
|
|
||||||
|
|
||||||
yml = `$ref: '#/components/schemas/Something'`
|
|
||||||
|
|
||||||
var idxNode yaml.Node
|
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
|
||||||
|
|
||||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
|
||||||
|
|
||||||
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.([]low.ValueReference[interface{}]))
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1244,7 +1221,7 @@ func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) {
|
|||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
|
||||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||||
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema())
|
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.A.Schema())
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1378,7 +1355,7 @@ func TestSchema_Hash_Equal(t *testing.T) {
|
|||||||
uniqueItems: 1
|
uniqueItems: 1
|
||||||
maxProperties: 10
|
maxProperties: 10
|
||||||
minProperties: 1
|
minProperties: 1
|
||||||
additionalProperties: anything
|
additionalProperties: true
|
||||||
description: milky
|
description: milky
|
||||||
contentEncoding: rubber shoes
|
contentEncoding: rubber shoes
|
||||||
contentMediaType: paper tiger
|
contentMediaType: paper tiger
|
||||||
@@ -1420,7 +1397,7 @@ func TestSchema_Hash_Equal(t *testing.T) {
|
|||||||
uniqueItems: 1
|
uniqueItems: 1
|
||||||
maxProperties: 10
|
maxProperties: 10
|
||||||
minProperties: 1
|
minProperties: 1
|
||||||
additionalProperties: anything
|
additionalProperties: true
|
||||||
description: milky
|
description: milky
|
||||||
contentEncoding: rubber shoes
|
contentEncoding: rubber shoes
|
||||||
contentMediaType: paper tiger
|
contentMediaType: paper tiger
|
||||||
@@ -1611,7 +1588,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsTrue(t *testing.T) {
|
|||||||
res, _ := ExtractSchema(idxNode.Content[0], idx)
|
res, _ := ExtractSchema(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
|
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
|
||||||
assert.True(t, *res.Value.Schema().UnevaluatedProperties.Value.B)
|
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.B)
|
||||||
|
|
||||||
assert.Equal(t, "571bd1853c22393131e2dcadce86894da714ec14968895c8b7ed18154b2be8cd",
|
assert.Equal(t, "571bd1853c22393131e2dcadce86894da714ec14968895c8b7ed18154b2be8cd",
|
||||||
low.GenerateHashString(res.Value.Schema().UnevaluatedProperties.Value))
|
low.GenerateHashString(res.Value.Schema().UnevaluatedProperties.Value))
|
||||||
@@ -1636,7 +1613,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsFalse(t *testing.T) {
|
|||||||
res, _ := ExtractSchema(idxNode.Content[0], idx)
|
res, _ := ExtractSchema(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
|
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
|
||||||
assert.False(t, *res.Value.Schema().UnevaluatedProperties.Value.B)
|
assert.False(t, res.Value.Schema().UnevaluatedProperties.Value.B)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) {
|
func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,7 +51,6 @@ func BenchmarkCreateDocument_Circular(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCreateDocument_k8s(b *testing.B) {
|
func BenchmarkCreateDocument_k8s(b *testing.B) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/k8s.json")
|
data, _ := os.ReadFile("../../../test_specs/k8s.json")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
|
||||||
@@ -69,7 +67,6 @@ func BenchmarkCreateDocument_k8s(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCircularReferenceError(t *testing.T) {
|
func TestCircularReferenceError(t *testing.T) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml")
|
data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
circDoc, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
circDoc, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||||
@@ -81,7 +78,6 @@ func TestCircularReferenceError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCircularReference_IgnoreArray(t *testing.T) {
|
func TestCircularReference_IgnoreArray(t *testing.T) {
|
||||||
|
|
||||||
spec := `openapi: 3.1.0
|
spec := `openapi: 3.1.0
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
@@ -110,7 +106,6 @@ components:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCircularReference_IgnorePoly(t *testing.T) {
|
func TestCircularReference_IgnorePoly(t *testing.T) {
|
||||||
|
|
||||||
spec := `openapi: 3.1.0
|
spec := `openapi: 3.1.0
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
@@ -168,7 +163,6 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocumentStripe(t *testing.T) {
|
func TestCreateDocumentStripe(t *testing.T) {
|
||||||
|
|
||||||
data, _ := os.ReadFile("../../../test_specs/stripe.yaml")
|
data, _ := os.ReadFile("../../../test_specs/stripe.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||||
@@ -303,7 +297,6 @@ func TestCreateDocument_Tags(t *testing.T) {
|
|||||||
// this is why we will need a higher level API to this model, this looks cool and all, but dude.
|
// this is why we will need a higher level API to this model, this looks cool and all, but dude.
|
||||||
assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"])
|
assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// tag2
|
/// tag2
|
||||||
@@ -313,7 +306,6 @@ func TestCreateDocument_Tags(t *testing.T) {
|
|||||||
assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
|
assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
|
||||||
assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
|
assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
|
||||||
assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0)
|
assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Paths(t *testing.T) {
|
func TestCreateDocument_Paths(t *testing.T) {
|
||||||
@@ -438,7 +430,6 @@ func TestCreateDocument_Paths(t *testing.T) {
|
|||||||
assert.NotNil(t, servers)
|
assert.NotNil(t, servers)
|
||||||
assert.Len(t, servers, 1)
|
assert.Len(t, servers, 1)
|
||||||
assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value)
|
assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Components_Schemas(t *testing.T) {
|
func TestCreateDocument_Components_Schemas(t *testing.T) {
|
||||||
@@ -464,7 +455,6 @@ func TestCreateDocument_Components_Schemas(t *testing.T) {
|
|||||||
p := fries.Value.Schema().FindProperty("favoriteDrink")
|
p := fries.Value.Schema().FindProperty("favoriteDrink")
|
||||||
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
|
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
|
||||||
p.Value.Schema().Description.Value)
|
p.Value.Schema().Description.Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Components_SecuritySchemes(t *testing.T) {
|
func TestCreateDocument_Components_SecuritySchemes(t *testing.T) {
|
||||||
@@ -493,7 +483,6 @@ func TestCreateDocument_Components_SecuritySchemes(t *testing.T) {
|
|||||||
readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers")
|
readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers")
|
||||||
assert.NotNil(t, readScope)
|
assert.NotNil(t, readScope)
|
||||||
assert.Equal(t, "modify burgers and stuff", readScope.Value)
|
assert.Equal(t, "modify burgers and stuff", readScope.Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Components_Responses(t *testing.T) {
|
func TestCreateDocument_Components_Responses(t *testing.T) {
|
||||||
@@ -506,7 +495,6 @@ func TestCreateDocument_Components_Responses(t *testing.T) {
|
|||||||
assert.NotNil(t, dressingResponse.Value)
|
assert.NotNil(t, dressingResponse.Value)
|
||||||
assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value)
|
assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value)
|
||||||
assert.Len(t, dressingResponse.Value.Content.Value, 1)
|
assert.Len(t, dressingResponse.Value.Content.Value, 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Components_Examples(t *testing.T) {
|
func TestCreateDocument_Components_Examples(t *testing.T) {
|
||||||
@@ -593,7 +581,6 @@ func TestCreateDocument_Component_Discriminator(t *testing.T) {
|
|||||||
assert.Nil(t, dsc.FindMappingValue("don't exist"))
|
assert.Nil(t, dsc.FindMappingValue("don't exist"))
|
||||||
assert.NotNil(t, doc.GetExternalDocs())
|
assert.NotNil(t, doc.GetExternalDocs())
|
||||||
assert.Nil(t, doc.FindSecurityRequirement("scooby doo"))
|
assert.Nil(t, doc.FindSecurityRequirement("scooby doo"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
|
func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
|
||||||
@@ -601,11 +588,8 @@ func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
|
|||||||
components := doc.Components.Value
|
components := doc.Components.Value
|
||||||
d := components.FindSchema("Dressing")
|
d := components.FindSchema("Dressing")
|
||||||
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
||||||
if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok {
|
|
||||||
assert.Equal(t, "something in here.", n.Schema().Description.Value)
|
assert.True(t, d.Value.Schema().AdditionalProperties.Value.IsA(), "should be a schema")
|
||||||
} else {
|
|
||||||
assert.Fail(t, "should be a schema")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
|
func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
|
||||||
@@ -613,7 +597,7 @@ func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
|
|||||||
components := doc.Components.Value
|
components := doc.Components.Value
|
||||||
d := components.FindSchema("Drink")
|
d := components.FindSchema("Drink")
|
||||||
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
||||||
assert.True(t, d.Value.Schema().AdditionalProperties.Value.(bool))
|
assert.True(t, d.Value.Schema().AdditionalProperties.Value.B)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Components_Error(t *testing.T) {
|
func TestCreateDocument_Components_Error(t *testing.T) {
|
||||||
@@ -667,7 +651,6 @@ components:
|
|||||||
AllowRemoteReferences: false,
|
AllowRemoteReferences: false,
|
||||||
})
|
})
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Paths_Errors(t *testing.T) {
|
func TestCreateDocument_Paths_Errors(t *testing.T) {
|
||||||
@@ -776,8 +759,8 @@ func TestCreateDocument_YamlAnchor(t *testing.T) {
|
|||||||
assert.NotNil(t, jsonGet)
|
assert.NotNil(t, jsonGet)
|
||||||
|
|
||||||
// Should this work? It doesn't
|
// Should this work? It doesn't
|
||||||
//postJsonType := examplePath.GetValue().Post.GetValue().RequestBody.GetValue().FindContent("application/json")
|
// postJsonType := examplePath.GetValue().Post.GetValue().RequestBody.GetValue().FindContent("application/json")
|
||||||
//assert.NotNil(t, postJsonType)
|
// assert.NotNil(t, postJsonType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleCreateDocument() {
|
func ExampleCreateDocument() {
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ type SchemaChanges struct {
|
|||||||
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
|
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
|
||||||
XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"`
|
XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"`
|
||||||
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
|
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
|
||||||
|
AdditionalPropertiesChanges *SchemaChanges `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
||||||
|
|
||||||
// 3.1 specifics
|
// 3.1 specifics
|
||||||
IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"`
|
IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"`
|
||||||
@@ -102,6 +103,9 @@ func (s *SchemaChanges) GetAllChanges() []*Change {
|
|||||||
if s.UnevaluatedPropertiesChanges != nil {
|
if s.UnevaluatedPropertiesChanges != nil {
|
||||||
changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...)
|
changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...)
|
||||||
}
|
}
|
||||||
|
if s.AdditionalPropertiesChanges != nil {
|
||||||
|
changes = append(changes, s.AdditionalPropertiesChanges.GetAllChanges()...)
|
||||||
|
}
|
||||||
if s.SchemaPropertyChanges != nil {
|
if s.SchemaPropertyChanges != nil {
|
||||||
for n := range s.SchemaPropertyChanges {
|
for n := range s.SchemaPropertyChanges {
|
||||||
if s.SchemaPropertyChanges[n] != nil {
|
if s.SchemaPropertyChanges[n] != nil {
|
||||||
@@ -185,6 +189,9 @@ func (s *SchemaChanges) TotalChanges() int {
|
|||||||
if s.UnevaluatedPropertiesChanges != nil {
|
if s.UnevaluatedPropertiesChanges != nil {
|
||||||
t += s.UnevaluatedPropertiesChanges.TotalChanges()
|
t += s.UnevaluatedPropertiesChanges.TotalChanges()
|
||||||
}
|
}
|
||||||
|
if s.AdditionalPropertiesChanges != nil {
|
||||||
|
t += s.AdditionalPropertiesChanges.TotalChanges()
|
||||||
|
}
|
||||||
if s.SchemaPropertyChanges != nil {
|
if s.SchemaPropertyChanges != nil {
|
||||||
for n := range s.SchemaPropertyChanges {
|
for n := range s.SchemaPropertyChanges {
|
||||||
if s.SchemaPropertyChanges[n] != nil {
|
if s.SchemaPropertyChanges[n] != nil {
|
||||||
@@ -267,6 +274,9 @@ func (s *SchemaChanges) TotalBreakingChanges() int {
|
|||||||
if s.UnevaluatedPropertiesChanges != nil {
|
if s.UnevaluatedPropertiesChanges != nil {
|
||||||
t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges()
|
t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges()
|
||||||
}
|
}
|
||||||
|
if s.AdditionalPropertiesChanges != nil {
|
||||||
|
t += s.AdditionalPropertiesChanges.TotalBreakingChanges()
|
||||||
|
}
|
||||||
if s.DependentSchemasChanges != nil {
|
if s.DependentSchemasChanges != nil {
|
||||||
for n := range s.DependentSchemasChanges {
|
for n := range s.DependentSchemasChanges {
|
||||||
t += s.DependentSchemasChanges[n].TotalBreakingChanges()
|
t += s.DependentSchemasChanges[n].TotalBreakingChanges()
|
||||||
@@ -728,18 +738,36 @@ func checkSchemaPropertyChanges(
|
|||||||
New: rSchema,
|
New: rSchema,
|
||||||
})
|
})
|
||||||
|
|
||||||
// AdditionalProperties (only if not an object)
|
// AdditionalProperties
|
||||||
if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) &&
|
if lSchema.AdditionalProperties.Value != nil && rSchema.AdditionalProperties.Value != nil {
|
||||||
!utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) {
|
if lSchema.AdditionalProperties.Value.IsA() && rSchema.AdditionalProperties.Value.IsA() {
|
||||||
props = append(props, &PropertyCheck{
|
if !low.AreEqual(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A) {
|
||||||
LeftNode: lSchema.AdditionalProperties.ValueNode,
|
sc.AdditionalPropertiesChanges = CompareSchemas(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A)
|
||||||
RightNode: rSchema.AdditionalProperties.ValueNode,
|
}
|
||||||
Label: v3.AdditionalPropertiesLabel,
|
} else {
|
||||||
Changes: changes,
|
if lSchema.AdditionalProperties.Value.IsB() && rSchema.AdditionalProperties.Value.IsB() {
|
||||||
Breaking: false,
|
if lSchema.AdditionalProperties.Value.B != rSchema.AdditionalProperties.Value.B {
|
||||||
Original: lSchema,
|
CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
|
||||||
New: rSchema,
|
lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, true,
|
||||||
})
|
lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
|
||||||
|
lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, true,
|
||||||
|
lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// added AdditionalProperties
|
||||||
|
if lSchema.AdditionalProperties.Value == nil && rSchema.AdditionalProperties.Value != nil {
|
||||||
|
CreateChange(changes, ObjectAdded, v3.AdditionalPropertiesLabel,
|
||||||
|
nil, rSchema.AdditionalProperties.ValueNode, true, nil, rSchema.AdditionalProperties.Value)
|
||||||
|
}
|
||||||
|
// removed AdditionalProperties
|
||||||
|
if lSchema.AdditionalProperties.Value != nil && rSchema.AdditionalProperties.Value == nil {
|
||||||
|
CreateChange(changes, ObjectRemoved, v3.AdditionalPropertiesLabel,
|
||||||
|
lSchema.AdditionalProperties.ValueNode, nil, true, lSchema.AdditionalProperties.Value, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Description
|
// Description
|
||||||
@@ -1108,20 +1136,6 @@ func checkSchemaPropertyChanges(
|
|||||||
// check extensions
|
// check extensions
|
||||||
sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions)
|
sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions)
|
||||||
|
|
||||||
// if additional properties is an object, then hash it
|
|
||||||
// AdditionalProperties (only if not an object)
|
|
||||||
if utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) ||
|
|
||||||
utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) {
|
|
||||||
|
|
||||||
lHash := low.GenerateHashString(lSchema.AdditionalProperties.ValueNode)
|
|
||||||
rHash := low.GenerateHashString(rSchema.AdditionalProperties.ValueNode)
|
|
||||||
if lHash != rHash {
|
|
||||||
CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
|
|
||||||
lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, false,
|
|
||||||
lSchema.AdditionalProperties.Value, rSchema.AdditionalProperties.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check core properties
|
// check core properties
|
||||||
CheckProperties(props)
|
CheckProperties(props)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// These tests require full documents to be tested properly. schemas are perhaps the most complex
|
// These tests require full documents to be tested properly. schemas are perhaps the most complex
|
||||||
@@ -158,7 +159,6 @@ components:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) {
|
func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) {
|
||||||
|
|
||||||
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
|
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
|
||||||
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
|
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
|
||||||
|
|
||||||
@@ -168,7 +168,6 @@ func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_BuildDocv2(l, r string) (*v2.Swagger, *v2.Swagger) {
|
func test_BuildDocv2(l, r string) (*v2.Swagger, *v2.Swagger) {
|
||||||
|
|
||||||
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
|
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
|
||||||
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
|
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
|
||||||
|
|
||||||
@@ -278,7 +277,6 @@ components:
|
|||||||
assert.Equal(t, Modified, changes.Changes[0].ChangeType)
|
assert.Equal(t, Modified, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
|
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
|
||||||
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].Original)
|
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].Original)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_InlineToRef(t *testing.T) {
|
func TestCompareSchemas_InlineToRef(t *testing.T) {
|
||||||
@@ -311,7 +309,6 @@ components:
|
|||||||
assert.Equal(t, Modified, changes.Changes[0].ChangeType)
|
assert.Equal(t, Modified, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
|
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
|
||||||
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].New)
|
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].New)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Identical(t *testing.T) {
|
func TestCompareSchemas_Identical(t *testing.T) {
|
||||||
@@ -1222,6 +1219,93 @@ components:
|
|||||||
assert.Equal(t, v3.UnevaluatedPropertiesLabel, changes.Changes[0].Property)
|
assert.Equal(t, v3.UnevaluatedPropertiesLabel, changes.Changes[0].Property)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_AdditionalProperties(t *testing.T) {
|
||||||
|
left := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
additionalProperties:
|
||||||
|
type: string`
|
||||||
|
|
||||||
|
right := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
additionalProperties:
|
||||||
|
type: int`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
|
||||||
|
changes := CompareSchemas(lSchemaProxy, rSchemaProxy)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
assert.Equal(t, 1, changes.AdditionalPropertiesChanges.PropertyChanges.TotalChanges())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_AdditionalProperties_Added(t *testing.T) {
|
||||||
|
left := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
type: string`
|
||||||
|
|
||||||
|
right := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
type: string
|
||||||
|
additionalProperties:
|
||||||
|
type: int`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
|
||||||
|
changes := CompareSchemas(lSchemaProxy, rSchemaProxy)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
assert.Equal(t, v3.AdditionalPropertiesLabel, changes.Changes[0].Property)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareSchemas_AdditionalProperties_Removed(t *testing.T) {
|
||||||
|
left := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
type: string`
|
||||||
|
|
||||||
|
right := `openapi: 3.1
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OK:
|
||||||
|
type: string
|
||||||
|
additionalProperties:
|
||||||
|
type: int`
|
||||||
|
|
||||||
|
leftDoc, rightDoc := test_BuildDoc(left, right)
|
||||||
|
|
||||||
|
// extract left reference schema and non reference schema.
|
||||||
|
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
|
||||||
|
|
||||||
|
changes := CompareSchemas(rSchemaProxy, lSchemaProxy)
|
||||||
|
assert.NotNil(t, changes)
|
||||||
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
assert.Equal(t, v3.AdditionalPropertiesLabel, changes.Changes[0].Property)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_UnevaluatedItems(t *testing.T) {
|
func TestCompareSchemas_UnevaluatedItems(t *testing.T) {
|
||||||
left := `openapi: 3.1
|
left := `openapi: 3.1
|
||||||
components:
|
components:
|
||||||
@@ -1611,7 +1695,6 @@ components:
|
|||||||
assert.Equal(t, Modified, changes.AnyOfChanges[0].Changes[0].ChangeType)
|
assert.Equal(t, Modified, changes.AnyOfChanges[0].Changes[0].ChangeType)
|
||||||
assert.Equal(t, "string", changes.AnyOfChanges[0].Changes[0].New)
|
assert.Equal(t, "string", changes.AnyOfChanges[0].Changes[0].New)
|
||||||
assert.Equal(t, "bool", changes.AnyOfChanges[0].Changes[0].Original)
|
assert.Equal(t, "bool", changes.AnyOfChanges[0].Changes[0].Original)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_OneOfModifyAndAddItem(t *testing.T) {
|
func TestCompareSchemas_OneOfModifyAndAddItem(t *testing.T) {
|
||||||
@@ -1848,7 +1931,6 @@ components:
|
|||||||
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
|
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
|
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
|
||||||
low.HashToString(changes.Changes[0].NewObject.(*base.Discriminator).Hash()))
|
low.HashToString(changes.Changes[0].NewObject.(*base.Discriminator).Hash()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_DiscriminatorRemove(t *testing.T) {
|
func TestCompareSchemas_DiscriminatorRemove(t *testing.T) {
|
||||||
@@ -1881,7 +1963,6 @@ components:
|
|||||||
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
|
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
|
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
|
||||||
low.HashToString(changes.Changes[0].OriginalObject.(*base.Discriminator).Hash()))
|
low.HashToString(changes.Changes[0].OriginalObject.(*base.Discriminator).Hash()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_ExternalDocsChange(t *testing.T) {
|
func TestCompareSchemas_ExternalDocsChange(t *testing.T) {
|
||||||
@@ -1948,7 +2029,6 @@ components:
|
|||||||
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
|
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
|
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
|
||||||
low.HashToString(changes.Changes[0].NewObject.(*base.ExternalDoc).Hash()))
|
low.HashToString(changes.Changes[0].NewObject.(*base.ExternalDoc).Hash()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_ExternalDocsRemove(t *testing.T) {
|
func TestCompareSchemas_ExternalDocsRemove(t *testing.T) {
|
||||||
@@ -1981,7 +2061,6 @@ components:
|
|||||||
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
|
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
|
||||||
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
|
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
|
||||||
low.HashToString(changes.Changes[0].OriginalObject.(*base.ExternalDoc).Hash()))
|
low.HashToString(changes.Changes[0].OriginalObject.(*base.ExternalDoc).Hash()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_AddExtension(t *testing.T) {
|
func TestCompareSchemas_AddExtension(t *testing.T) {
|
||||||
@@ -2402,7 +2481,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) {
|
func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) {
|
||||||
@@ -2432,7 +2510,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Schema_DeletePoly(t *testing.T) {
|
func TestCompareSchemas_Schema_DeletePoly(t *testing.T) {
|
||||||
@@ -2466,7 +2543,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
assert.Equal(t, 1, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Schema_AddExamplesArray_AllOf(t *testing.T) {
|
func TestCompareSchemas_Schema_AddExamplesArray_AllOf(t *testing.T) {
|
||||||
@@ -2499,7 +2575,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Schema_AddExampleMap_AllOf(t *testing.T) {
|
func TestCompareSchemas_Schema_AddExampleMap_AllOf(t *testing.T) {
|
||||||
@@ -2566,7 +2641,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Schema_AddExamplesMap(t *testing.T) {
|
func TestCompareSchemas_Schema_AddExamplesMap(t *testing.T) {
|
||||||
@@ -2601,7 +2675,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_Schema_AddExamples(t *testing.T) {
|
func TestCompareSchemas_Schema_AddExamples(t *testing.T) {
|
||||||
@@ -2661,7 +2734,6 @@ components:
|
|||||||
assert.Equal(t, 1, changes.TotalChanges())
|
assert.Equal(t, 1, changes.TotalChanges())
|
||||||
assert.Len(t, changes.GetAllChanges(), 1)
|
assert.Len(t, changes.GetAllChanges(), 1)
|
||||||
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
assert.Equal(t, 0, changes.TotalBreakingChanges())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareSchemas_CheckIssue_170(t *testing.T) {
|
func TestCompareSchemas_CheckIssue_170(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user