Skip to content

Commit 1080e1b

Browse files
committed
Send unresolved-type hint to stderr, not stdout
On a failed create or update --type, the available-types list was rendered through the active renderer to stdout, so --format json emitted a types array instead of nothing. Add printer.AvailableTypes, which lists names on stderr, and use it on both not-found paths so stdout carries only the command result.
1 parent aba2381 commit 1080e1b

4 files changed

Lines changed: 56 additions & 3 deletions

File tree

components/printer/types.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
package printer
22

3-
import "github.com/opf/openproject-cli/models"
3+
import (
4+
"fmt"
5+
6+
"github.com/opf/openproject-cli/models"
7+
)
48

59
func Types(types []*models.Type) {
610
activeRenderer.Types(types)
711
}
12+
13+
// AvailableTypes lists type names on standard error as a guidance hint, so it
14+
// never pollutes machine-readable output (e.g. JSON) on a failed command.
15+
func AvailableTypes(types []*models.Type) {
16+
Info("Available types:")
17+
for _, t := range types {
18+
Info(fmt.Sprintf(" - %s", t.Name))
19+
}
20+
}

components/resources/work_packages/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func typeCreate(projectId string, workPackage *dtos.WorkPackageDto, input string
5151
printer.Cyan(input),
5252
printer.Red(projectId),
5353
))
54-
printer.Types(types.Convert())
54+
printer.AvailableTypes(types.Convert())
5555
return openerrors.ErrHandled
5656
}
5757

components/resources/work_packages/create_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66
"net/http/httptest"
77
"net/url"
8+
"strings"
89
"testing"
910

1011
openerrors "github.com/opf/openproject-cli/components/errors"
@@ -49,3 +50,42 @@ func TestCreateRejectsInvalidTypeBeforePosting(t *testing.T) {
4950
t.Errorf("POST count = %d, want 0", postCount)
5051
}
5152
}
53+
54+
// On an unresolved --type, the "available types" hint is guidance and must go
55+
// to stderr, never to stdout, so --format json output stays clean.
56+
func TestCreateInvalidTypeHintGoesToStderr(t *testing.T) {
57+
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
58+
response.Header().Set("Content-Type", "application/json")
59+
switch {
60+
case request.Method == http.MethodGet && request.URL.Path == "/api/v3/projects/1":
61+
_, _ = response.Write([]byte(`{"_links":{"types":{"href":"/api/v3/projects/1/types"}}}`))
62+
case request.Method == http.MethodGet && request.URL.Path == "/api/v3/projects/1/types":
63+
_, _ = response.Write([]byte(`{"_embedded":{"elements":[{"id":7,"name":"Task"}]}}`))
64+
default:
65+
http.Error(response, "unexpected request", http.StatusNotFound)
66+
}
67+
}))
68+
t.Cleanup(server.Close)
69+
70+
host, err := url.Parse(server.URL)
71+
if err != nil {
72+
t.Fatal(err)
73+
}
74+
requests.Init(host, "", false)
75+
testingPrinter := &printer.TestingPrinter{}
76+
printer.Init(testingPrinter)
77+
78+
_, err = work_packages.Create("1", map[work_packages.CreateOption]string{
79+
work_packages.CreateSubject: "Subject",
80+
work_packages.CreateType: "Missing",
81+
})
82+
if !errors.Is(err, openerrors.ErrHandled) {
83+
t.Fatalf("Create error = %v, want ErrHandled", err)
84+
}
85+
if testingPrinter.Result != "" {
86+
t.Errorf("stdout = %q, want empty on failed create", testingPrinter.Result)
87+
}
88+
if !strings.Contains(testingPrinter.ErrResult, "Task") {
89+
t.Errorf("stderr = %q, want it to list available type %q", testingPrinter.ErrResult, "Task")
90+
}
91+
}

components/resources/work_packages/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ func typePatch(patch, workPackage *dtos.WorkPackageDto, input string) (string, e
184184
printer.Red(fmt.Sprintf("#%d", parser.IdFromLink(workPackage.Links.Project.Href))),
185185
))
186186

187-
printer.Types(types.Convert())
187+
printer.AvailableTypes(types.Convert())
188188

189189
return "", openerrors.ErrHandled
190190
}

0 commit comments

Comments
 (0)