diff --git a/datamodel/high/base/schema.go b/datamodel/high/base/schema.go index 8dc22da..00e9ef7 100644 --- a/datamodel/high/base/schema.go +++ b/datamodel/high/base/schema.go @@ -323,10 +323,10 @@ func NewSchema(schema *base.Schema) *Schema { // for every item, build schema async buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) { n := &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: sch.GetValueNode(), + ValueNode: sch.ValueNode, Value: sch.Value, } - n.SetReference(sch.GetReference(), sch.GetValueNode()) + n.SetReference(sch.GetReference(), sch.GetReferenceNode()) p := NewSchemaProxy(n) diff --git a/datamodel/high/base/schema_proxy.go b/datamodel/high/base/schema_proxy.go index 4961993..612c7c5 100644 --- a/datamodel/high/base/schema_proxy.go +++ b/datamodel/high/base/schema_proxy.go @@ -122,7 +122,7 @@ func (sp *SchemaProxy) GetReference() string { func (sp *SchemaProxy) GetReferenceNode() *yaml.Node { if sp.refStr != "" { - return nil + return utils.CreateStringNode(sp.refStr) } return sp.schema.GetValue().GetReferenceNode() } diff --git a/datamodel/low/extraction_functions.go b/datamodel/low/extraction_functions.go index cdcef53..4658a4b 100644 --- a/datamodel/low/extraction_functions.go +++ b/datamodel/low/extraction_functions.go @@ -779,157 +779,6 @@ func ExtractMapExtensions[PT Buildable[N], N any]( return nil, labelNode, valueNode, nil } -// ExtractMapExtensions will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'label' is -// used to locate the node to be extracted from the root node supplied. Supply a bit to decide if extensions should -// be included or not. required in some use cases. -// -// The second return value is the yaml.Node found for the 'label' and the third return value is the yaml.Node -// found for the value extracted from the label node. -func ExtractMapExtensionsOld[PT Buildable[N], N any]( - ctx context.Context, - label string, - root *yaml.Node, - idx *index.SpecIndex, - extensions bool, -) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) { - var referenceValue string - var labelNode, valueNode *yaml.Node - var circError error - root = utils.NodeAlias(root) - if rf, rl, rv := utils.IsNodeRefValue(root); rf { - // locate reference in index. - ref, fIdx, err, fCtx := LocateRefNodeWithContext(ctx, root, idx) - if ref != nil { - valueNode = ref - labelNode = rl - referenceValue = rv - ctx = fCtx - idx = fIdx - if err != nil { - circError = err - } - } else { - return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s", - root.Content[1].Value) - } - } else { - _, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content) - valueNode = utils.NodeAlias(valueNode) - if valueNode != nil { - if h, _, rvt := utils.IsNodeRefValue(valueNode); h { - ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, valueNode, idx) - if ref != nil { - valueNode = ref - referenceValue = rvt - idx = fIdx - ctx = nCtx - if err != nil { - circError = err - } - } else { - if err != nil { - return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s", - err.Error()) - } - } - } - } - } - if valueNode != nil { - var currentLabelNode *yaml.Node - valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]() - - // TODO: Convert to datamodel.TranslatePipeline. - bChan := make(chan mappingResult[PT]) - eChan := make(chan error) - - buildMap := func(nctx context.Context, label *yaml.Node, value *yaml.Node, c chan mappingResult[PT], ec chan<- error, ref string, fIdx *index.SpecIndex) { - var n PT = new(N) - value = utils.NodeAlias(value) - _ = BuildModel(value, n) - err := n.Build(nctx, label, value, fIdx) - if err != nil { - ec <- err - return - } - - if ref != "" { - SetReference(n, ref, nil) - } - - c <- mappingResult[PT]{ - k: KeyReference[string]{ - KeyNode: label, - Value: label.Value, - }, - v: ValueReference[PT]{ - Value: n, - ValueNode: value, - // Reference: ref, - }, - } - } - - totalKeys := 0 - for i, en := range valueNode.Content { - en = utils.NodeAlias(en) - referenceValue = "" - if i%2 == 0 { - currentLabelNode = en - continue - } - - foundIndex := idx - foundContext := ctx - - // check our valueNode isn't a reference still. - if h, _, refVal := utils.IsNodeRefValue(en); h { - ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, en, idx) - if ref != nil { - en = ref - referenceValue = refVal - if fIdx != nil { - foundIndex = fIdx - } - foundContext = nCtx - if err != nil { - circError = err - } - } else { - if err != nil { - return nil, labelNode, valueNode, fmt.Errorf("flat map build failed: reference cannot be found: %s", - err.Error()) - } - } - } - - if !extensions { - if strings.HasPrefix(currentLabelNode.Value, "x-") { - continue // yo, don't pay any attention to extensions, not here anyway. - } - } - totalKeys++ - go buildMap(foundContext, currentLabelNode, en, bChan, eChan, referenceValue, foundIndex) - } - - completedKeys := 0 - for completedKeys < totalKeys { - select { - case err := <-eChan: - return valueMap, labelNode, valueNode, err - case res := <-bChan: - completedKeys++ - valueMap.Set(res.k, res.v) - } - } - if circError != nil && !idx.AllowCircularReferenceResolving() { - return valueMap, labelNode, valueNode, circError - } - return valueMap, labelNode, valueNode, nil - } - return nil, labelNode, valueNode, nil -} - // ExtractMap will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'label' is // used to locate the node to be extracted from the root node supplied. // diff --git a/datamodel/low/extraction_functions_test.go b/datamodel/low/extraction_functions_test.go index 7e42f22..5dfbb85 100644 --- a/datamodel/low/extraction_functions_test.go +++ b/datamodel/low/extraction_functions_test.go @@ -18,6 +18,7 @@ import ( "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -2063,3 +2064,45 @@ func TestArray_NotRefNotArray(t *testing.T) { assert.Equal(t, err.Error(), "array build failed, input is not an array, line 2, column 3") assert.Len(t, things, 0) } + +func TestHashExtensions(t *testing.T) { + type args struct { + ext *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "empty", + args: args{ + ext: orderedmap.New[KeyReference[string], ValueReference[*yaml.Node]](), + }, + want: []string{}, + }, + { + name: "hashes extensions", + args: args{ + ext: orderedmap.ToOrderedMap(map[KeyReference[string]]ValueReference[*yaml.Node]{ + {Value: "x-burger"}: { + Value: utils.CreateStringNode("yummy"), + }, + {Value: "x-car"}: { + Value: utils.CreateStringNode("ford"), + }, + }), + }, + want: []string{ + "x-burger-2a296977a4572521773eb7e7773cc054fae3e8589511ce9bf90cec7dd93d016a", + "x-car-7d3aa6a5c79cdb0c2585daed714fa0936a18e6767b2dcc804992a90f6d0b8f5e", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hash := HashExtensions(tt.args.ext) + assert.Equal(t, tt.want, hash) + }) + } +} diff --git a/orderedmap/builder.go b/orderedmap/builder.go index c9221cd..aa06d72 100644 --- a/orderedmap/builder.go +++ b/orderedmap/builder.go @@ -55,16 +55,8 @@ func (o *Map[K, V]) ToYamlNode(n NodeBuilder, l any) *yaml.Node { for pair := First(o); pair != nil; pair = pair.Next() { var k any = pair.Key() if m, ok := k.(marshaler); ok { // TODO marshal inline? - k, _ = m.MarshalYAML() - } - - var y any - y, ok := k.(yaml.Node) - if !ok { - y, ok = k.(*yaml.Node) - } - if ok { - b, _ := yaml.Marshal(y) + mk, _ := m.MarshalYAML() + b, _ := yaml.Marshal(mk) k = strings.TrimSpace(string(b)) } diff --git a/orderedmap/builder_test.go b/orderedmap/builder_test.go new file mode 100644 index 0000000..c2c4a52 --- /dev/null +++ b/orderedmap/builder_test.go @@ -0,0 +1,140 @@ +package orderedmap_test + +import ( + "testing" + + "github.com/pb33f/libopenapi/datamodel/high" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestOrderedMap_ToYamlNode(t *testing.T) { + type args struct { + om any + low any + } + tests := []struct { + name string + args args + want string + }{ + { + name: "simple ordered map", + args: args{ + om: orderedmap.ToOrderedMap(map[string]string{ + "one": "two", + "three": "four", + }), + }, + want: `one: two +three: four +`, + }, + { + name: "simple ordered map with low representation", + args: args{ + om: orderedmap.ToOrderedMap(map[string]string{ + "one": "two", + "three": "four", + }), + low: low.NodeReference[*orderedmap.Map[*low.KeyReference[string], *low.ValueReference[string]]]{ + Value: orderedmap.ToOrderedMap(map[*low.KeyReference[string]]*low.ValueReference[string]{ + {Value: "one", KeyNode: utils.CreateStringNode("one")}: {Value: "two", ValueNode: utils.CreateStringNode("two")}, + }), + ValueNode: utils.CreateYamlNode(orderedmap.ToOrderedMap(map[string]string{ + "one": "two", + "three": "four", + })), + }, + }, + want: `one: two +three: four +`, + }, + { + name: "ordered map with KeyReference", + args: args{ + om: orderedmap.ToOrderedMap(map[*low.KeyReference[string]]string{ + { + KeyNode: utils.CreateStringNode("one"), + }: "two", + { + KeyNode: utils.CreateStringNode("three"), + }: "four", + }), + }, + want: `one: two +three: four + `, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nb := new(high.NodeBuilder) + + node := tt.args.om.(orderedmap.MapToYamlNoder).ToYamlNode(nb, tt.args.low) + b, err := yaml.Marshal(node) + require.NoError(t, err) + require.Equal(t, tt.want, string(b)) + }) + } +} + +type findValueUntyped interface { + FindValueUntyped(k string) any +} + +func TestOrderedMap_FindValueUntyped(t *testing.T) { + type args struct { + om any + key string + } + tests := []struct { + name string + args args + want any + }{ + { + name: "find value in simple ordered map", + args: args{ + om: orderedmap.ToOrderedMap(map[string]string{ + "one": "two", + "three": "four", + }), + key: "one", + }, + want: "two", + }, + { + name: "unable to find value in simple ordered map", + args: args{ + om: orderedmap.ToOrderedMap(map[string]string{ + "one": "two", + "three": "four", + }), + key: "five", + }, + want: nil, + }, + { + name: "find value in ordered map with KeyReference", + args: args{ + om: orderedmap.ToOrderedMap(map[*low.KeyReference[string]]string{ + {Value: "one"}: "two", + {Value: "three"}: "four", + }), + key: "three", + }, + want: "four", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + value := tt.args.om.(findValueUntyped).FindValueUntyped(tt.args.key) + require.Equal(t, tt.want, value) + }) + } +} diff --git a/orderedmap/orderedmap.go b/orderedmap/orderedmap.go index fc660ef..c4400d5 100644 --- a/orderedmap/orderedmap.go +++ b/orderedmap/orderedmap.go @@ -157,7 +157,7 @@ func ToOrderedMap[K comparable, V any](m map[K]V) *Map[K, V] { for k, v := range m { om.Set(k, v) } - return om + return SortAlpha(om) } // First returns map's first pair for iteration.