@@ -40,8 +40,6 @@ type MappingValidator struct {
4040 indexTemplateName string
4141
4242 dataStreamName string
43-
44- LocalSchema []FieldDefinition
4543}
4644
4745// MappingValidatorOption represents an optional flag that can be passed to CreateValidatorForMappings.
@@ -115,6 +113,14 @@ func WithMappingValidatorDataStream(dataStream string) MappingValidatorOption {
115113 }
116114}
117115
116+ // WithMappingValidatorDataStream configures the Data Stream to query in Elasticsearch.
117+ func WithMappingValidatorFallbackSchema (schema []FieldDefinition ) MappingValidatorOption {
118+ return func (v * MappingValidator ) error {
119+ v .Schema = schema
120+ return nil
121+ }
122+ }
123+
118124// CreateValidatorForMappings function creates a validator for the mappings.
119125func CreateValidatorForMappings (fieldsParentDir string , esClient * elasticsearch.Client , opts ... MappingValidatorOption ) (v * MappingValidator , err error ) {
120126 p := packageRoot {}
@@ -130,6 +136,10 @@ func createValidatorForMappingsAndPackageRoot(fieldsParentDir string, finder pac
130136 }
131137 }
132138
139+ if len (v .Schema ) > 0 {
140+ return v , nil
141+ }
142+
133143 fieldsDir := filepath .Join (fieldsParentDir , "fields" )
134144
135145 var fdm * DependencyManager
@@ -151,10 +161,7 @@ func createValidatorForMappingsAndPackageRoot(fieldsParentDir string, finder pac
151161 return nil , fmt .Errorf ("can't load fields from directory (path: %s): %w" , fieldsDir , err )
152162 }
153163
154- // Load field definitions from package in a different Validator variable
155- // It allows to check whether or not a given mapping comes from ECS
156- // Or just check if a packages contains some specific field definition (e.g. type array)
157- v .LocalSchema = fields
164+ v .Schema = append (fields , v .Schema ... )
158165 return v , nil
159166}
160167
@@ -201,7 +208,7 @@ func (v *MappingValidator) ValidateIndexMappings(ctx context.Context) multierror
201208 // - If the same mapping exists in both, but they have different "type", there is some issue
202209 // - If there is a new mapping,
203210 // - It could come from a ECS definition, compare that mapping with the ECS field definitions
204- // - Does this come from some dynamic template? ECS componente template or dynamic templates defined in the package? This mapping is valid
211+ // - Does this come from some dynamic template? ECS components template or dynamic templates defined in the package? This mapping is valid
205212 // - conditions found in current dynamic templates: match, path_match, path_unmatch, match_mapping_type, unmatch_mapping_type
206213 // - if it does not match, there should be some issue and it should be reported
207214 // - If the mapping is a constant_keyword type (e.g. data_stream.dataset), how to check the value?
@@ -224,7 +231,7 @@ func (v *MappingValidator) ValidateIndexMappings(ctx context.Context) multierror
224231 return errs .Unique ()
225232 }
226233
227- mappingErrs := compareMappings ("" , rawPreview , rawActual , v .Schema , v . LocalSchema )
234+ mappingErrs := compareMappings ("" , rawPreview , rawActual , v .Schema )
228235 errs = append (errs , mappingErrs ... )
229236
230237 if len (errs ) > 0 {
@@ -258,6 +265,9 @@ func isLocalFieldTypeArray(field string, schema []FieldDefinition) bool {
258265 if definition == nil {
259266 return false
260267 }
268+ if definition .External != "" {
269+ return false
270+ }
261271 return definition .Type == "array"
262272}
263273
@@ -352,15 +362,22 @@ func isMultiFields(definition map[string]any) bool {
352362 return true
353363}
354364
355- func validateMappingInSchema (currentPath string , definition map [string ]any , schema []FieldDefinition ) error {
365+ func validateMappingInECSSchema (currentPath string , definition map [string ]any , schema []FieldDefinition ) error {
356366 found := FindElementDefinition (currentPath , schema )
357367 if found == nil {
358368 return fmt .Errorf ("missing definition for path" )
359369 }
360370
371+ if found .External != "ecs" {
372+ return fmt .Errorf ("missing definition for path" )
373+ }
374+
375+ logger .Debugf ("Found ECS field %q for path %s (External %q)" , found .Name , currentPath , found .External )
376+
361377 if found .Type != mappingParameter ("type" , definition ) {
362378 return fmt .Errorf ("mapping type does not match with ECS definition" )
363379 }
380+ // any other field to validate here?
364381 return nil
365382}
366383
@@ -450,7 +467,7 @@ func validateConstantKeywordField(path string, preview, actual map[string]any) (
450467 return isConstantKeyword , nil
451468}
452469
453- func compareMappings (path string , preview , actual map [string ]any , ecsSchema , localSchema []FieldDefinition ) multierror.Error {
470+ func compareMappings (path string , preview , actual map [string ]any , schema []FieldDefinition ) multierror.Error {
454471 var errs multierror.Error
455472
456473 isConstantKeywordType , err := validateConstantKeywordField (path , preview , actual )
@@ -484,7 +501,7 @@ func compareMappings(path string, preview, actual map[string]any, ecsSchema, loc
484501 errs = append (errs , fmt .Errorf ("found invalid properties type in actual mappings for path %q: %w" , path , err ))
485502 }
486503 // logger.Debugf(">>> Comparing field with properties (object): %q", path)
487- compareErrors := compareMappings (path , previewProperties , actualProperties , ecsSchema , localSchema )
504+ compareErrors := compareMappings (path , previewProperties , actualProperties , schema )
488505 errs = append (errs , compareErrors ... )
489506
490507 if len (errs ) == 0 {
@@ -508,21 +525,21 @@ func compareMappings(path string, preview, actual map[string]any, ecsSchema, loc
508525 errs = append (errs , fmt .Errorf ("found invalid multi_fields type in actual mappings for path %q: %w" , path , err ))
509526 }
510527 // logger.Debugf(">>> Comparing multi_fields: %q", path)
511- compareErrors := compareMappings (path , previewFields , actualFields , ecsSchema , localSchema )
528+ compareErrors := compareMappings (path , previewFields , actualFields , schema )
512529 errs = append (errs , compareErrors ... )
513530 // not returning here to keep validating the other fields of this object if any
514531 }
515532
516533 // Compare and validate the elements under "properties": objects or fields and its parameters
517- propertiesErrs := validateObjectProperties (path , containsMultifield , actual , preview , localSchema , ecsSchema )
534+ propertiesErrs := validateObjectProperties (path , containsMultifield , actual , preview , schema )
518535 errs = append (errs , propertiesErrs ... )
519536 if len (errs ) == 0 {
520537 return nil
521538 }
522539 return errs .Unique ()
523540}
524541
525- func validateObjectProperties (path string , containsMultifield bool , actual , preview map [string ]any , localSchema , ecsSchema []FieldDefinition ) multierror.Error {
542+ func validateObjectProperties (path string , containsMultifield bool , actual , preview map [string ]any , schema []FieldDefinition ) multierror.Error {
526543 var errs multierror.Error
527544 for key , value := range actual {
528545 if containsMultifield && key == "fields" {
@@ -544,14 +561,14 @@ func validateObjectProperties(path string, containsMultifield bool, actual, prev
544561 continue
545562 }
546563 errs = append (errs ,
547- validateMappingsNotInPreview (currentPath , childField , localSchema , ecsSchema )... ,
564+ validateMappingsNotInPreview (currentPath , childField , schema )... ,
548565 )
549566 }
550567
551568 continue
552569 }
553570
554- fieldErrs := validateObjectMappingAndParameters (preview [key ], value , currentPath , ecsSchema , localSchema )
571+ fieldErrs := validateObjectMappingAndParameters (preview [key ], value , currentPath , schema )
555572 errs = append (errs , fieldErrs ... )
556573 }
557574 if len (errs ) == 0 {
@@ -562,7 +579,7 @@ func validateObjectProperties(path string, containsMultifield bool, actual, prev
562579
563580// validateMappingsNotInPreview validates the object and the nested objects in the current path with other resources
564581// like ECS schema, dynamic templates or local fields defined in the package (type array).
565- func validateMappingsNotInPreview (currentPath string , childField map [string ]any , localSchema , ecsSchema []FieldDefinition ) multierror.Error {
582+ func validateMappingsNotInPreview (currentPath string , childField map [string ]any , schema []FieldDefinition ) multierror.Error {
566583 var errs multierror.Error
567584 logger .Debugf ("Calculating flatten fields for %s" , currentPath )
568585 flattenFields , err := flattenMappings (currentPath , childField )
@@ -580,7 +597,7 @@ func validateMappingsNotInPreview(currentPath string, childField map[string]any,
580597 continue
581598 }
582599
583- if isLocalFieldTypeArray (fieldPath , localSchema ) {
600+ if isLocalFieldTypeArray (fieldPath , schema ) {
584601 logger .Debugf ("Found field definition with type array, skipping path: %q" , fieldPath )
585602 continue
586603 }
@@ -589,7 +606,7 @@ func validateMappingsNotInPreview(currentPath string, childField map[string]any,
589606 // just raise an error if both validation processes fail
590607
591608 // are all fields under this key defined in ECS?
592- err = validateMappingInSchema (fieldPath , def , ecsSchema )
609+ err = validateMappingInECSSchema (fieldPath , def , schema )
593610 if err != nil {
594611 logger .Warnf ("undefined path %q (pending to check dynamic templates)" , fieldPath )
595612 errs = append (errs , fmt .Errorf ("field %q is undefined: %w" , fieldPath , err ))
@@ -600,7 +617,7 @@ func validateMappingsNotInPreview(currentPath string, childField map[string]any,
600617
601618// validateObjectMappingAndParameters validates the current object or field parameter (currentPath) comparing the values
602619// in the actual mapping with the values in the preview mapping.
603- func validateObjectMappingAndParameters (previewValue , actualValue any , currentPath string , ecsSchema , localSchema []FieldDefinition ) multierror.Error {
620+ func validateObjectMappingAndParameters (previewValue , actualValue any , currentPath string , schema []FieldDefinition ) multierror.Error {
604621 var errs multierror.Error
605622 switch actualValue .(type ) {
606623 case map [string ]any :
@@ -614,7 +631,7 @@ func validateObjectMappingAndParameters(previewValue, actualValue any, currentPa
614631 errs = append (errs , fmt .Errorf ("unexpected type in actual mappings for path: %q" , currentPath ))
615632 }
616633 logger .Debugf (">>>> Comparing Mappings map[string]any: path %s" , currentPath )
617- errs = append (errs , compareMappings (currentPath , previewField , actualField , ecsSchema , localSchema )... )
634+ errs = append (errs , compareMappings (currentPath , previewField , actualField , schema )... )
618635 case any :
619636 // Validate each setting/parameter of the mapping
620637 // If a mapping exist in both preview and actual, they should be the same. But forcing to compare each parameter just in case
@@ -623,15 +640,16 @@ func validateObjectMappingAndParameters(previewValue, actualValue any, currentPa
623640 if previewValue == actualValue {
624641 return nil
625642 }
643+ // Get the string representation via JSON Marshalling
626644 previewData , err := json .Marshal (previewValue )
627645 if err != nil {
628- errs = append (errs , fmt .Errorf ("error unmarshalling preview value %s (path: %s): %w" , previewValue , currentPath , err ))
646+ errs = append (errs , fmt .Errorf ("error marshalling preview value %s (path: %s): %w" , previewValue , currentPath , err ))
629647 return errs
630648 }
631649
632650 actualData , err := json .Marshal (actualValue )
633651 if err != nil {
634- errs = append (errs , fmt .Errorf ("error unmarshalling actual value %s (path: %s): %w" , actualValue , currentPath , err ))
652+ errs = append (errs , fmt .Errorf ("error marshalling actual value %s (path: %s): %w" , actualValue , currentPath , err ))
635653 return errs
636654 }
637655 errs = append (errs , fmt .Errorf ("unexpected value found in mapping for field %q: preview mappings value (%s) different from the actual mappings value (%s)" , currentPath , string (previewData ), string (actualData )))
0 commit comments