Files
Steve Munene 5a6e0343dc NOISSUE - Fix Report time range display (#330)
* fx to and from

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* change to UTC

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix template pagination and address comment

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* revert env variable

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix pagination

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
2025-10-09 16:28:15 +02:00

165 lines
4.4 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package reports
import (
"encoding/json"
"fmt"
"text/template"
"text/template/parse"
)
type ReportTemplate string
func (temp ReportTemplate) String() string {
return string(temp)
}
func (temp ReportTemplate) MarshalJSON() ([]byte, error) {
return json.Marshal(string(temp))
}
func (temp *ReportTemplate) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*temp = ReportTemplate(s)
return nil
}
func (temp ReportTemplate) Validate() error {
templateStr := string(temp)
// Validate template syntax using Go's template parser
tmpl := template.New("validate").Funcs(template.FuncMap{
"add": func(a, b int) int { return a + b },
"sub": func(a, b int) int { return a - b },
"div": func(a, b int) int {
if b == 0 {
return 0
}
return a / b
},
"mod": func(a, b int) int {
if b == 0 {
return 0
}
return a % b
},
"eq": func(a, b int) bool { return a == b },
"ge": func(a, b int) bool { return a >= b },
"lt": func(a, b int) bool { return a < b },
"iterate": func(count int) []int { return make([]int, count) },
"getStartRow": func(pageNum, firstPageRows, continuationPageRows int) int { return 0 },
"getEndRow": func(pageNum, firstPageRows, continuationPageRows, totalMessages int) int { return 0 },
"formatTime": func(t any) string { return "" },
"formatValue": func(v any) string { return "" },
})
parsed, err := tmpl.Parse(templateStr)
if err != nil {
return fmt.Errorf("template syntax error: %w", err)
}
var hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd bool
// Validate essential fields are present using template parsing
if err := validateEssentialFields(parsed.Tree.Root, &hasTitle, &hasRange, &hasFormatTime, &hasFormatValue, &hasEnd); err != nil {
return err
}
if !hasTitle {
return fmt.Errorf("missing essential template field: {{$.Title}}")
}
if !hasRange {
return fmt.Errorf("missing essential template field: {{range .Messages}} or {{range .Reports}}")
}
if !hasFormatTime {
return fmt.Errorf("missing essential template field: {{formatTime .Time}}")
}
if !hasFormatValue {
return fmt.Errorf("missing essential template field: {{formatValue .}}")
}
if !hasEnd {
return fmt.Errorf("missing essential template field: {{end}}")
}
return nil
}
func validateEssentialFields(node parse.Node, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd *bool) error {
if node == nil {
return nil
}
switch n := node.(type) {
case *parse.ListNode:
for _, sub := range n.Nodes {
if err := validateEssentialFields(sub, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
}
case *parse.ActionNode:
if n.Pipe != nil {
for _, cmd := range n.Pipe.Cmds {
cmdStr := cmd.String()
if cmdStr == "$.Title" {
*hasTitle = true
}
if len(cmd.Args) > 0 {
firstArg := cmd.Args[0].String()
if firstArg == "formatTime" {
*hasFormatTime = true
}
if firstArg == "formatValue" {
*hasFormatValue = true
}
}
}
}
case *parse.RangeNode:
if n.Pipe != nil && len(n.Pipe.Cmds) > 0 {
cmdStr := n.Pipe.Cmds[0].String()
// Accept .Messages, .Reports, or $report.Messages
if cmdStr == ".Messages" || cmdStr == ".Reports" || cmdStr == "$report.Messages" {
*hasRange = true
}
}
if err := validateEssentialFields(n.List, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
if n.ElseList != nil {
if err := validateEssentialFields(n.ElseList, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
}
*hasEnd = true
case *parse.IfNode:
if err := validateEssentialFields(n.List, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
if n.ElseList != nil {
if err := validateEssentialFields(n.ElseList, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
}
case *parse.WithNode:
if err := validateEssentialFields(n.List, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
if n.ElseList != nil {
if err := validateEssentialFields(n.ElseList, hasTitle, hasRange, hasFormatTime, hasFormatValue, hasEnd); err != nil {
return err
}
}
}
return nil
}