Skip to content

Commit

Permalink
Update query command to output JSON (#29)
Browse files Browse the repository at this point in the history
Fixes #21
  • Loading branch information
trueleo authored Oct 19, 2023
1 parent cc60210 commit 39054ff
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 40 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,31 @@ pb profile default local

#### Query

To query a stream, run:
##### Interactive Mode

To run query/view data in interactive TUI mode use

```bash
pb query i <stream-name> --duration=5
```

##### Getting JSON output

You can also query directly and output json data to stdout.

```bash
pb query <stream-name>
pb query "select * from table" --from=10m --to=now
```

or specifying time range in rfc3999

```bash
pb query "select * from table" --from=2023-01-00T01:40:00.000Z --to=2023-01-00T01:55:00.000Z
```

![note]
Query command outputs data to stdout. Output json is not beautified. Use tool like `jq` to format and filter json output.

![pb query](https://github.com/parseablehq/.github/blob/main/images/pb/query.png?raw=true)

#### Streams
Expand Down
136 changes: 136 additions & 0 deletions cmd/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright (c) 2023 Cloudnatively Services Pvt Ltd
//
// 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 <http://www.gnu.org/licenses/>.

package cmd

import (
"bytes"
"fmt"
"io"
"os"
"pb/pkg/model"
"strconv"

tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
)

var (
durationFlag = "duration"
durationFlagShort = "d"
defaultDuration = "10"

startFlag = "from"
startFlagShort = "f"
defaultStart = "1m"

endFlag = "to"
endFlagShort = "t"
defaultEnd = "now"
)

var queryInteractive = &cobra.Command{
Use: "i [stream-name] --duration 10",
Example: " pb query frontend --duration 10",
Short: "Interactive query table view",
Long: "\n command is used to open a prompt to query a stream.",
Args: cobra.ExactArgs(1),
PreRunE: PreRunDefaultProfile,
RunE: func(command *cobra.Command, args []string) error {
stream := args[0]
duration, _ := command.Flags().GetString(durationFlag)

if duration == "" {
duration = defaultDuration
}
durationInt, err := strconv.Atoi(duration)
if err != nil {
return err
}

p := tea.NewProgram(model.NewQueryModel(DefaultProfile, stream, uint(durationInt)), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Printf("there's been an error: %v", err)
os.Exit(1)
}

return nil
},
}

var queryJSON = &cobra.Command{
Use: "query [query] --from=10m --to=now",
Example: " pb query \"select * from frontend\" --from=10m --to=now",
Short: "Run SQL query",
Long: "\nquery command is used to run query. Output format is json string",
Args: cobra.ExactArgs(1),
PreRunE: PreRunDefaultProfile,
RunE: func(command *cobra.Command, args []string) error {
query := args[0]
start, _ := command.Flags().GetString(startFlag)
end, _ := command.Flags().GetString(endFlag)

if start == "" {
start = defaultStart
}
if end == "" {
end = defaultEnd
}

client := DefaultClient()
return fetchData(&client, query, start, end)
},
}

var QueryInteractiveCmd = func() *cobra.Command {
queryInteractive.Flags().StringP(durationFlag, durationFlagShort, defaultDuration, "specify the duration in minutes for which queries should be executed. Defaults to 10 minutes")
return queryInteractive
}()

var QueryCmd = func() *cobra.Command {
queryJSON.Flags().StringP(startFlag, startFlagShort, defaultStart, "Specify start datetime of query. Supports RFC3999 time format and durations (ex. 10m, 1hr ..) ")
queryJSON.Flags().StringP(endFlag, endFlagShort, defaultEnd, "Specify end datetime of query. Supports RFC3999 time format and literal - now ")
queryJSON.AddCommand(queryInteractive)
return queryJSON
}()

func fetchData(client *HTTPClient, query string, startTime string, endTime string) (err error) {
queryTemplate := `{
"query": "%s",
"startTime": "%s",
"endTime": "%s"
}
`

finalQuery := fmt.Sprintf(queryTemplate, query, startTime, endTime)

req, err := client.NewRequest("POST", "query", bytes.NewBuffer([]byte(finalQuery)))
if err != nil {
return
}
resp, err := client.client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()

if resp.StatusCode != 200 {
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
} else {
io.Copy(os.Stdout, resp.Body)
}
return
}
41 changes: 3 additions & 38 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ package main

import (
"errors"
"fmt"
"os"
"pb/cmd"
"pb/pkg/config"
"pb/pkg/model"
"strconv"

tea "github.com/charmbracelet/bubbletea"
"github.com/spf13/cobra"
)

Expand All @@ -36,11 +32,8 @@ var (
)

var (
durationFlag = "duration"
durationFlagShort = "d"
versionFlag = "version"
versionFlagShort = "v"
defaultDuration = "10"
versionFlag = "version"
versionFlagShort = "v"
)

func defaultInitialProfile() config.Profile {
Expand Down Expand Up @@ -92,32 +85,6 @@ var stream = &cobra.Command{
PersistentPreRunE: cmd.PreRunDefaultProfile,
}

var query = &cobra.Command{
Use: "query [stream-name] --duration 10",
Example: " pb query frontend --duration 10",
Short: "Open SQL query prompt",
Long: "\nquery command is used to open a prompt to query a stream.",
Args: cobra.ExactArgs(1),
PreRunE: cmd.PreRunDefaultProfile,
RunE: func(command *cobra.Command, args []string) error {
stream := args[0]
duration, _ := command.Flags().GetString(durationFlag)
if duration == "" {
duration = defaultDuration
}
durationInt, err := strconv.Atoi(duration)
if err != nil {
return err
}
p := tea.NewProgram(model.NewQueryModel(cmd.DefaultProfile, stream, uint(durationInt)), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Printf("there's been an error: %v", err)
os.Exit(1)
}
return nil
},
}

func main() {
profile.AddCommand(cmd.AddProfileCmd)
profile.AddCommand(cmd.RemoveProfileCmd)
Expand All @@ -137,10 +104,8 @@ func main() {
stream.AddCommand(cmd.ListStreamCmd)
stream.AddCommand(cmd.StatStreamCmd)

query.PersistentFlags().StringP(durationFlag, durationFlagShort, defaultDuration, "specify the duration in minutes for which queries should be executed. Defaults to 10 minutes")

cli.AddCommand(profile)
cli.AddCommand(query)
cli.AddCommand(cmd.QueryCmd)
cli.AddCommand(stream)
cli.AddCommand(user)
cli.AddCommand(role)
Expand Down

0 comments on commit 39054ff

Please sign in to comment.