code/go/internal/validator/semantic/validate_required_fields.go (83 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. package semantic import ( "fmt" "github.com/elastic/package-spec/v3/code/go/internal/fspath" "github.com/elastic/package-spec/v3/code/go/pkg/specerrors" ) // ValidateRequiredFields validates that required fields are present and have the expected // types. func ValidateRequiredFields(fsys fspath.FS) specerrors.ValidationErrors { requiredFields := map[string]string{ "data_stream.type": "constant_keyword", "data_stream.dataset": "constant_keyword", "data_stream.namespace": "constant_keyword", "@timestamp": "date", } return validateRequiredFields(fsys, requiredFields) } type unexpectedTypeRequiredField struct { field string expectedType string foundType string dataStream string fullPath string } func (e unexpectedTypeRequiredField) Error() string { return fmt.Sprintf("expected type %q for required field %q, found %q in %q", e.expectedType, e.field, e.foundType, e.fullPath) } type notFoundRequiredField struct { field string expectedType string dataStream string } func (e notFoundRequiredField) Error() string { message := fmt.Sprintf("expected field %q with type %q not found", e.field, e.expectedType) if e.dataStream != "" { message = fmt.Sprintf("%s in datastream %q", message, e.dataStream) } return message } func validateRequiredFields(fsys fspath.FS, requiredFields map[string]string) specerrors.ValidationErrors { // map datastream/package -> field name -> found foundFields := make(map[string]map[string]struct{}) checkField := func(metadata fieldFileMetadata, f field) specerrors.ValidationErrors { if _, ok := foundFields[metadata.dataStream]; !ok { foundFields[metadata.dataStream] = make(map[string]struct{}) } foundFields[metadata.dataStream][f.Name] = struct{}{} expectedType, found := requiredFields[f.Name] if !found { return nil } // Check if type is the expected one, but only for fields what are // not declared as external. External fields won't have a type in // the definition. // More info in https://github.com/elastic/elastic-package/issues/749 if f.External == "" && f.Type != expectedType { return specerrors.ValidationErrors{ specerrors.NewStructuredError( unexpectedTypeRequiredField{ field: f.Name, foundType: f.Type, dataStream: metadata.dataStream, fullPath: metadata.fullFilePath, expectedType: expectedType, }, specerrors.UnassignedCode, ), } } return nil } errs := validateFields(fsys, checkField) // Using the data streams found here, since there could not be a data stream // without the `fields` folder or an input package without that folder for dataStream, dataStreamFields := range foundFields { for requiredName, requiredType := range requiredFields { if _, found := dataStreamFields[requiredName]; !found { errs = append(errs, specerrors.NewStructuredError( notFoundRequiredField{ field: requiredName, expectedType: requiredType, dataStream: dataStream, }, specerrors.UnassignedCode, ), ) } } } return errs }