allow multiline json data in repl (#24)

This commit is contained in:
T. von Dein
2026-05-29 12:42:02 +02:00
parent 1c72b5ec9a
commit 2b2094b155

View File

@@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package es package es
import ( import (
"bufio"
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
@@ -34,19 +35,98 @@ import (
"github.com/chzyer/readline" "github.com/chzyer/readline"
) )
const intro = `Input format: verb path [data]"
Example:
post /yourindex/_ccr/pause_follow
put /yourindex/_settings {"number_of_replicas": 1}
You can also put multiline JSON after the path like:
put /yourindex/_settings
{
"number_of_replicas": 1
}
If you do NOT supply a JSON in the first line, you need to hit ENTER
twice to complete.`
func Repl(conf *cfg.Config) error {
verbs := []string{"post", "get", "put", "delete"}
fmt.Println(intro)
fmt.Println()
reader, err := readline.NewEx(&readline.Config{
Prompt: "> ",
HistoryFile: os.Getenv("HOME") + "/.config/esctl/history",
HistoryLimit: 500,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
})
if err != nil {
return fmt.Errorf("failed to initialize readline lib: %s", err)
}
for {
text, err := reader.Readline()
if err != nil {
break
}
text = strings.TrimSpace(text)
if text == "" {
continue
}
parts := strings.SplitN(strings.TrimSpace(text), " ", 3)
if len(parts) < 2 {
fmt.Println("error: you need to input a verb, uri [and post data]")
continue
}
if !slices.Contains(verbs, strings.ToLower(parts[0])) {
fmt.Println("error: verb must be one of " + strings.Join(verbs, ","))
continue
}
if !strings.HasPrefix(parts[1], "/") {
parts[1] = "/" + parts[1]
}
json := ""
if len(parts) == 3 {
// put /uri {json}
json = parts[2]
}
// put /uri/<Ret> [json]
data, err := readJSON(json)
if err != nil {
fmt.Println(err)
}
err = CallAPI(conf, parts[0], parts[1], data)
if err != nil {
fmt.Printf("failed to call API: %s\n", err)
}
reader.SetPrompt("> ")
}
return nil
}
func encodeAuth(username, password string) string { func encodeAuth(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
} }
func CallAPI(conf *cfg.Config, input []string) error { func CallAPI(conf *cfg.Config, verb, path, data string) error {
var data string verb = strings.ToUpper(verb)
verb := strings.ToUpper(input[0])
path := input[1]
if len(input) == 3 {
data = input[2]
}
// we're using port-forwards anyway // we're using port-forwards anyway
tr := &http.Transport{ tr := &http.Transport{
@@ -108,69 +188,37 @@ func prettyfiJson(conf *cfg.Config, raw []byte) error {
return nil return nil
} }
func Repl(conf *cfg.Config) error { // interactively read arbitrary JSON data from STDIN, which is
verbs := []string{"post", "get", "put", "delete"} // virtually a repl inside the primary repl
func readJSON(input string) (string, error) {
data := ""
fmt.Println("Input format: verb path [data]") if input != "" {
fmt.Println("example: post /yourindex/_ccr/pause_follow") data = input
} else {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
reader, err := readline.NewEx(&readline.Config{ if line == "" {
Prompt: "> ",
HistoryFile: os.Getenv("HOME") + "/.config/esctl/history",
HistoryLimit: 500,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
})
if err != nil {
return fmt.Errorf("failed to initialize readline lib: %s", err)
}
for {
text, err := reader.Readline()
if err != nil {
break break
} }
text = strings.TrimSpace(text) data += line
}
if text == "" {
continue
} }
parts := strings.SplitN(strings.TrimSpace(text), " ", 3) if data == "" {
if len(parts) < 2 { return data, nil
fmt.Println("error: you need to input a verb, uri [and post data]")
continue
} }
if !slices.Contains(verbs, strings.ToLower(parts[0])) { // validate
fmt.Println("error: verb must be one of " + strings.Join(verbs, ",")) check := map[string]any{}
continue err := json.Unmarshal([]byte(data), &check)
}
if !strings.HasPrefix(parts[1], "/") {
fmt.Println("error: url path must start with /")
continue
}
if len(parts) == 3 {
data := map[string]any{}
err := json.Unmarshal([]byte(parts[2]), &data)
if err != nil { if err != nil {
fmt.Printf("error: input data is not proper JSON: %s", err) return "", fmt.Errorf("error: input data is not proper JSON: %s", err)
continue
}
} }
err = CallAPI(conf, parts) return data, nil
if err != nil {
fmt.Printf("failed to call API: %s\n", err)
}
reader.SetPrompt("> ")
}
return nil
} }