Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ zeltapp review progress <uuid>
zeltapp review participation <uuid>
zeltapp review result <uuid> [--user me|<id>]
zeltapp review entry [--user me|<id>]
zeltapp review detail <entry-uuid> # entry + the cycle's live question set
zeltapp review answers <entry-uuid> [<question-id>] # saved answers
zeltapp goal list [--user me|<id>]
```

Expand Down
47 changes: 47 additions & 0 deletions cmd/zeltapp/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ func reviewCmd() *cobra.Command {
reviewParticipationCmd(),
reviewResultCmd(),
reviewEntryCmd(),
reviewDetailCmd(),
reviewAnswersCmd(),
)
return cmd
}
Expand Down Expand Up @@ -206,6 +208,51 @@ func reviewEntryCmd() *cobra.Command {
return cmd
}

func reviewDetailCmd() *cobra.Command {
return &cobra.Command{
Use: "detail ENTRY_UUID",
Short: "Entry detail incl. the cycle's live question set",
Long: "Fetches /apiv2/review-entries/{id}/detail (note the plural), the endpoint the web app uses. Questions are snapshotted per cycle, so this is the authoritative question set for an entry; the review-template endpoint can be stale.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateOutput(); err != nil {
return err
}
return withClient(func(c *client) error {
var v json.RawMessage
if err := c.do("GET", "/apiv2/review-entries/"+args[0]+"/detail", nil, &v); err != nil {
return err
}
return emit(&resourceView{raw: rawToAny(v)})
})
},
}
}

func reviewAnswersCmd() *cobra.Command {
return &cobra.Command{
Use: "answers ENTRY_UUID [QUESTION_UUID]",
Short: "Saved answers for an entry (optionally one question)",
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateOutput(); err != nil {
return err
}
return withClient(func(c *client) error {
path := "/apiv2/review-answer/entry/" + args[0]
if len(args) == 2 {
path = "/apiv2/review-answer/" + args[0] + "/" + args[1] + "/by-question-id"
}
var v json.RawMessage
if err := c.do("GET", path, nil, &v); err != nil {
return err
}
return emit(&resourceView{raw: rawToAny(v)})
})
},
}
}

func goalCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "goal",
Expand Down
48 changes: 48 additions & 0 deletions cmd/zeltapp/review_detail_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"encoding/json"
"testing"
)

// ---------- review detail / answers ----------

func TestReview_Detail_UsesPluralEntriesPath(t *testing.T) {
srv, st, _, buf := withTestEnv(t)
_ = srv
st.jsonRoute("GET", "/apiv2/review-entries/abc-123/detail", 200,
[]byte(`{"id":"abc-123","questions":[{"id":"q1","questionMain":"What went well?"}]}`))
if err := runCmd(t, "review", "detail", "abc-123"); err != nil {
t.Fatal(err)
}
var out map[string]any
if err := json.Unmarshal([]byte(buf.String()), &out); err != nil {
t.Fatalf("output not JSON: %s", buf.String())
}
if out["id"] != "abc-123" {
t.Errorf("expected entry id in output, got %v", out)
}
if len(st.requestsTo("GET", "/apiv2/review-entries/abc-123/detail")) != 1 {
t.Error("expected exactly one request to the plural review-entries detail path")
}
}

func TestReview_Answers_EntryAndByQuestion(t *testing.T) {
srv, st, _, _ := withTestEnv(t)
_ = srv
st.jsonRoute("GET", "/apiv2/review-answer/entry/abc-123", 200, []byte(`[]`))
if err := runCmd(t, "review", "answers", "abc-123"); err != nil {
t.Fatal(err)
}
if len(st.requestsTo("GET", "/apiv2/review-answer/entry/abc-123")) != 1 {
t.Error("expected request to the entry answers path")
}

st.jsonRoute("GET", "/apiv2/review-answer/abc-123/q1/by-question-id", 200, []byte(`{"id":"a1"}`))
if err := runCmd(t, "review", "answers", "abc-123", "q1"); err != nil {
t.Fatal(err)
}
if len(st.requestsTo("GET", "/apiv2/review-answer/abc-123/q1/by-question-id")) != 1 {
t.Error("expected request to the by-question-id path")
}
}