-
Notifications
You must be signed in to change notification settings - Fork 86
Handlebars template validation and documentation #1030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
d6d204e
ea6d4bd
67389f6
97a32b2
f5854ea
32ebc29
e76c19a
49c9c5a
86281bb
3e48130
32ab54d
bc7eee5
0f1ae93
d4898fe
d6ee0cd
ab1a541
0d298e7
e699759
31644e8
62679a5
17b18db
bd2358e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| // 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 ( | ||
| "errors" | ||
| "io/fs" | ||
| "path" | ||
|
|
||
| "github.com/mailgun/raymond/v2" | ||
|
|
||
| "github.com/elastic/package-spec/v3/code/go/internal/fspath" | ||
| "github.com/elastic/package-spec/v3/code/go/internal/linkedfiles" | ||
| "github.com/elastic/package-spec/v3/code/go/pkg/specerrors" | ||
| ) | ||
|
|
||
| var ( | ||
| errInvalidHandlebarsTemplate = errors.New("invalid handlebars template") | ||
| ) | ||
|
|
||
| // ValidateHandlebarsFiles validates all Handlebars (.hbs) files in the package filesystem. | ||
| // It returns a list of validation errors if any Handlebars files are invalid. | ||
| // hbs are located in both the package root and data stream directories under the agent folder. | ||
| func ValidateHandlebarsFiles(fsys fspath.FS) specerrors.ValidationErrors { | ||
| // template files are placed at /agent/input directory or | ||
| // at the datastream /agent/stream directory | ||
| inputDir := path.Join("agent", "input") | ||
| err := validateTemplateDir(fsys, inputDir) | ||
| if err != nil { | ||
| return specerrors.ValidationErrors{ | ||
| specerrors.NewStructuredErrorf("%w in %s: %w", errInvalidHandlebarsTemplate, inputDir, err), | ||
| } | ||
| } | ||
|
|
||
| datastreamEntries, err := fs.ReadDir(fsys, "data_stream") | ||
| if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
| return specerrors.ValidationErrors{ | ||
| specerrors.NewStructuredErrorf("error reading data_stream directory: %w", err), | ||
| } | ||
| } | ||
| for _, dsEntry := range datastreamEntries { | ||
| if !dsEntry.IsDir() { | ||
| continue | ||
| } | ||
| streamDir := path.Join("data_stream", dsEntry.Name(), "agent", "stream") | ||
| err := validateTemplateDir(fsys, streamDir) | ||
| if err != nil { | ||
| return specerrors.ValidationErrors{ | ||
| specerrors.NewStructuredErrorf("%w in %s: %w", errInvalidHandlebarsTemplate, streamDir, err), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // validateTemplateDir validates all Handlebars files in the given directory. | ||
| func validateTemplateDir(fsys fspath.FS, dir string) error { | ||
| entries, err := fs.ReadDir(fsys, dir) | ||
| if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
| return err | ||
| } | ||
| for _, entry := range entries { | ||
| if path.Ext(entry.Name()) == ".hbs" { | ||
| err := validateHandlebarsEntry(fsys, dir, entry.Name()) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| continue | ||
| } | ||
| if path.Ext(entry.Name()) == ".link" { | ||
| linkFilePath := path.Join(dir, entry.Name()) | ||
| linkFile, err := linkedfiles.NewLinkedFile(fsys.Path(linkFilePath)) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| err = validateHandlebarsEntry(fsys, dir, linkFile.IncludedFilePath) | ||
| if err != nil { | ||
| return err | ||
teresaromero marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| continue | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // validateHandlebarsEntry validates a single Handlebars file located at filePath. | ||
| // it parses the file using the raymond library to check for syntax errors. | ||
| func validateHandlebarsEntry(fsys fspath.FS, dir, entryName string) error { | ||
| if entryName == "" { | ||
| return nil | ||
| } | ||
| filePath := fsys.Path(path.Join(dir, entryName)) | ||
teresaromero marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| _, err := raymond.ParseFile(filePath) | ||
| return err | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| // 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 ( | ||
| "os" | ||
| "path" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/elastic/package-spec/v3/code/go/internal/fspath" | ||
| ) | ||
|
|
||
| func TestValidateTemplateDir(t *testing.T) { | ||
| t.Run("empty directory", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| pkgDir := path.Join(tmpDir, "package") | ||
| err := os.MkdirAll(pkgDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| templateDir := path.Join(pkgDir, "agent", "input") | ||
| err = os.MkdirAll(templateDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| fsys := fspath.DirFS(pkgDir) | ||
| err = validateTemplateDir(fsys, path.Join("agent", "input")) | ||
| require.NoError(t, err) | ||
|
|
||
| }) | ||
| t.Run("valid handlebars file", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| pkgDir := path.Join(tmpDir, "package") | ||
| err := os.MkdirAll(pkgDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| templateDir := path.Join(pkgDir, "agent", "input") | ||
| err = os.MkdirAll(templateDir, 0o755) | ||
| require.NoError(t, err) | ||
| hbsFilePath := path.Join(templateDir, "template.hbs") | ||
| hbsContent := `{{#if condition}}Valid Handlebars{{/if}}` | ||
| err = os.WriteFile(hbsFilePath, []byte(hbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| fsys := fspath.DirFS(pkgDir) | ||
| err = validateTemplateDir(fsys, path.Join("agent", "input")) | ||
| require.NoError(t, err) | ||
| }) | ||
| t.Run("invalid handlebars file", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| pkgDir := path.Join(tmpDir, "package") | ||
| err := os.MkdirAll(pkgDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| templateDir := path.Join(pkgDir, "agent", "input") | ||
| err = os.MkdirAll(templateDir, 0o755) | ||
| require.NoError(t, err) | ||
| hbsFilePath := path.Join(templateDir, "template.hbs") | ||
| hbsContent := `{{#if condition}}Valid Handlebars` | ||
| err = os.WriteFile(hbsFilePath, []byte(hbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| fsys := fspath.DirFS(pkgDir) | ||
| err = validateTemplateDir(fsys, path.Join("agent", "input")) | ||
| require.Error(t, err) | ||
| }) | ||
| t.Run("valid linked handlebars file", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| pkgDir := path.Join(tmpDir, "package") | ||
| err := os.MkdirAll(pkgDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| pkgDirLinked := path.Join(tmpDir, "linked") | ||
| err = os.MkdirAll(pkgDirLinked, 0o755) | ||
| require.NoError(t, err) | ||
| linkedHbsFilePath := path.Join(pkgDirLinked, "linked_template.hbs") | ||
| linkedHbsContent := `{{#if condition}}Valid Linked Handlebars{{/if}}` | ||
| err = os.WriteFile(linkedHbsFilePath, []byte(linkedHbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| templateDir := path.Join(pkgDir, "agent", "input") | ||
| err = os.MkdirAll(templateDir, 0o755) | ||
| require.NoError(t, err) | ||
| hbsFilePath := path.Join(templateDir, "template.hbs.link") | ||
| hbsContent := `../../../linked/linked_template.hbs` | ||
| err = os.WriteFile(hbsFilePath, []byte(hbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| fsys := fspath.DirFS(pkgDir) | ||
| err = validateTemplateDir(fsys, path.Join("agent", "input")) | ||
| require.NoError(t, err) | ||
|
|
||
| }) | ||
| t.Run("invalid linked handlebars file", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| pkgDir := path.Join(tmpDir, "package") | ||
| err := os.MkdirAll(pkgDir, 0o755) | ||
| require.NoError(t, err) | ||
|
|
||
| pkgDirLinked := path.Join(tmpDir, "linked") | ||
| err = os.MkdirAll(pkgDirLinked, 0o755) | ||
| require.NoError(t, err) | ||
| linkedHbsFilePath := path.Join(pkgDirLinked, "linked_template.hbs") | ||
| linkedHbsContent := `{{#if condition}}Valid Linked Handlebars` | ||
| err = os.WriteFile(linkedHbsFilePath, []byte(linkedHbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| templateDir := path.Join(pkgDir, "agent", "input") | ||
| err = os.MkdirAll(templateDir, 0o755) | ||
| require.NoError(t, err) | ||
| hbsFilePath := path.Join(templateDir, "template.hbs.link") | ||
| hbsContent := `../../../linked/linked_template.hbs` | ||
| err = os.WriteFile(hbsFilePath, []byte(hbsContent), 0o644) | ||
| require.NoError(t, err) | ||
|
|
||
| fsys := fspath.DirFS(pkgDir) | ||
| err = validateTemplateDir(fsys, path.Join("agent", "input")) | ||
| require.Error(t, err) | ||
| }) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.