Skip to content

Commit 99ece25

Browse files
authored
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!
1 parent 305b67c commit 99ece25

File tree

4 files changed

+60
-16
lines changed

4 files changed

+60
-16
lines changed

cli/cliui/prompt.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package cliui
22

33
import (
44
"bufio"
5+
"encoding/json"
56
"fmt"
7+
"io"
68
"os"
79
"os/signal"
810
"strings"
@@ -45,12 +47,22 @@ func Prompt(cmd *cobra.Command, opts PromptOptions) (string, error) {
4547
} else {
4648
reader := bufio.NewReader(cmd.InOrStdin())
4749
line, err = reader.ReadString('\n')
48-
// Multiline with single quotes!
49-
if err == nil && strings.HasPrefix(line, "'") {
50-
rest, err := reader.ReadString('\'')
50+
51+
// Check if the first line beings with JSON object or array chars.
52+
// This enables multiline JSON to be pasted into an input, and have
53+
// it parse properly.
54+
if err == nil && (strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[")) {
55+
pipeReader, pipeWriter := io.Pipe()
56+
defer pipeWriter.Close()
57+
defer pipeReader.Close()
58+
go func() {
59+
_, _ = pipeWriter.Write([]byte(line))
60+
_, _ = reader.WriteTo(pipeWriter)
61+
}()
62+
var rawMessage json.RawMessage
63+
err := json.NewDecoder(pipeReader).Decode(&rawMessage)
5164
if err == nil {
52-
line += rest
53-
line = strings.Trim(line, "'")
65+
line = string(rawMessage)
5466
}
5567
}
5668
}

cli/cliui/prompt_test.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cliui_test
22

33
import (
44
"context"
5-
"runtime"
65
"testing"
76

87
"github.com/spf13/cobra"
@@ -47,7 +46,7 @@ func TestPrompt(t *testing.T) {
4746
require.Equal(t, "yes", <-doneChan)
4847
})
4948

50-
t.Run("Multiline", func(t *testing.T) {
49+
t.Run("JSON", func(t *testing.T) {
5150
t.Parallel()
5251
ptty := ptytest.New(t)
5352
doneChan := make(chan string)
@@ -59,13 +58,44 @@ func TestPrompt(t *testing.T) {
5958
doneChan <- resp
6059
}()
6160
ptty.ExpectMatch("Example")
62-
ptty.WriteLine("'this is a")
63-
ptty.WriteLine("test'")
64-
newline := "\n"
65-
if runtime.GOOS == "windows" {
66-
newline = "\r\n"
67-
}
68-
require.Equal(t, "this is a"+newline+"test", <-doneChan)
61+
ptty.WriteLine("{}")
62+
require.Equal(t, "{}", <-doneChan)
63+
})
64+
65+
t.Run("BadJSON", func(t *testing.T) {
66+
t.Parallel()
67+
ptty := ptytest.New(t)
68+
doneChan := make(chan string)
69+
go func() {
70+
resp, err := newPrompt(ptty, cliui.PromptOptions{
71+
Text: "Example",
72+
})
73+
require.NoError(t, err)
74+
doneChan <- resp
75+
}()
76+
ptty.ExpectMatch("Example")
77+
ptty.WriteLine("{a")
78+
require.Equal(t, "{a", <-doneChan)
79+
})
80+
81+
t.Run("MultilineJSON", func(t *testing.T) {
82+
t.Parallel()
83+
ptty := ptytest.New(t)
84+
doneChan := make(chan string)
85+
go func() {
86+
resp, err := newPrompt(ptty, cliui.PromptOptions{
87+
Text: "Example",
88+
})
89+
require.NoError(t, err)
90+
doneChan <- resp
91+
}()
92+
ptty.ExpectMatch("Example")
93+
ptty.WriteLine(`{
94+
"test": "wow"
95+
}`)
96+
require.Equal(t, `{
97+
"test": "wow"
98+
}`, <-doneChan)
6999
})
70100
}
71101

cli/workspacecreate_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package cli_test
33
import (
44
"testing"
55

6+
"github.com/stretchr/testify/require"
7+
68
"github.com/coder/coder/cli/clitest"
79
"github.com/coder/coder/coderd/coderdtest"
810
"github.com/coder/coder/pty/ptytest"
9-
"github.com/stretchr/testify/require"
1011
)
1112

1213
func TestWorkspaceCreate(t *testing.T) {

database/postgres/postgres.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import (
99
"sync"
1010
"time"
1111

12-
"github.com/coder/coder/cryptorand"
1312
"github.com/ory/dockertest/v3"
1413
"github.com/ory/dockertest/v3/docker"
1514
"golang.org/x/xerrors"
15+
16+
"github.com/coder/coder/cryptorand"
1617
)
1718

1819
// Required to prevent port collision during container creation.

0 commit comments

Comments
 (0)