Addressed comments from review and fixed bug with schema props

props did not have context, therefore they had no idea where they were or where to resolve from.

Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
quobix
2023-11-02 10:28:29 -04:00
parent 80b2b2d0b5
commit 8bbb022daa
6 changed files with 43 additions and 35 deletions

View File

@@ -720,7 +720,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
} }
// handle properties // handle properties
props, err := buildPropertyMap(root, idx, PropertiesLabel) props, err := buildPropertyMap(ctx, root, idx, PropertiesLabel)
if err != nil { if err != nil {
return err return err
} }
@@ -729,7 +729,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
} }
// handle dependent schemas // handle dependent schemas
props, err = buildPropertyMap(root, idx, DependentSchemasLabel) props, err = buildPropertyMap(ctx, root, idx, DependentSchemasLabel)
if err != nil { if err != nil {
return err return err
} }
@@ -738,7 +738,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
} }
// handle pattern properties // handle pattern properties
props, err = buildPropertyMap(root, idx, PatternPropertiesLabel) props, err = buildPropertyMap(ctx, root, idx, PatternPropertiesLabel)
if err != nil { if err != nil {
return err return err
} }
@@ -1036,11 +1036,11 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
return nil return nil
} }
func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) { func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) {
// for property, build in a new thread! // for property, build in a new thread!
bChan := make(chan schemaProxyBuildResult) bChan := make(chan schemaProxyBuildResult)
buildProperty := func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool, buildProperty := func(ctx context.Context, label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool,
refString string, refString string,
) { ) {
c <- schemaProxyBuildResult{ c <- schemaProxyBuildResult{
@@ -1049,7 +1049,7 @@ func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low
Value: label.Value, Value: label.Value,
}, },
v: low.ValueReference[*SchemaProxy]{ v: low.ValueReference[*SchemaProxy]{
Value: &SchemaProxy{kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString}, Value: &SchemaProxy{ctx: ctx, kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString},
ValueNode: value, ValueNode: value,
}, },
} }
@@ -1066,22 +1066,24 @@ func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low
continue continue
} }
foundCtx := ctx
// check our prop isn't reference // check our prop isn't reference
isRef := false isRef := false
refString := "" refString := ""
if h, _, l := utils.IsNodeRefValue(prop); h { if h, _, l := utils.IsNodeRefValue(prop); h {
ref, _, _ := low.LocateRefNode(prop, idx) ref, _, _, fctx := low.LocateRefNodeWithContext(ctx, prop, idx)
if ref != nil { if ref != nil {
isRef = true isRef = true
prop = ref prop = ref
refString = l refString = l
foundCtx = fctx
} else { } else {
return nil, fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", return nil, fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d",
prop.Content[1].Value, prop.Content[1].Line, prop.Content[1].Column) prop.Content[1].Value, prop.Content[1].Line, prop.Content[1].Column)
} }
} }
totalProps++ totalProps++
go buildProperty(currentProp, prop, bChan, isRef, refString) go buildProperty(foundCtx, currentProp, prop, bChan, isRef, refString)
} }
completedProps := 0 completedProps := 0
for completedProps < totalProps { for completedProps < totalProps {

View File

@@ -109,8 +109,17 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
} else { } else {
if specPath != "" { if specPath != "" {
var abs string
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(specPath), explodedRefValue[0])) // multi file ref, looking for the root.
if filepath.Base(specPath) == "root.yaml" && explodedRefValue[0] == "" {
abs = specPath
} else {
if explodedRefValue[0] == "" {
abs = specPath
} else {
abs, _ = filepath.Abs(filepath.Join(filepath.Dir(specPath), explodedRefValue[0]))
}
}
rv = fmt.Sprintf("%s#%s", abs, explodedRefValue[1]) rv = fmt.Sprintf("%s#%s", abs, explodedRefValue[1])
} else { } else {

View File

@@ -28,8 +28,18 @@ type ResolvingError struct {
} }
func (r *ResolvingError) Error() string { func (r *ResolvingError) Error() string {
return fmt.Sprintf("%s: %s [%d:%d]", r.ErrorRef.Error(), errs := utils.UnwrapErrors(r.ErrorRef)
r.Path, r.Node.Line, r.Node.Column) var msgs []string
for _, e := range errs {
if idxErr, ok := e.(*IndexingError); ok {
msgs = append(msgs, fmt.Sprintf("%s: %s [%d:%d]", idxErr.Error(),
idxErr.Path, idxErr.Node.Line, idxErr.Node.Column))
} else {
msgs = append(msgs, fmt.Sprintf("%s: %s [%d:%d]", e.Error(),
r.Path, r.Node.Line, r.Node.Column))
}
}
return strings.Join(msgs, "\n")
} }
// Resolver will use a *index.SpecIndex to stitch together a resolved root tree using all the discovered // Resolver will use a *index.SpecIndex to stitch together a resolved root tree using all the discovered

View File

@@ -30,19 +30,13 @@ import (
// how the index is set up. // how the index is set up.
func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecIndex { func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecIndex {
index := new(SpecIndex) index := new(SpecIndex)
//if config != nil && config.seenRemoteSources == nil {
// config.seenRemoteSources = &syncmap.Map{}
//}
//config.remoteLock = &sync.Mutex{}
index.config = config index.config = config
index.rolodex = config.Rolodex index.rolodex = config.Rolodex
//index.parentIndex = config.ParentIndex
index.uri = config.uri index.uri = config.uri
index.specAbsolutePath = config.SpecAbsolutePath index.specAbsolutePath = config.SpecAbsolutePath
if rootNode == nil || len(rootNode.Content) <= 0 { if rootNode == nil || len(rootNode.Content) <= 0 {
return index return index
} }
if config.Logger != nil { if config.Logger != nil {
index.logger = config.Logger index.logger = config.Logger
} else { } else {
@@ -50,7 +44,6 @@ func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecI
Level: slog.LevelError, Level: slog.LevelError,
})) }))
} }
boostrapIndexCollections(rootNode, index) boostrapIndexCollections(rootNode, index)
return createNewIndex(rootNode, index, config.AvoidBuildIndex) return createNewIndex(rootNode, index, config.AvoidBuildIndex)
} }
@@ -99,15 +92,10 @@ func createNewIndex(rootNode *yaml.Node, index *SpecIndex, avoidBuildOut bool) *
index.BuildIndex() index.BuildIndex()
} }
// do a copy!
//index.config.seenRemoteSources.Range(func(k, v any) bool {
// index.seenRemoteSources[k.(string)] = v.(*yaml.Node)
// return true
//})
return index return index
} }
// BuildIndex will run all of the count operations required to build up maps of everything. It's what makes the index // BuildIndex will run all the count operations required to build up maps of everything. It's what makes the index
// useful for looking up things, the count operations are all run in parallel and then the final calculations are run // useful for looking up things, the count operations are all run in parallel and then the final calculations are run
// the index is ready. // the index is ready.
func (index *SpecIndex) BuildIndex() { func (index *SpecIndex) BuildIndex() {
@@ -446,11 +434,6 @@ func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Refer
return index.opServersRefs return index.opServersRefs
} }
//// GetAllExternalIndexes will return all indexes for external documents
//func (index *SpecIndex) GetAllExternalIndexes() map[string]*SpecIndex {
// return index.externalSpecIndex
//}
// SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want // SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want
// to allow or disallow circular references to be resolved or visited // to allow or disallow circular references to be resolved or visited
func (index *SpecIndex) SetAllowCircularReferenceResolving(allow bool) { func (index *SpecIndex) SetAllowCircularReferenceResolving(allow bool) {
@@ -984,12 +967,10 @@ func (index *SpecIndex) GetOperationCount() int {
Path: fmt.Sprintf("$.paths.%s.%s", p.Value, m.Value), Path: fmt.Sprintf("$.paths.%s.%s", p.Value, m.Value),
ParentNode: m, ParentNode: m,
} }
//index.pathRefsLock.Lock()
if locatedPathRefs[p.Value] == nil { if locatedPathRefs[p.Value] == nil {
locatedPathRefs[p.Value] = make(map[string]*Reference) locatedPathRefs[p.Value] = make(map[string]*Reference)
} }
locatedPathRefs[p.Value][ref.Name] = ref locatedPathRefs[p.Value][ref.Name] = ref
//index.pathRefsLock.Unlock()
// update // update
opCount++ opCount++
} }
@@ -997,11 +978,9 @@ func (index *SpecIndex) GetOperationCount() int {
} }
} }
} }
index.pathRefsLock.Lock()
for k, v := range locatedPathRefs { for k, v := range locatedPathRefs {
index.pathRefs[k] = v index.pathRefs[k] = v
} }
index.pathRefsLock.Unlock()
index.operationCount = opCount index.operationCount = opCount
return opCount return opCount
} }

View File

@@ -7,5 +7,9 @@ func UnwrapErrors(err error) []error {
if err == nil { if err == nil {
return []error{} return []error{}
} }
return err.(interface{ Unwrap() []error }).Unwrap() if uw, ok := err.(interface{ Unwrap() []error }); ok {
return uw.Unwrap()
} else {
return []error{err}
}
} }

View File

@@ -30,3 +30,7 @@ func TestUnwrapErrors(t *testing.T) {
func TestUnwrapErrors_Empty(t *testing.T) { func TestUnwrapErrors_Empty(t *testing.T) {
assert.Len(t, UnwrapErrors(nil), 0) assert.Len(t, UnwrapErrors(nil), 0)
} }
func TestUnwrapErrors_SingleError(t *testing.T) {
assert.Len(t, UnwrapErrors(errors.New("single error")), 1)
}