scripts/generate-docs/exported_fields.go (142 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 main import ( "fmt" "io/ioutil" "path/filepath" "sort" "strings" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) type fieldDefinition struct { Name string `yaml:"name,omitempty"` Type string `yaml:"type,omitempty"` Description string `yaml:"description,omitempty"` Fields fieldDefinitionArray `yaml:"fields,omitempty"` Enabled *bool `yaml:"enabled,omitempty"` } type fieldDefinitionArray []*fieldDefinition func (s fieldDefinitionArray) Len() int { return len(s) } func (s fieldDefinitionArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s fieldDefinitionArray) Less(i, j int) bool { return s[i].Name < s[j].Name } type fieldsTableRecord struct { name string description string aType string } func renderExportedFields(options generateOptions, packageName, dataStreamName string) (string, error) { dataStreamPath := filepath.Join(options.packagesSourceDir, packageName, "data_stream", dataStreamName) fieldFiles, err := listFieldFields(dataStreamPath) if err != nil { return "", errors.Wrapf(err, "listing field files failed (dataStreamPath: %s)", dataStreamPath) } fields, err := loadFields(fieldFiles) if err != nil { return "", errors.Wrap(err, "loading fields files failed") } collected, err := collectFieldsFromDefinitions(fields) if err != nil { return "", errors.Wrap(err, "collecting fields files failed") } var builder strings.Builder builder.WriteString("#### Exported fields\n\n") if len(collected) == 0 { builder.WriteString("(no fields available)\n") return builder.String(), nil } builder.WriteString("| Field | Description | Type |\n") builder.WriteString("|---|---|---|\n") for _, c := range collected { description := strings.TrimSpace(strings.ReplaceAll(c.description, "\n", " ")) builder.WriteString(fmt.Sprintf("| %s | %s | %s |\n", c.name, description, c.aType)) } return builder.String(), nil } func listFieldFields(dataStreamPath string) ([]string, error) { fieldsPath := filepath.Join(dataStreamPath, "fields") var files []string fileInfos, err := ioutil.ReadDir(fieldsPath) if err != nil { return nil, errors.Wrapf(err, "reading dataStream fields dir failed (path: %s)", fieldsPath) } for _, fileInfo := range fileInfos { if !fileInfo.IsDir() { files = append(files, filepath.Join(fieldsPath, fileInfo.Name())) } } return files, nil } func loadFields(files []string) (fieldDefinitionArray, error) { var fdas fieldDefinitionArray for _, f := range files { var fda fieldDefinitionArray body, err := ioutil.ReadFile(f) if err != nil { return nil, errors.Wrapf(err, "reading fields file failed (path: %s)", f) } err = yaml.Unmarshal(body, &fda) if err != nil { return nil, errors.Wrapf(err, "unmarshaling fields file failed (path: %s)", f) } fdas = append(fdas, fda...) } return fdas, nil } func collectFieldsFromDefinitions(fieldDefinitions fieldDefinitionArray) ([]fieldsTableRecord, error) { var records []fieldsTableRecord var err error var disabledField string sort.Sort(fieldDefinitions) for _, f := range fieldDefinitions { records, err = visitFields("", f, records, &disabledField) if err != nil { return nil, errors.Wrapf(err, "visiting fields failed") } } sort.Slice(records, func(i, j int) bool { return sort.StringsAreSorted([]string{records[i].name, records[j].name}) }) return uniqueTableRecords(records), nil } func visitFields(namePrefix string, f *fieldDefinition, records []fieldsTableRecord, disabledField *string) ([]fieldsTableRecord, error) { var name = namePrefix if namePrefix != "" { name += "." } name += f.Name if disabledField != nil && *disabledField != "" && strings.HasPrefix(name, *disabledField) { // skipping the field because a top level field was disabled return records, nil } if f.Enabled != nil && *f.Enabled == false { // found a new disabled field so use that one from now on // this works because all the fields are sorted so the top level fields are encountered first *disabledField = name return records, nil } if len(f.Fields) == 0 && f.Type != "group" { records = append(records, fieldsTableRecord{ name: name, description: f.Description, aType: f.Type, }) return records, nil } var err error sort.Sort(f.Fields) for _, fieldEntry := range f.Fields { records, err = visitFields(name, fieldEntry, records, disabledField) if err != nil { return nil, errors.Wrapf(err, "recursive visiting fields failed (namePrefix: %s)", namePrefix) } } return records, nil } func uniqueTableRecords(records []fieldsTableRecord) []fieldsTableRecord { fieldNames := make(map[string]bool) var unique []fieldsTableRecord for _, r := range records { if _, ok := fieldNames[r.name]; !ok { fieldNames[r.name] = true unique = append(unique, r) } } return unique }