Validate final identity fields as application payload

This commit is contained in:
2026-10-24 00:12:31 +00:00
parent dcf0379e02
commit 085b57eeb6
6 changed files with 74 additions and 12 deletions

View File

@@ -406,7 +406,7 @@ func validateRegisteredSequences(claims []compositionClaim, identityFieldsByPath
contribution := validatedSequence{items: node.Sequence}
records, diagnostics := validateNormalizedSequenceIdentityFrom(node, claim.path, identityFields, sequenceIdentityDiagnosticCodes{
invalid: CodeComposeInvalid, conflict: CodeComposeConflict,
}, start)
}, start, sequenceStructuralRecord)
if len(diagnostics) != 0 {
return nil, nil, diagnostics
}

View File

@@ -23,7 +23,7 @@ func validateFinalSequenceIdentities(root *document.Node, policy sequencePolicy)
if fields != nil {
_, issues := validateNormalizedSequenceIdentityFrom(node, path, fields, sequenceIdentityDiagnosticCodes{
invalid: CodeSequenceInvalid, conflict: CodeSequenceConflict,
}, 0)
}, 0, sequencePayloadRecord)
diagnostics = append(diagnostics, issues...)
}
return

View File

@@ -59,7 +59,7 @@ func analyzeIdentifiedInheritanceGraph(sequence *document.Node, scopePath Path,
}}
}
lookup, diagnostics := validateNormalizedSequenceIdentityFrom(
sequence, scopePath, fields, sequenceCombinationIdentityCodes, 0,
sequence, scopePath, fields, sequenceCombinationIdentityCodes, 0, sequenceStructuralRecord,
)
if len(diagnostics) != 0 {
return nil, diagnostics
@@ -136,7 +136,7 @@ func identifiedInheritanceReference(reference *document.Node, path Path, identit
)}
}
}
index := newSequenceMappingIndex(reference)
index := newSequenceMappingIndex(reference, sequencePayloadRecord)
tuple, _, missing, invalid := index.identity(identityFields)
if missing != "" || invalid >= 0 || invalid == -2 {
value := reference

View File

@@ -28,6 +28,13 @@ type sequenceIdentityRecord struct {
location document.Location
}
type sequenceIdentityContext uint8
const (
sequenceStructuralRecord sequenceIdentityContext = iota
sequencePayloadRecord
)
// normalizeSequenceIdentityFields treats identity field names as a set.
func normalizeSequenceIdentityFields(fields []string) ([]string, error) {
if len(fields) == 0 {
@@ -150,21 +157,21 @@ func sequenceIdentityTuple(values []*document.Node) (string, error) {
return encoded.String(), nil
}
// sequenceMappingIndex is built once per record. Structural fields are omitted
// by entry index, so a directive is excluded only at its actual occurrence.
// sequenceMappingIndex is built once per record. Structural lookup excludes the
// directive's occurrence; effective payload and reference mappings keep all keys.
type sequenceMappingIndex struct {
entries []document.Entry
byKey map[string]int
}
func newSequenceMappingIndex(record *document.Node) sequenceMappingIndex {
func newSequenceMappingIndex(record *document.Node, context sequenceIdentityContext) sequenceMappingIndex {
index := sequenceMappingIndex{byKey: make(map[string]int)}
if record == nil || record.Kind != document.Mapping {
return index
}
index.entries = record.Mapping
directiveIndex := -1
if directive, exists := inheritanceDirective(record); exists {
if directive, exists := inheritanceDirective(record); context == sequenceStructuralRecord && exists {
for entryIndex := range record.Mapping {
if record.Mapping[entryIndex].Key == directive.Key && record.Mapping[entryIndex].KeyLocation == directive.KeyLocation {
directiveIndex = entryIndex
@@ -227,10 +234,10 @@ func validateSequenceIdentityFrom(sequence *document.Node, path Path, fields []s
}
return nil, []Diagnostic{{Code: codes.invalid, Severity: SeverityError, Path: path, Message: "sequence identity requires at least one field", Primary: location}}
}
return validateNormalizedSequenceIdentityFrom(sequence, path, normalized, codes, startIndex)
return validateNormalizedSequenceIdentityFrom(sequence, path, normalized, codes, startIndex, sequenceStructuralRecord)
}
func validateNormalizedSequenceIdentityFrom(sequence *document.Node, path Path, normalized []string, codes sequenceIdentityDiagnosticCodes, startIndex int) (map[string]sequenceIdentityRecord, []Diagnostic) {
func validateNormalizedSequenceIdentityFrom(sequence *document.Node, path Path, normalized []string, codes sequenceIdentityDiagnosticCodes, startIndex int, context sequenceIdentityContext) (map[string]sequenceIdentityRecord, []Diagnostic) {
if sequence == nil || sequence.Kind != document.Sequence {
location := (*Location)(nil)
if sequence != nil {
@@ -251,7 +258,7 @@ func validateNormalizedSequenceIdentityFrom(sequence *document.Node, path Path,
}
return nil, []Diagnostic{{Code: codes.invalid, Severity: SeverityError, Path: itemPath, Message: "identified collection items must be mappings", Primary: primary}}
}
mapping := newSequenceMappingIndex(item)
mapping := newSequenceMappingIndex(item, context)
tuple, locations, missing, entryIndex := mapping.identity(normalized)
if entryIndex == -1 {
location := publicLocation(item.Location)