diff --git a/cmd/filterList.go b/cmd/filterList.go
new file mode 100644
index 0000000..3e2dc61
--- /dev/null
+++ b/cmd/filterList.go
@@ -0,0 +1,142 @@
+// Copyright (c) 2024 Parseable, Inc
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "fmt"
+ "os"
+ "pb/pkg/model"
+ "strings"
+ "time"
+
+ "github.com/spf13/cobra"
+)
+
+var FilterList = &cobra.Command{
+ Use: "list",
+ Example: "pb query list ",
+ Short: "List of saved filter for a stream",
+ Long: "\nShow a list of saved filter for a stream ",
+ PreRunE: PreRunDefaultProfile,
+ Run: func(command *cobra.Command, args []string) {
+ client := DefaultClient()
+
+ p := model.UiApp()
+ _, err := p.Run()
+ if err != nil {
+ os.Exit(1)
+ }
+
+ a := model.FilterToApply()
+ d := model.FilterToDelete()
+ if a.Stream() != "" {
+ filterToPbQuery(a.Stream(), a.StartTime(), a.EndTime())
+ }
+ if d.FilterId() != "" {
+ deleteFilter(&client, d.FilterId())
+ }
+ },
+}
+
+// Delete a saved filter from the list of filter
+func deleteFilter(client *HTTPClient, filterID string) {
+ deleteUrl := `filters/filter/` + filterID
+ req, err := client.NewRequest("DELETE", deleteUrl, nil)
+ if err != nil {
+ fmt.Println("Error deleting the filter")
+ }
+
+ resp, err := client.client.Do(req)
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == 200 {
+ fmt.Printf("\n\nFilter Deleted")
+ }
+}
+
+// Convert a filter to executable pb query
+func filterToPbQuery(query string, start string, end string) {
+ var timeStamps string
+ if start == "" || end == "" {
+ timeStamps = ``
+ } else {
+ startFormatted := formatToRFC3339(start)
+ endFormatted := formatToRFC3339(end)
+ timeStamps = ` --from=` + startFormatted + ` --to=` + endFormatted
+ }
+ queryTemplate := `pb query run ` + query + timeStamps
+ fmt.Printf("\nCopy and paste the command")
+ fmt.Printf("\n\n%s\n\n", queryTemplate)
+}
+
+// Parses all UTC time format from string to time interface
+func parseTimeToFormat(input string) (time.Time, error) {
+ // List of possible formats
+ formats := []string{
+ time.RFC3339,
+ "2006-01-02 15:04:05",
+ "2006-01-02",
+ "01/02/2006 15:04:05",
+ "02-Jan-2006 15:04:05 MST",
+ "2006-01-02T15:04:05Z",
+ "02-Jan-2006",
+ }
+
+ var err error
+ var t time.Time
+
+ for _, format := range formats {
+ t, err = time.Parse(format, input)
+ if err == nil {
+ return t, nil
+ }
+ }
+
+ return t, fmt.Errorf("unable to parse time: %s", input)
+}
+
+// Converts to RFC3339
+func convertTime(input string) (string, error) {
+ t, err := parseTimeToFormat(input)
+ if err != nil {
+ return "", err
+ }
+
+ return t.Format(time.RFC3339), nil
+}
+
+// Converts User inputted time to string type RFC3339 time
+func formatToRFC3339(time string) string {
+ var formattedTime string
+ if len(strings.Fields(time)) > 1 {
+ newTime := strings.Fields(time)[0:2]
+ rfc39990time, err := convertTime(strings.Join(newTime, " "))
+ if err != nil {
+ fmt.Println("error formatting time")
+ }
+ formattedTime = rfc39990time
+ } else {
+ rfc39990time, err := convertTime(time)
+ if err != nil {
+ fmt.Println("error formatting time")
+ }
+ formattedTime = rfc39990time
+ }
+ return formattedTime
+}
diff --git a/cmd/query.go b/cmd/query.go
index dbc2948..4db92e3 100644
--- a/cmd/query.go
+++ b/cmd/query.go
@@ -21,11 +21,11 @@ import (
"io"
"os"
"pb/pkg/config"
- "pb/pkg/model"
+ // "pb/pkg/model"
"strings"
"time"
-
- tea "github.com/charmbracelet/bubbletea"
+ //! This dependancy is required by the interactive flag Do not remove
+ // tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
)
@@ -45,8 +45,8 @@ var (
saveFilterTimeFlag = "with-time"
saveFilterTimeShort = "w"
- interactiveFlag = "interactive"
- interactiveFlagShort = "i"
+ // interactiveFlag = "interactive"
+ // interactiveFlagShort = "i"
)
var query = &cobra.Command{
@@ -65,9 +65,9 @@ var query = &cobra.Command{
fmt.Println("please enter your query")
fmt.Printf("Example:\n pb query run \"select * from frontend\" --from=10m --to=now\n")
return nil
- } else {
- query = args[0]
}
+
+ query = args[0]
start, err := command.Flags().GetString(startFlag)
if err != nil {
@@ -85,15 +85,16 @@ var query = &cobra.Command{
end = defaultEnd
}
- interactive, err := command.Flags().GetBool(interactiveFlag)
- if err != nil {
- return err
- }
+ //TODO: Interactive Flag disabled
+ // interactive, err := command.Flags().GetBool(interactiveFlag)
+ // if err != nil {
+ // return err
+ // }
- startTime, endTime, err := parseTime(start, end)
- if err != nil {
- return err
- }
+ // startTime, endTime, err := parseTime(start, end)
+ // if err != nil {
+ // return err
+ // }
keepTime, err := command.Flags().GetBool(saveFilterTimeFlag)
if err != nil {
@@ -106,14 +107,15 @@ var query = &cobra.Command{
}
filterNameTrimmed := strings.Trim(filterName, " ")
- if interactive {
- p := tea.NewProgram(model.NewQueryModel(DefaultProfile, query, startTime, endTime), tea.WithAltScreen())
- if _, err := p.Run(); err != nil {
- fmt.Printf("there's been an error: %v", err)
- os.Exit(1)
- }
- return nil
- }
+ //TODO: Interactive Flag disabled
+ // if interactive {
+ // p := tea.NewProgram(model.NewQueryModel(DefaultProfile, query, startTime, endTime), tea.WithAltScreen())
+ // if _, err := p.Run(); err != nil {
+ // fmt.Printf("there's been an error: %v", err)
+ // os.Exit(1)
+ // }
+ // return nil
+ // }
// Checks if there is filter name which is not empty. Empty filter name wont be allowed
if command.Flags().Changed(saveFilterFlag) {
@@ -143,7 +145,7 @@ var query = &cobra.Command{
var QueryCmd = func() *cobra.Command {
query.Flags().BoolP(saveFilterTimeFlag, saveFilterTimeShort, false, "Save the time range associated in the query to the filter") // save time for a filter flag; default value = false (boolean type)
- query.Flags().BoolP(interactiveFlag, interactiveFlagShort, false, "open the query result in interactive mode")
+ // query.Flags().BoolP(interactiveFlag, interactiveFlagShort, false, "open the query result in interactive mode")
query.Flags().StringP(startFlag, startFlagShort, defaultStart, "Start time for query. Takes date as '2024-10-12T07:20:50.52Z' or string like '10m', '1hr'")
query.Flags().StringP(endFlag, endFlagShort, defaultEnd, "End time for query. Takes date as '2024-10-12T07:20:50.52Z' or 'now'")
query.Flags().StringP(saveFilterFlag, saveFilterShort, "", "Save a query filter") // save filter flag. Default value = FILTER_NAME (type string)
diff --git a/go.mod b/go.mod
index f0f3e42..42f5166 100644
--- a/go.mod
+++ b/go.mod
@@ -4,8 +4,9 @@ go 1.22
require (
github.com/apache/arrow/go/v13 v13.0.0
- github.com/charmbracelet/bubbles v0.16.1
- github.com/charmbracelet/bubbletea v0.26.4
+ github.com/charmbracelet/bubbles v0.18.0
+ github.com/charmbracelet/bubbletea v0.26.6
+ github.com/charmbracelet/lipgloss v0.12.1
github.com/dustin/go-humanize v1.0.1
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8
golang.org/x/term v0.21.0
@@ -13,7 +14,7 @@ require (
)
require (
- github.com/charmbracelet/x/ansi v0.1.2 // indirect
+ github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/charmbracelet/x/input v0.1.0 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.0 // indirect
@@ -37,17 +38,16 @@ require (
require (
github.com/atotto/clipboard v0.1.4 // indirect
- github.com/charmbracelet/lipgloss v0.7.1
github.com/evertras/bubble-table v0.15.2
github.com/muesli/termenv v0.15.2
github.com/pelletier/go-toml/v2 v2.0.9
- github.com/sahilm/fuzzy v0.1.0 // indirect
+ github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
diff --git a/go.sum b/go.sum
index 859bae7..2094cee 100644
--- a/go.sum
+++ b/go.sum
@@ -4,14 +4,14 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
-github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
-github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40=
-github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0=
-github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
-github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
-github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY=
-github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
+github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
+github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
+github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
+github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
+github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
+github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
+github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
+github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
@@ -46,8 +46,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
@@ -72,8 +72,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
-github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
diff --git a/main.go b/main.go
index 5985d38..e974f11 100644
--- a/main.go
+++ b/main.go
@@ -113,6 +113,7 @@ func main() {
stream.AddCommand(cmd.StatStreamCmd)
query.AddCommand(cmd.QueryCmd)
+ query.AddCommand(cmd.FilterList)
cli.AddCommand(profile)
cli.AddCommand(query)
diff --git a/pkg/model/savedFilters.go b/pkg/model/savedFilters.go
new file mode 100644
index 0000000..e40f65e
--- /dev/null
+++ b/pkg/model/savedFilters.go
@@ -0,0 +1,267 @@
+// Copyright (c) 2024 Parseable, Inc
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "pb/pkg/config"
+ "strings"
+ "time"
+
+ "github.com/charmbracelet/bubbles/key"
+ "github.com/charmbracelet/bubbles/list"
+ tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+)
+
+const (
+ applyFilterButton = "a"
+ deleteFilterButton = "d"
+)
+
+var docStyle = lipgloss.NewStyle().Margin(1, 2)
+
+// FilterDetails represents the structure of filter data
+type FilterDetails struct {
+ FilterId string `json:"filter_id"`
+ FilterName string `json:"filter_name"`
+ StreamName string `json:"stream_name"`
+ QueryField map[string]interface{} `json:"query"`
+ TimeFilter map[string]interface{} `json:"time_filter"`
+}
+
+type item struct {
+ id, title, stream, desc, from, to string
+}
+
+var (
+ titleStyles = lipgloss.NewStyle().PaddingLeft(0).Bold(true).Foreground(lipgloss.Color("9"))
+ queryStyle = lipgloss.NewStyle().PaddingLeft(0).Foreground(lipgloss.Color("7"))
+ itemStyle = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("8"))
+ // selectedItemStyle = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("170"))
+ selectedItemStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.AdaptiveColor{Light: "16", Dark: "226"})
+)
+
+type itemDelegate struct{}
+
+func (d itemDelegate) Height() int { return 4 }
+func (d itemDelegate) Spacing() int { return 1 }
+func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
+func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
+ i, ok := listItem.(item)
+ if !ok {
+ return
+ }
+ var str string
+
+ if i.from != "" || i.to != "" {
+ str = fmt.Sprintf("From: %s\nTo: %s", i.from, i.to)
+ } else {
+ str = ""
+ }
+
+ fn := itemStyle.Render
+ tr := titleStyles.Render
+ qr := queryStyle.Render
+ if index == m.Index() {
+ tr = func(s ...string) string {
+ return selectedItemStyle.Render("> " + strings.Join(s, " "))
+ }
+ }
+
+ fmt.Fprint(w, fn(tr(i.title)+"\n"+qr(i.desc)+"\n"+str))
+}
+
+func (d itemDelegate) ShortHelp() []key.Binding {
+ return []key.Binding{
+ key.NewBinding(
+ key.WithKeys(applyFilterButton),
+ key.WithHelp(applyFilterButton, "apply"),
+ ),
+ key.NewBinding(
+ key.WithKeys(deleteFilterButton),
+ key.WithHelp(deleteFilterButton, "delete"),
+ ),
+ }
+}
+
+// FullHelp returns the extended list of keybindings.
+func (d itemDelegate) FullHelp() [][]key.Binding {
+ return [][]key.Binding{
+ {
+ key.NewBinding(
+ key.WithKeys(applyFilterButton),
+ key.WithHelp(applyFilterButton, "apply"),
+ ),
+ key.NewBinding(
+ key.WithKeys(deleteFilterButton),
+ key.WithHelp(deleteFilterButton, "delete"),
+ ),
+ },
+ }
+}
+
+var selectedFilterApply item
+var selectedFilterDelete item
+
+func (i item) Title() string { return fmt.Sprintf("Filter:%s, Query:%s", i.title, i.desc) }
+
+func (i item) Description() string {
+ if i.to == "" || i.from == "" {
+ return ""
+ }
+ return fmt.Sprintf("From:%s To:%s", i.from, i.to)
+}
+
+func (i item) FilterValue() string { return i.title }
+func (i item) FilterId() string { return i.id }
+func (i item) Stream() string { return i.desc }
+func (i item) StartTime() string { return i.from }
+func (i item) EndTime() string { return i.to }
+
+type modelFilter struct {
+ list list.Model
+}
+
+func (m modelFilter) Init() tea.Cmd {
+ return nil
+}
+
+func (m modelFilter) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+ switch msg := msg.(type) {
+ case tea.KeyMsg:
+ if msg.String() == "ctrl+c" {
+ return m, tea.Quit
+ }
+ if msg.String() == "a" || msg.Type == tea.KeyEnter {
+ selectedFilterApply = m.list.SelectedItem().(item)
+ return m, tea.Quit
+ }
+ if msg.String() == "d" {
+ selectedFilterDelete = m.list.SelectedItem().(item)
+ return m, tea.Quit
+
+ }
+ case tea.WindowSizeMsg:
+ h, v := docStyle.GetFrameSize()
+ m.list.SetSize(msg.Width-h, msg.Height-v)
+ }
+
+ var cmd tea.Cmd
+ m.list, cmd = m.list.Update(msg)
+ return m, cmd
+}
+
+func (m modelFilter) View() string {
+ return docStyle.Render(m.list.View())
+}
+
+// Interactive list for the user to display all the available filters (only saved SQL filters )
+func UiApp() *tea.Program {
+
+ userConfig, err := config.ReadConfigFromFile()
+ if err != nil {
+ fmt.Println("Error reading Default Profile")
+ }
+ var userProfile config.Profile
+ if profile, ok := userConfig.Profiles[userConfig.DefaultProfile]; ok {
+ userProfile = profile
+ }
+
+ client := &http.Client{
+ Timeout: time.Second * 60,
+ }
+ userFilters := fetchFilters(client, &userProfile)
+
+ m := modelFilter{list: list.New(userFilters, itemDelegate{}, 0, 0)}
+ m.list.Title = fmt.Sprintf("Saved Filters for User: %s", userProfile.Username)
+
+ return tea.NewProgram(m, tea.WithAltScreen())
+
+}
+
+// fetchFilters fetches filters from the server and sends them to the channel
+func fetchFilters(client *http.Client, profile *config.Profile) []list.Item {
+
+ endpoint := fmt.Sprintf("%s/%s/%s", profile.URL, "api/v1/filters", profile.Username)
+ req, err := http.NewRequest("GET", endpoint, nil)
+ if err != nil {
+ fmt.Println("Error creating request:", err)
+ return nil
+ }
+
+ req.SetBasicAuth(profile.Username, profile.Password)
+ req.Header.Add("Content-Type", "application/json")
+ resp, err := client.Do(req)
+ if err != nil {
+ fmt.Println("Error making request:", err)
+ return nil
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Println("Error reading response body:", err)
+ return nil
+ }
+
+ var filters []FilterDetails
+ err = json.Unmarshal(body, &filters)
+ if err != nil {
+ fmt.Println("Error unmarshaling response:", err)
+ return nil
+ }
+ var userFilters []list.Item
+ for _, filter := range filters {
+ var userFilter item
+ queryBytes, _ := json.Marshal(filter.QueryField["filter_query"])
+
+ // Extract "from" and "to" from time_filter
+ var from, to string
+ if fromValue, exists := filter.TimeFilter["from"]; exists {
+ from = fmt.Sprintf("%v", fromValue)
+ }
+ if toValue, exists := filter.TimeFilter["to"]; exists {
+ to = fmt.Sprintf("%v", toValue)
+ }
+ // filtering only SQL type filters Filter_name is tile and Stream Name is desc
+ if string(queryBytes) != "null" {
+ userFilter = item{
+ id: filter.FilterId,
+ title: filter.FilterName,
+ stream: filter.StreamName,
+ desc: string(queryBytes),
+ from: from,
+ to: to,
+ }
+ userFilters = append(userFilters, userFilter)
+ }
+ }
+ return userFilters
+
+}
+
+// returns the selected filter by user in the iteractive list
+func FilterToApply() item {
+ return selectedFilterApply
+}
+
+// returns the selected filter by user in the iteractive list
+func FilterToDelete() item {
+ return selectedFilterDelete
+}