Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
479 changes: 407 additions & 72 deletions bundler/bundler.go

Large diffs are not rendered by default.

331 changes: 250 additions & 81 deletions bundler/bundler_composer.go

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions bundler/bundler_composer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/pb33f/libopenapi/datamodel"
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.yaml.in/yaml/v4"
Expand Down Expand Up @@ -151,6 +152,155 @@ func TestBundlerComposed_StrangeRefs(t *testing.T) {
}
}

func TestEnqueueDiscriminatorMappingTargets_StripsFragmentWhenNameMissing(t *testing.T) {
tmpDir := t.TempDir()

rootSpec := `openapi: 3.1.0
info:
title: Enqueue Mapping Fragment
version: 1.0.0
paths: {}
components:
schemas:
Animal:
type: object
discriminator:
propertyName: kind
mapping:
cat: './schemas/Cat.yaml#/components/schemas/Cat'`

catSpec := `components:
schemas:
Cat:
type: object`

require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "schemas"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "root.yaml"), []byte(rootSpec), 0644))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "schemas", "Cat.yaml"), []byte(catSpec), 0644))

specBytes, err := os.ReadFile(filepath.Join(tmpDir, "root.yaml"))
require.NoError(t, err)

cfg := datamodel.NewDocumentConfiguration()
cfg.BasePath = tmpDir
cfg.AllowFileReferences = true
cfg.SpecFilePath = "root.yaml"

doc, err := libopenapi.NewDocumentWithConfiguration(specBytes, cfg)
require.NoError(t, err)

v3Doc, err := doc.BuildV3Model()
require.NoError(t, err)
require.NotNil(t, v3Doc)

rolodex := v3Doc.Index.GetRolodex()
mappings := collectDiscriminatorMappingNodesWithContext(rolodex)
require.NotEmpty(t, mappings)

refValue := mappings[0].node.Value
ref, _ := mappings[0].sourceIdx.SearchIndexForReference(refValue)
require.NotNil(t, ref)

// Force name extraction logic by clearing the name.
ref.Name = ""
if !strings.Contains(ref.FullDefinition, "#") {
ref.FullDefinition = ref.FullDefinition + "#/components/schemas/Cat"
}

cf := &handleIndexConfig{
refMap: orderedmap.New[string, *processRef](),
}

enqueueDiscriminatorMappingTargets(mappings, cf, rolodex.GetRootIndex())

pr := cf.refMap.GetOrZero(ref.FullDefinition)
require.NotNil(t, pr)
assert.Equal(t, "Cat", pr.name)
}

func TestDeriveNameFromFullDefinition_StripsFragment(t *testing.T) {
name := deriveNameFromFullDefinition("/tmp/path/Cat.yaml#/components/schemas/Cat")
assert.Equal(t, "Cat", name)
}

func TestResolveDiscriminatorMappingTarget_NilSourceIdx(t *testing.T) {
ref, idx := resolveDiscriminatorMappingTarget(nil, "schemas/Cat.yaml")
assert.Nil(t, ref)
assert.Nil(t, idx)
}

func TestResolveDiscriminatorMappingTarget_NoRolodex(t *testing.T) {
var rootNode yaml.Node
require.NoError(t, yaml.Unmarshal([]byte(`openapi: 3.0.0
info:
title: No Rolodex
version: 1.0.0
paths: {}`), &rootNode))

cfg := index.CreateOpenAPIIndexConfig()
sourceIdx := index.NewSpecIndexWithConfig(&rootNode, cfg)

ref, idx := resolveDiscriminatorMappingTarget(sourceIdx, "schemas/Cat.yaml")
assert.Nil(t, ref)
assert.Nil(t, idx)
}

func TestResolveDiscriminatorMappingTarget_IndexesAndReturnsRef(t *testing.T) {
tmpDir := t.TempDir()

require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "schemas"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "schemas", "Cat.yaml"), []byte("type: object\n"), 0644))

var rootNode yaml.Node
require.NoError(t, yaml.Unmarshal([]byte(`openapi: 3.0.0
info:
title: Root
version: 1.0.0
paths: {}`), &rootNode))

cfg := index.CreateOpenAPIIndexConfig()
cfg.BasePath = tmpDir
cfg.SpecAbsolutePath = ""

sourceIdx := index.NewSpecIndexWithConfig(&rootNode, cfg)
rolodex := index.NewRolodex(cfg)

localFS, err := index.NewLocalFSWithConfig(&index.LocalFSConfig{
BaseDirectory: tmpDir,
IndexConfig: nil,
})
require.NoError(t, err)

rolodex.AddLocalFS(tmpDir, localFS)
sourceIdx.SetRolodex(rolodex)

ref, idx := resolveDiscriminatorMappingTarget(sourceIdx, "schemas/Cat.yaml")
require.NotNil(t, ref)
_ = idx

expected, err := filepath.Abs(filepath.Join(tmpDir, "schemas", "Cat.yaml"))
require.NoError(t, err)

assert.Equal(t, expected, ref.FullDefinition)
assert.Equal(t, expected, ref.RemoteLocation)
assert.Equal(t, filepath.Base(expected), ref.Name)
require.NotNil(t, ref.Node)
assert.NotEqual(t, yaml.DocumentNode, ref.Node.Kind)
}

func TestHandleDiscriminatorMappingIndexes_SkipsRootTarget(t *testing.T) {
rootIdx := &index.SpecIndex{}
cf := &handleIndexConfig{
idx: rootIdx,
rootIdx: rootIdx,
discriminatorMappings: []*discriminatorMappingWithContext{{targetIdx: rootIdx}},
}

err := handleDiscriminatorMappingIndexes(cf, rootIdx, nil)
require.NoError(t, err)
assert.Equal(t, rootIdx, cf.idx)
}

// TestBundleBytesComposed_DiscriminatorMapping tests that composed bundling correctly
// updates discriminator mappings when external schemas are moved to components.
func TestBundleBytesComposed_DiscriminatorMapping(t *testing.T) {
Expand Down
Loading