Skip to content

Commit 5d3a13e

Browse files
Simplify JSON output for postgres get, delete, suspend, and resume (#481)
Add envelope wrapper and remove full Environment and Project resources from get, delete, suspend, and resume commands for Postgres Part of GROW-2588 GitOrigin-RevId: 9b40bc3afd3a8a988210de99375c601d527b90ea
1 parent aa7be73 commit 5d3a13e

14 files changed

Lines changed: 158 additions & 139 deletions

cmd/pg_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"testing"
67

78
"github.com/stretchr/testify/require"
@@ -63,3 +64,12 @@ func seedPGInEnv(server *renderapi.Server, name string, envID string) *client.Po
6364
EnvironmentId: pointers.From(envID),
6465
}))
6566
}
67+
68+
// unmarshalPGJSONOutput decodes command stdout for tests that assert Postgres JSON output.
69+
func unmarshalPGJSONOutput(t *testing.T, stdout string) map[string]any {
70+
t.Helper()
71+
72+
var body map[string]any
73+
require.NoError(t, json.Unmarshal([]byte(stdout), &body), "expected valid JSON, got: %s", stdout)
74+
return body
75+
}

cmd/pgcreate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,6 @@ Examples:
128128
func pgCreateSuccessMessage(pg *client.PostgresDetail) string {
129129
return fmt.Sprintf(
130130
"Created Postgres database\n\n%s\n",
131-
text.PostgresDetail(pg),
131+
text.PostgresAPIDetail(pg),
132132
)
133133
}

cmd/pgdelete.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cmd
33
import (
44
"github.com/spf13/cobra"
55

6-
"github.com/render-oss/cli/pkg/client"
76
"github.com/render-oss/cli/pkg/command"
87
"github.com/render-oss/cli/pkg/dependencies"
98
"github.com/render-oss/cli/pkg/postgres"
@@ -23,11 +22,6 @@ func normalizePGDeleteInput(input pgDeleteInput) pgDeleteInput {
2322
return input
2423
}
2524

26-
type pgDeleteResult struct {
27-
Postgres *client.PostgresDetail `json:"postgres"`
28-
Deleted bool `json:"deleted"`
29-
}
30-
3125
func newPgDeleteCmd(deps *dependencies.Dependencies) *cobra.Command {
3226
cmd := &cobra.Command{
3327
Use: "delete <postgresID|postgresName>",
@@ -80,7 +74,7 @@ Postgres ID instead (which works across workspaces).`,
8074
input = normalizePGDeleteInput(input)
8175
confirm := command.GetConfirmFromContext(cmd.Context())
8276

83-
loadData := func() (*pgDeleteResult, error) {
77+
loadData := func() (*postgres.DeleteOut, error) {
8478
resolved, err := deps.PostgresService().Resolve(cmd.Context(), postgres.ResolveInput{
8579
IDOrName: input.IDOrName,
8680
ProjectIDOrName: input.ProjectIDOrName,
@@ -89,13 +83,18 @@ Postgres ID instead (which works across workspaces).`,
8983
if err != nil {
9084
return nil, err
9185
}
92-
pg := resolved.Postgres
86+
out := postgres.NewPostgresDeleteOut(resolved)
87+
out.Meta = postgres.DeleteOutMeta{
88+
Deleted: confirm,
89+
}
9390
if confirm {
94-
if err := deps.PostgresService().Delete(cmd.Context(), pg.Id); err != nil {
91+
if err := deps.PostgresService().Delete(cmd.Context(), out.Data.Id); err != nil {
9592
return nil, err
9693
}
94+
} else {
95+
out.Meta.Message = "re-run with --confirm to delete"
9796
}
98-
return &pgDeleteResult{Postgres: pg, Deleted: confirm}, nil
97+
return &out, nil
9998
}
10099

101100
_, err := command.NonInteractive(cmd,
@@ -108,11 +107,11 @@ Postgres ID instead (which works across workspaces).`,
108107
return cmd
109108
}
110109

111-
func pgDeleteTextOutput(r *pgDeleteResult) string {
112-
if r.Deleted {
113-
return "Deleted this Postgres database:\n\n" + text.PostgresDetail(r.Postgres) + "\n"
110+
func pgDeleteTextOutput(r *postgres.DeleteOut) string {
111+
if r.Meta.Deleted {
112+
return "Deleted this Postgres database:\n\n" + text.PostgresDetail(&r.Data) + "\n"
114113
}
115114
return "This command would delete this Postgres database:\n\n" +
116-
text.PostgresDetail(r.Postgres) +
115+
text.PostgresDetail(&r.Data) +
117116
"\n\nRe-run with --confirm to proceed\n"
118117
}

cmd/pgdelete_test.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package cmd
22

33
import (
4-
"encoding/json"
54
"testing"
65

76
"github.com/stretchr/testify/assert"
87
"github.com/stretchr/testify/require"
98

109
renderapi "github.com/render-oss/cli/internal/fakes/renderapi"
10+
"github.com/render-oss/cli/internal/testrequire"
1111
"github.com/render-oss/cli/pkg/client"
1212
)
1313

@@ -79,17 +79,29 @@ func TestPGDelete_JSONOutput_AfterConfirm(t *testing.T) {
7979
require.NoError(t, err)
8080
assert.Empty(t, server.Postgres.Instances)
8181

82-
var body struct {
83-
Postgres struct {
84-
ID string `json:"id"`
85-
Name string `json:"name"`
86-
} `json:"postgres"`
87-
Deleted bool `json:"deleted"`
88-
}
89-
require.NoError(t, json.Unmarshal([]byte(result.Stdout), &body))
90-
assert.Equal(t, pg.Id, body.Postgres.ID)
91-
assert.Equal(t, "json-db", body.Postgres.Name)
92-
assert.True(t, body.Deleted)
82+
body := unmarshalPGJSONOutput(t, result.Stdout)
83+
data := testrequire.SubMap(t, body, "data")
84+
meta := testrequire.SubMap(t, body, "meta")
85+
assert.Equal(t, pg.Id, data["id"])
86+
assert.Equal(t, "json-db", data["name"])
87+
assert.Equal(t, true, meta["deleted"])
88+
}
89+
90+
func TestPGDelete_JSONOutput_Preview(t *testing.T) {
91+
server := renderapi.NewServer(t)
92+
pg := seedPG(server, "json-db")
93+
94+
result, err := executePGDelete(t, server, pg.Id, "--output", "json")
95+
require.NoError(t, err)
96+
assert.Len(t, server.Postgres.Instances, 1)
97+
98+
body := unmarshalPGJSONOutput(t, result.Stdout)
99+
data := testrequire.SubMap(t, body, "data")
100+
meta := testrequire.SubMap(t, body, "meta")
101+
assert.Equal(t, pg.Id, data["id"])
102+
assert.Equal(t, "json-db", data["name"])
103+
assert.Equal(t, false, meta["deleted"])
104+
assert.Equal(t, "re-run with --confirm to delete", meta["message"])
93105
}
94106

95107
func TestPGDelete_JSONOutput_OnError(t *testing.T) {

cmd/pgget.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Postgres ID instead (which works across workspaces).`,
6161
}
6262
input = pgtypes.NormalizeGetInput(input)
6363

64-
loadData := func() (*postgres.GetResult, error) {
64+
loadData := func() (*postgres.GetOut, error) {
6565
resolved, err := deps.PostgresService().Resolve(cmd.Context(), postgres.ResolveInput{
6666
IDOrName: input.IDOrName,
6767
ProjectIDOrName: input.ProjectIDOrName,
@@ -70,19 +70,20 @@ Postgres ID instead (which works across workspaces).`,
7070
if err != nil {
7171
return nil, err
7272
}
73-
pg := resolved.Postgres
73+
out := postgres.NewPostgresGetOut(resolved)
7474
var conn *client.PostgresConnectionInfo
7575
if input.IncludeSensitiveConnectionInfo {
76-
conn, err = deps.PostgresService().GetConnectionInfo(cmd.Context(), pg.Id)
76+
conn, err = deps.PostgresService().GetConnectionInfo(cmd.Context(), out.Data.Id)
7777
if err != nil {
7878
return nil, err
7979
}
8080
}
81-
return &postgres.GetResult{Postgres: pg, ConnectionInfo: conn}, nil
81+
out.Data.ConnectionInfo = conn
82+
return &out, nil
8283
}
8384

84-
_, err := command.NonInteractive(cmd, loadData, func(r *postgres.GetResult) string {
85-
return text.PostgresGetDetail(r.Postgres, r.ConnectionInfo) + "\n"
85+
_, err := command.NonInteractive(cmd, loadData, func(out *postgres.GetOut) string {
86+
return text.PostgresGetDetail(&out.Data) + "\n"
8687
})
8788
return err
8889
}

cmd/pgget_test.go

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package cmd
22

33
import (
4-
"encoding/json"
54
"testing"
65

76
"github.com/stretchr/testify/assert"
87
"github.com/stretchr/testify/require"
98

109
renderapi "github.com/render-oss/cli/internal/fakes/renderapi"
1110
"github.com/render-oss/cli/internal/testids"
11+
"github.com/render-oss/cli/internal/testrequire"
1212
"github.com/render-oss/cli/pkg/client"
1313
)
1414

@@ -174,17 +174,11 @@ func TestPGGet_JSONOutput_WithoutConnectionInfo(t *testing.T) {
174174
result, err := harness.execute(pg.Id, "--output", "json")
175175
require.NoError(t, err)
176176

177-
var body struct {
178-
Postgres struct {
179-
ID string `json:"id"`
180-
Name string `json:"name"`
181-
} `json:"postgres"`
182-
ConnectionInfo *struct{} `json:"connectionInfo"`
183-
}
184-
require.NoError(t, json.Unmarshal([]byte(result.Stdout), &body))
185-
assert.Equal(t, pg.Id, body.Postgres.ID)
186-
assert.Equal(t, "json-db", body.Postgres.Name)
187-
assert.Nil(t, body.ConnectionInfo)
177+
body := unmarshalPGJSONOutput(t, result.Stdout)
178+
data := testrequire.SubMap(t, body, "data")
179+
assert.Equal(t, pg.Id, data["id"])
180+
assert.Equal(t, "json-db", data["name"])
181+
assert.NotContains(t, data, "connectionInfo")
188182
assert.NotContains(t, result.Stdout, "psqlCommand", "connection info must not appear without --include-sensitive-connection-info")
189183
assert.NotContains(t, result.Stdout, "password", "connection info must not appear without --include-sensitive-connection-info")
190184
assert.False(t, harness.server.HasRequest("GET", "/connection-info"), "no connection info request without flag")
@@ -197,21 +191,13 @@ func TestPGGet_JSONOutput_WithConnectionInfo(t *testing.T) {
197191
result, err := harness.execute(pg.Id, "--include-sensitive-connection-info", "--output", "json")
198192
require.NoError(t, err)
199193

200-
var body struct {
201-
Postgres struct {
202-
ID string `json:"id"`
203-
Name string `json:"name"`
204-
} `json:"postgres"`
205-
ConnectionInfo struct {
206-
PsqlCommand string `json:"psqlCommand"`
207-
Password string `json:"password"`
208-
} `json:"connectionInfo"`
209-
}
210-
require.NoError(t, json.Unmarshal([]byte(result.Stdout), &body))
211-
assert.Equal(t, pg.Id, body.Postgres.ID)
212-
assert.Equal(t, "json-db", body.Postgres.Name)
213-
assert.NotEmpty(t, body.ConnectionInfo.PsqlCommand)
214-
assert.NotEmpty(t, body.ConnectionInfo.Password)
194+
body := unmarshalPGJSONOutput(t, result.Stdout)
195+
data := testrequire.SubMap(t, body, "data")
196+
assert.Equal(t, pg.Id, data["id"])
197+
assert.Equal(t, "json-db", data["name"])
198+
connectionInfo := testrequire.SubMap(t, data, "connectionInfo")
199+
assert.NotEmpty(t, connectionInfo["psqlCommand"])
200+
assert.NotEmpty(t, connectionInfo["password"])
215201
}
216202

217203
func TestPGGet_DefaultOutput_TreatedAsText(t *testing.T) {

cmd/pgresume.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cmd
33
import (
44
"github.com/spf13/cobra"
55

6-
"github.com/render-oss/cli/pkg/client"
76
"github.com/render-oss/cli/pkg/command"
87
"github.com/render-oss/cli/pkg/dependencies"
98
"github.com/render-oss/cli/pkg/postgres"
@@ -56,7 +55,7 @@ Postgres ID instead (which works across workspaces).`,
5655
}
5756
input = pgtypes.NormalizeResumeInput(input)
5857

59-
loadData := func() (*client.PostgresDetail, error) {
58+
loadData := func() (*postgres.ResumeOut, error) {
6059
resolved, err := deps.PostgresService().Resolve(cmd.Context(), postgres.ResolveInput{
6160
IDOrName: input.IDOrName,
6261
ProjectIDOrName: input.ProjectIDOrName,
@@ -65,15 +64,16 @@ Postgres ID instead (which works across workspaces).`,
6564
if err != nil {
6665
return nil, err
6766
}
68-
pg := resolved.Postgres
69-
if err := deps.PostgresService().ResumePostgres(cmd.Context(), pg.Id); err != nil {
67+
out := postgres.NewPostgresResumeOut(resolved)
68+
if err := deps.PostgresService().ResumePostgres(cmd.Context(), out.Data.Id); err != nil {
7069
return nil, err
7170
}
72-
resolved, err = deps.PostgresService().Resolve(cmd.Context(), postgres.ResolveInput{IDOrName: pg.Id})
71+
resolved, err = deps.PostgresService().Resolve(cmd.Context(), postgres.ResolveInput{IDOrName: out.Data.Id})
7372
if err != nil {
7473
return nil, err
7574
}
76-
return resolved.Postgres, nil
75+
out = postgres.NewPostgresResumeOut(resolved)
76+
return &out, nil
7777
}
7878

7979
_, err := command.NonInteractive(cmd,
@@ -86,6 +86,6 @@ Postgres ID instead (which works across workspaces).`,
8686
return cmd
8787
}
8888

89-
func pgResumeTextOutput(pg *client.PostgresDetail) string {
90-
return "Resumed this Postgres database:\n\n" + text.PostgresDetail(pg) + "\n"
89+
func pgResumeTextOutput(out *postgres.ResumeOut) string {
90+
return "Resumed this Postgres database:\n\n" + text.PostgresDetail(&out.Data) + "\n"
9191
}

cmd/pgresume_test.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cmd
22

33
import (
4-
"encoding/json"
54
"net/http"
65
"testing"
76

@@ -10,6 +9,7 @@ import (
109

1110
renderapi "github.com/render-oss/cli/internal/fakes/renderapi"
1211
"github.com/render-oss/cli/internal/testids"
12+
"github.com/render-oss/cli/internal/testrequire"
1313
"github.com/render-oss/cli/pkg/client"
1414
)
1515

@@ -100,15 +100,11 @@ func TestPGResume_JSONOutput(t *testing.T) {
100100
require.NoError(t, err)
101101
assert.Equal(t, client.DatabaseStatusAvailable, harness.server.Postgres.Instances[0].Status)
102102

103-
var body struct {
104-
ID string `json:"id"`
105-
Name string `json:"name"`
106-
Status string `json:"status"`
107-
}
108-
require.NoError(t, json.Unmarshal([]byte(result.Stdout), &body))
109-
assert.Equal(t, pg.Id, body.ID)
110-
assert.Equal(t, "json-db", body.Name)
111-
assert.Equal(t, string(client.DatabaseStatusAvailable), body.Status)
103+
body := unmarshalPGJSONOutput(t, result.Stdout)
104+
data := testrequire.SubMap(t, body, "data")
105+
assert.Equal(t, pg.Id, data["id"])
106+
assert.Equal(t, "json-db", data["name"])
107+
assert.Equal(t, string(client.DatabaseStatusAvailable), data["status"])
112108
}
113109

114110
func TestPGResume_NameCollision_NarrowedByEnvironment_Resumes(t *testing.T) {

0 commit comments

Comments
 (0)