Skip to content

Commit

Permalink
solve type conflicts by preferring string type attributes and preferr…
Browse files Browse the repository at this point in the history
…ing number types to integer (#26)
  • Loading branch information
FrimIdan authored Feb 21, 2023
1 parent bf6104f commit 7776596
Show file tree
Hide file tree
Showing 4 changed files with 454 additions and 54 deletions.
47 changes: 47 additions & 0 deletions pkg/spec/conflict.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package spec
import (
"fmt"

spec "github.com/getkin/kin-openapi/openapi3"
"k8s.io/utils/field"
)

Expand All @@ -39,3 +40,49 @@ func createHeaderInConflictMsg(path *field.Path, in, in2 interface{}) string {
func (c conflict) String() string {
return c.msg
}

const (
NoConflict = iota
PreferType1
PreferType2
ConflictUnresolved
)

// conflictSolver will get 2 types and returns:
//
// NoConflict - type1 and type2 are equal
// PreferType1 - type1 should be used
// PreferType2 - type2 should be used
// ConflictUnresolved - types conflict can't be resolved
func conflictSolver(type1, type2 string) int {
if type1 == type2 {
return NoConflict
}

if shouldPreferType(type1, type2) {
return PreferType1
}

if shouldPreferType(type2, type1) {
return PreferType2
}

return ConflictUnresolved
}

// shouldPreferType return true if type1 should be preferred over type2.
func shouldPreferType(type1, type2 string) bool {
switch type1 {
case spec.TypeBoolean, spec.TypeObject, spec.TypeArray:
// Should not prefer boolean, object and array type over any other type.
return false
case spec.TypeNumber:
// Preferring number to integer type.
return type2 == spec.TypeInteger
case spec.TypeString:
// Preferring string to any type.
return true
}

return false
}
170 changes: 170 additions & 0 deletions pkg/spec/conflict_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright © 2021 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spec

import (
"testing"

spec "github.com/getkin/kin-openapi/openapi3"
)

func Test_shouldPreferType(t *testing.T) {
type args struct {
t1 string
t2 string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "should not prefer - bool",
args: args{
t1: spec.TypeBoolean,
},
want: false,
},
{
name: "should not prefer - obj",
args: args{
t1: spec.TypeObject,
},
want: false,
},
{
name: "should not prefer - array",
args: args{
t1: spec.TypeArray,
},
want: false,
},
{
name: "should not prefer - number over object",
args: args{
t1: spec.TypeNumber,
t2: spec.TypeObject,
},
want: false,
},
{
name: "prefer - number over int",
args: args{
t1: spec.TypeNumber,
t2: spec.TypeInteger,
},
want: true,
},
{
name: "prefer - string over anything",
args: args{
t1: spec.TypeString,
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := shouldPreferType(tt.args.t1, tt.args.t2); got != tt.want {
t.Errorf("shouldPreferType() = %v, want %v", got, tt.want)
}
})
}
}

func Test_conflictSolver(t *testing.T) {
type args struct {
t1 string
t2 string
}
tests := []struct {
name string
args args
want int
}{
{
name: "no conflict",
args: args{
t1: spec.TypeNumber,
t2: spec.TypeNumber,
},
want: NoConflict,
},
{
name: "prefer string over anything",
args: args{
t1: spec.TypeString,
t2: spec.TypeNumber,
},
want: PreferType1,
},
{
name: "prefer string over anything",
args: args{
t1: spec.TypeInteger,
t2: spec.TypeString,
},
want: PreferType2,
},
{
name: "prefer number over int",
args: args{
t1: spec.TypeNumber,
t2: spec.TypeInteger,
},
want: PreferType1,
},
{
name: "prefer number over int",
args: args{
t1: spec.TypeInteger,
t2: spec.TypeNumber,
},
want: PreferType2,
},
{
name: "conflict - bool",
args: args{
t1: spec.TypeInteger,
t2: spec.TypeBoolean,
},
want: ConflictUnresolved,
},
{
name: "conflict - obj",
args: args{
t1: spec.TypeObject,
t2: spec.TypeBoolean,
},
want: ConflictUnresolved,
},
{
name: "conflict - array",
args: args{
t1: spec.TypeObject,
t2: spec.TypeArray,
},
want: ConflictUnresolved,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := conflictSolver(tt.args.t1, tt.args.t2); got != tt.want {
t.Errorf("conflictSolver() = %v, want %v", got, tt.want)
}
})
}
}
20 changes: 17 additions & 3 deletions pkg/spec/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,14 @@ func mergeParameter(parameter, parameter2 *spec.Parameter, path *field.Path) (*s
}

type1, type2 := parameter.Schema.Value.Type, parameter2.Schema.Value.Type
if type1 != type2 {
switch conflictSolver(type1, type2) {
case NoConflict, PreferType1:
// do nothing, parameter is used.
case PreferType2:
// use parameter2.
type1 = type2
parameter = parameter2
case ConflictUnresolved:
return parameter, []conflict{
{
path: path,
Expand Down Expand Up @@ -250,7 +257,13 @@ func mergeSchema(schema, schema2 *spec.Schema, path *field.Path) (*spec.Schema,
return s, nil
}

if schema.Type != schema2.Type {
switch conflictSolver(schema.Type, schema2.Type) {
case NoConflict, PreferType1:
// do nothing, schema is used.
case PreferType2:
// use schema2.
schema = schema2
case ConflictUnresolved:
return schema, []conflict{
{
path: path,
Expand All @@ -265,7 +278,8 @@ func mergeSchema(schema, schema2 *spec.Schema, path *field.Path) (*spec.Schema,
case spec.TypeBoolean, spec.TypeInteger, spec.TypeNumber:
return schema, nil
case spec.TypeString:
if schema.Format != schema2.Format {
// Ignore format only if both schemas are string type and formats are different.
if schema2.Type == spec.TypeString && schema.Format != schema2.Format {
schema.Format = ""
}
return schema, nil
Expand Down
Loading

0 comments on commit 7776596

Please sign in to comment.