mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
fix: Parse prompt input JSON using object or array chars (#538)
Fixes #492. There is no more single-quote parsing, and instead we use a JSON decoder for multiline values. This is a much better UX!
This commit is contained in:
@ -2,7 +2,9 @@ package cliui
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
@ -45,12 +47,22 @@ func Prompt(cmd *cobra.Command, opts PromptOptions) (string, error) {
|
||||
} else {
|
||||
reader := bufio.NewReader(cmd.InOrStdin())
|
||||
line, err = reader.ReadString('\n')
|
||||
// Multiline with single quotes!
|
||||
if err == nil && strings.HasPrefix(line, "'") {
|
||||
rest, err := reader.ReadString('\'')
|
||||
|
||||
// Check if the first line beings with JSON object or array chars.
|
||||
// This enables multiline JSON to be pasted into an input, and have
|
||||
// it parse properly.
|
||||
if err == nil && (strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[")) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
defer pipeWriter.Close()
|
||||
defer pipeReader.Close()
|
||||
go func() {
|
||||
_, _ = pipeWriter.Write([]byte(line))
|
||||
_, _ = reader.WriteTo(pipeWriter)
|
||||
}()
|
||||
var rawMessage json.RawMessage
|
||||
err := json.NewDecoder(pipeReader).Decode(&rawMessage)
|
||||
if err == nil {
|
||||
line += rest
|
||||
line = strings.Trim(line, "'")
|
||||
line = string(rawMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package cliui_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -47,7 +46,7 @@ func TestPrompt(t *testing.T) {
|
||||
require.Equal(t, "yes", <-doneChan)
|
||||
})
|
||||
|
||||
t.Run("Multiline", func(t *testing.T) {
|
||||
t.Run("JSON", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ptty := ptytest.New(t)
|
||||
doneChan := make(chan string)
|
||||
@ -59,13 +58,44 @@ func TestPrompt(t *testing.T) {
|
||||
doneChan <- resp
|
||||
}()
|
||||
ptty.ExpectMatch("Example")
|
||||
ptty.WriteLine("'this is a")
|
||||
ptty.WriteLine("test'")
|
||||
newline := "\n"
|
||||
if runtime.GOOS == "windows" {
|
||||
newline = "\r\n"
|
||||
}
|
||||
require.Equal(t, "this is a"+newline+"test", <-doneChan)
|
||||
ptty.WriteLine("{}")
|
||||
require.Equal(t, "{}", <-doneChan)
|
||||
})
|
||||
|
||||
t.Run("BadJSON", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ptty := ptytest.New(t)
|
||||
doneChan := make(chan string)
|
||||
go func() {
|
||||
resp, err := newPrompt(ptty, cliui.PromptOptions{
|
||||
Text: "Example",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
doneChan <- resp
|
||||
}()
|
||||
ptty.ExpectMatch("Example")
|
||||
ptty.WriteLine("{a")
|
||||
require.Equal(t, "{a", <-doneChan)
|
||||
})
|
||||
|
||||
t.Run("MultilineJSON", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ptty := ptytest.New(t)
|
||||
doneChan := make(chan string)
|
||||
go func() {
|
||||
resp, err := newPrompt(ptty, cliui.PromptOptions{
|
||||
Text: "Example",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
doneChan <- resp
|
||||
}()
|
||||
ptty.ExpectMatch("Example")
|
||||
ptty.WriteLine(`{
|
||||
"test": "wow"
|
||||
}`)
|
||||
require.Equal(t, `{
|
||||
"test": "wow"
|
||||
}`, <-doneChan)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,11 @@ package cli_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/cli/clitest"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWorkspaceCreate(t *testing.T) {
|
||||
|
@ -9,10 +9,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coder/coder/cryptorand"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/cryptorand"
|
||||
)
|
||||
|
||||
// Required to prevent port collision during container creation.
|
||||
|
Reference in New Issue
Block a user