diff --git a/internal/pkg/cli/command/apiKey/create.go b/internal/pkg/cli/command/apiKey/create.go index 54751a80..37a7a382 100644 --- a/internal/pkg/cli/command/apiKey/create.go +++ b/internal/pkg/cli/command/apiKey/create.go @@ -96,7 +96,7 @@ func NewCreateApiKeyCmd() *cobra.Command { if options.json { json := text.IndentJSON(keyWithSecret) - pcio.Println(json) + pcio.PrintJSON(json) } else { msg.SuccessMsg("API key %s created successfully.\n", style.Emphasis(keyWithSecret.Key.Name)) presenters.PrintDescribeAPIKeyWithSecretTable(keyWithSecret) diff --git a/internal/pkg/cli/command/apiKey/describe.go b/internal/pkg/cli/command/apiKey/describe.go index ea1610ab..3fa05b01 100644 --- a/internal/pkg/cli/command/apiKey/describe.go +++ b/internal/pkg/cli/command/apiKey/describe.go @@ -39,7 +39,7 @@ func NewDescribeAPIKeyCmd() *cobra.Command { if options.json { json := text.IndentJSON(apiKey) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeAPIKeyTable(apiKey) } diff --git a/internal/pkg/cli/command/apiKey/list.go b/internal/pkg/cli/command/apiKey/list.go index c5c392dd..aa9faa2a 100644 --- a/internal/pkg/cli/command/apiKey/list.go +++ b/internal/pkg/cli/command/apiKey/list.go @@ -65,7 +65,7 @@ func NewListKeysCmd() *cobra.Command { if options.json { json := text.IndentJSON(sortedKeys) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(sortedKeys) } diff --git a/internal/pkg/cli/command/apiKey/update.go b/internal/pkg/cli/command/apiKey/update.go index 53387377..5eb51a74 100644 --- a/internal/pkg/cli/command/apiKey/update.go +++ b/internal/pkg/cli/command/apiKey/update.go @@ -50,7 +50,7 @@ func NewUpdateAPIKeyCmd() *cobra.Command { if options.json { json := text.IndentJSON(apiKey) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/auth/configure.go b/internal/pkg/cli/command/auth/configure.go index 90f57c4c..ddca5553 100644 --- a/internal/pkg/cli/command/auth/configure.go +++ b/internal/pkg/cli/command/auth/configure.go @@ -240,7 +240,7 @@ func Run(ctx context.Context, io IO, opts configureCmdOptions) { defaultAPIKey := secrets.DefaultAPIKey.Get() targetContext.DefaultAPIKey = presenters.MaskHeadTail(defaultAPIKey, 4, 4) json := text.IndentJSON(targetContext) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/auth/local_keys_list.go b/internal/pkg/cli/command/auth/local_keys_list.go index e0e3ae7d..eed75948 100644 --- a/internal/pkg/cli/command/auth/local_keys_list.go +++ b/internal/pkg/cli/command/auth/local_keys_list.go @@ -42,7 +42,7 @@ func NewListLocalKeysCmd() *cobra.Command { if options.json { maskedMap := maskForJSON(managedKeys, options.reveal) json := text.IndentJSON(maskedMap) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(managedKeys, options.reveal) } diff --git a/internal/pkg/cli/command/auth/local_keys_prune.go b/internal/pkg/cli/command/auth/local_keys_prune.go index d99e6fd7..43386bdf 100644 --- a/internal/pkg/cli/command/auth/local_keys_prune.go +++ b/internal/pkg/cli/command/auth/local_keys_prune.go @@ -215,7 +215,7 @@ func confirmPruneKeys(plan []planItem, options pruneLocalKeysCmdOptions) (bool, func printDryRunPlan(plan []planItem, options pruneLocalKeysCmdOptions) { if options.json { json := text.IndentJSON(plan) - pcio.Println(json) + pcio.PrintJSON(json) } else { for _, key := range plan { if key.onServer { diff --git a/internal/pkg/cli/command/auth/status.go b/internal/pkg/cli/command/auth/status.go index 72cb90d3..b76de949 100644 --- a/internal/pkg/cli/command/auth/status.go +++ b/internal/pkg/cli/command/auth/status.go @@ -99,7 +99,7 @@ func runAuthStatus(cmd *cobra.Command, options authStatusCmdOptions) error { if options.json { json := text.IndentJSON(authStatus) - pcio.Println(json) + pcio.PrintJSON(json) return nil } diff --git a/internal/pkg/cli/command/backup/restore/cmd.go b/internal/pkg/cli/command/backup/restore/cmd.go index 5284ea38..e06e6c0a 100644 --- a/internal/pkg/cli/command/backup/restore/cmd.go +++ b/internal/pkg/cli/command/backup/restore/cmd.go @@ -113,7 +113,7 @@ func runRestoreJobCmd(ctx context.Context, svc RestoreJobService, options restor } if options.json { - pcio.Println(text.IndentJSON(resp)) + pcio.PrintJSON(text.IndentJSON(resp)) return nil } diff --git a/internal/pkg/cli/command/backup/restore/describe.go b/internal/pkg/cli/command/backup/restore/describe.go index 0982c738..f9e519f3 100644 --- a/internal/pkg/cli/command/backup/restore/describe.go +++ b/internal/pkg/cli/command/backup/restore/describe.go @@ -58,7 +58,7 @@ func runDescribeRestoreJobCmd(ctx context.Context, svc RestoreJobService, option } if options.json { - pcio.Println(text.IndentJSON(resp)) + pcio.PrintJSON(text.IndentJSON(resp)) } else { presenters.PrintRestoreJob(resp) } diff --git a/internal/pkg/cli/command/backup/restore/list.go b/internal/pkg/cli/command/backup/restore/list.go index 9e45d6a1..fdbb039e 100644 --- a/internal/pkg/cli/command/backup/restore/list.go +++ b/internal/pkg/cli/command/backup/restore/list.go @@ -69,7 +69,7 @@ func runListRestoreJobsCmd(ctx context.Context, svc RestoreJobService, options l } if options.json { - pcio.Println(text.IndentJSON(resp)) + pcio.PrintJSON(text.IndentJSON(resp)) } else { presenters.PrintRestoreJobList(resp) } diff --git a/internal/pkg/cli/command/collection/create.go b/internal/pkg/cli/command/collection/create.go index b749ea03..44daacd5 100644 --- a/internal/pkg/cli/command/collection/create.go +++ b/internal/pkg/cli/command/collection/create.go @@ -45,7 +45,7 @@ func NewCreateCollectionCmd() *cobra.Command { if options.json { json := text.IndentJSON(collection) - pcio.Println(json) + pcio.PrintJSON(json) } else { describeCommand := pcio.Sprintf("pc collection describe --name %s", collection.Name) msg.SuccessMsg("Collection %s created successfully. Run %s to check status. \n\n", style.Emphasis(collection.Name), style.Code(describeCommand)) diff --git a/internal/pkg/cli/command/collection/describe.go b/internal/pkg/cli/command/collection/describe.go index 21aa7d9a..9214a16c 100644 --- a/internal/pkg/cli/command/collection/describe.go +++ b/internal/pkg/cli/command/collection/describe.go @@ -37,7 +37,7 @@ func NewDescribeCollectionCmd() *cobra.Command { if options.json { json := text.IndentJSON(collection) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeCollectionTable(collection) } diff --git a/internal/pkg/cli/command/collection/list.go b/internal/pkg/cli/command/collection/list.go index b30fb2ca..1db99c08 100644 --- a/internal/pkg/cli/command/collection/list.go +++ b/internal/pkg/cli/command/collection/list.go @@ -47,7 +47,7 @@ func NewListCollectionsCmd() *cobra.Command { if options.json { json := text.IndentJSON(collections) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(collections) } diff --git a/internal/pkg/cli/command/index/configure.go b/internal/pkg/cli/command/index/configure.go index 24243d62..41cc9883 100644 --- a/internal/pkg/cli/command/index/configure.go +++ b/internal/pkg/cli/command/index/configure.go @@ -136,7 +136,7 @@ func runConfigureIndexCmd(ctx context.Context, cmd *cobra.Command, options confi if options.json { json := text.IndentJSON(idx) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/index/create.go b/internal/pkg/cli/command/index/create.go index 3ac05254..b1326d60 100644 --- a/internal/pkg/cli/command/index/create.go +++ b/internal/pkg/cli/command/index/create.go @@ -309,7 +309,7 @@ func runCreateIndexCmd(ctx context.Context, cmd *cobra.Command, service CreateIn func renderSuccessOutput(idx *pinecone.Index, options createIndexOptions) { if options.json { json := text.IndentJSON(idx) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/index/describe.go b/internal/pkg/cli/command/index/describe.go index 501c010a..d169a8b7 100644 --- a/internal/pkg/cli/command/index/describe.go +++ b/internal/pkg/cli/command/index/describe.go @@ -45,7 +45,7 @@ func NewDescribeCmd() *cobra.Command { if options.json { json := text.IndentJSON(idx) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeIndexTable(idx) } diff --git a/internal/pkg/cli/command/index/describe_stats.go b/internal/pkg/cli/command/index/describe_stats.go index 2cc1625f..7f731d8f 100644 --- a/internal/pkg/cli/command/index/describe_stats.go +++ b/internal/pkg/cli/command/index/describe_stats.go @@ -77,7 +77,7 @@ func runDescribeIndexStatsCmd(ctx context.Context, options describeIndexStatsCmd if options.json { json := text.IndentJSON(resp) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeIndexStatsTable(resp) } diff --git a/internal/pkg/cli/command/index/list.go b/internal/pkg/cli/command/index/list.go index f5dcd296..5ba4ac75 100644 --- a/internal/pkg/cli/command/index/list.go +++ b/internal/pkg/cli/command/index/list.go @@ -47,7 +47,7 @@ func NewListCmd() *cobra.Command { if options.json { json := text.IndentJSON(idxs) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(idxs, options.wide) } diff --git a/internal/pkg/cli/command/index/namespace/create.go b/internal/pkg/cli/command/index/namespace/create.go index d80cae98..51145750 100644 --- a/internal/pkg/cli/command/index/namespace/create.go +++ b/internal/pkg/cli/command/index/namespace/create.go @@ -96,7 +96,7 @@ func runCreateNamespaceCmd(ctx context.Context, ic NamespaceService, options cre if options.json { json := text.IndentJSON(ns) - pcio.Println(json) + pcio.PrintJSON(json) } else { msg.SuccessMsg("Namespace %s created successfully.", options.name) presenters.PrintDescribeNamespaceTable(ns) diff --git a/internal/pkg/cli/command/index/namespace/describe.go b/internal/pkg/cli/command/index/namespace/describe.go index 381af59d..77b371f1 100644 --- a/internal/pkg/cli/command/index/namespace/describe.go +++ b/internal/pkg/cli/command/index/namespace/describe.go @@ -80,7 +80,7 @@ func runDescribeNamespaceCmd(ctx context.Context, ic NamespaceService, options d if options.json { json := text.IndentJSON(ns) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeNamespaceTable(ns) } diff --git a/internal/pkg/cli/command/index/namespace/list.go b/internal/pkg/cli/command/index/namespace/list.go index 974ea150..aaaebf23 100644 --- a/internal/pkg/cli/command/index/namespace/list.go +++ b/internal/pkg/cli/command/index/namespace/list.go @@ -109,7 +109,7 @@ func runListNamespaceCmd(ctx context.Context, ic NamespaceService, options listN if options.json { json := text.IndentJSON(resp) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(resp) } diff --git a/internal/pkg/cli/command/index/record/search.go b/internal/pkg/cli/command/index/record/search.go index c0ba9a6f..8c800859 100644 --- a/internal/pkg/cli/command/index/record/search.go +++ b/internal/pkg/cli/command/index/record/search.go @@ -265,7 +265,7 @@ func runSearchCmd(ctx context.Context, ic RecordService, options searchCmdOption } if options.json { - pcio.Println(text.IndentJSON(resp)) + pcio.PrintJSON(text.IndentJSON(resp)) } else { presenters.PrintSearchRecordsTable(resp) } diff --git a/internal/pkg/cli/command/index/record/upsert.go b/internal/pkg/cli/command/index/record/upsert.go index 26433afc..988b64d3 100644 --- a/internal/pkg/cli/command/index/record/upsert.go +++ b/internal/pkg/cli/command/index/record/upsert.go @@ -130,7 +130,7 @@ func runUpsertCmd(ctx context.Context, ic RecordService, options upsertCmdOption "records": len(batch), "namespace": options.namespace, } - pcio.Println(text.IndentJSON(summary)) + pcio.PrintJSON(text.IndentJSON(summary)) } else { msg.SuccessMsg("Upserted %d records into namespace %s (batch %d of %d)", len(batch), options.namespace, i+1, len(batches)) } diff --git a/internal/pkg/cli/command/index/vector/fetch.go b/internal/pkg/cli/command/index/vector/fetch.go index e2f4e7ee..8c385509 100644 --- a/internal/pkg/cli/command/index/vector/fetch.go +++ b/internal/pkg/cli/command/index/vector/fetch.go @@ -159,7 +159,7 @@ func runFetchCmd(ctx context.Context, options fetchCmdOptions) { func printFetchVectorsResults(results *presenters.FetchVectorsResults, options fetchCmdOptions) { if options.json { json := text.IndentJSON(results) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintFetchVectorsTable(results) } diff --git a/internal/pkg/cli/command/index/vector/list_vectors.go b/internal/pkg/cli/command/index/vector/list_vectors.go index 006d0e31..ada12df2 100644 --- a/internal/pkg/cli/command/index/vector/list_vectors.go +++ b/internal/pkg/cli/command/index/vector/list_vectors.go @@ -76,7 +76,7 @@ func runListVectorsCmd(ctx context.Context, options listVectorsCmdOptions) { if options.json { json := text.IndentJSON(resp) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintListVectorsTable(resp) } diff --git a/internal/pkg/cli/command/index/vector/query.go b/internal/pkg/cli/command/index/vector/query.go index cd3c33c2..c5acc69c 100644 --- a/internal/pkg/cli/command/index/vector/query.go +++ b/internal/pkg/cli/command/index/vector/query.go @@ -208,7 +208,7 @@ func runQueryCmd(ctx context.Context, options queryCmdOptions) { if options.json { json := text.IndentJSON(queryResponse) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintQueryVectorsTable(queryResponse) } diff --git a/internal/pkg/cli/command/index/vector/update.go b/internal/pkg/cli/command/index/vector/update.go index d7c1ce54..ccf8a566 100644 --- a/internal/pkg/cli/command/index/vector/update.go +++ b/internal/pkg/cli/command/index/vector/update.go @@ -197,7 +197,7 @@ func runUpdateCmd(ctx context.Context, options updateCmdOptions) { presenters.PrintUpdateVectorsByMetadataTable(resp) } else { json := text.IndentJSON(resp) - pcio.Println(json) + pcio.PrintJSON(json) } return } diff --git a/internal/pkg/cli/command/index/vector/upsert.go b/internal/pkg/cli/command/index/vector/upsert.go index abbb3589..10093f57 100644 --- a/internal/pkg/cli/command/index/vector/upsert.go +++ b/internal/pkg/cli/command/index/vector/upsert.go @@ -136,7 +136,7 @@ func runUpsertCmd(ctx context.Context, options upsertCmdOptions) { } else { if options.json { json := text.IndentJSON(resp) - pcio.Println(json) + pcio.PrintJSON(json) } else { msg.SuccessMsg("Upserted %d vectors into namespace %s (batch %d of %d)", len(batch), options.namespace, i+1, len(batches)) } diff --git a/internal/pkg/cli/command/organization/describe.go b/internal/pkg/cli/command/organization/describe.go index 2e008212..102b03e9 100644 --- a/internal/pkg/cli/command/organization/describe.go +++ b/internal/pkg/cli/command/organization/describe.go @@ -50,7 +50,7 @@ func NewDescribeOrganizationCmd() *cobra.Command { if options.json { json := text.IndentJSON(org) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeOrganizationTable(org) } diff --git a/internal/pkg/cli/command/organization/list.go b/internal/pkg/cli/command/organization/list.go index 96b27420..823f73fa 100644 --- a/internal/pkg/cli/command/organization/list.go +++ b/internal/pkg/cli/command/organization/list.go @@ -41,7 +41,7 @@ func NewListOrganizationsCmd() *cobra.Command { if options.json { json := text.IndentJSON(orgs) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/organization/update.go b/internal/pkg/cli/command/organization/update.go index 41203839..c24cd8e2 100644 --- a/internal/pkg/cli/command/organization/update.go +++ b/internal/pkg/cli/command/organization/update.go @@ -58,7 +58,7 @@ func NewUpdateOrganizationCmd() *cobra.Command { if options.json { json := text.IndentJSON(org) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/project/create.go b/internal/pkg/cli/command/project/create.go index 5620eaee..1732077d 100644 --- a/internal/pkg/cli/command/project/create.go +++ b/internal/pkg/cli/command/project/create.go @@ -69,7 +69,7 @@ func NewCreateProjectCmd() *cobra.Command { if options.json { json := text.IndentJSON(proj) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/project/describe.go b/internal/pkg/cli/command/project/describe.go index 72c080a1..bbc6778f 100644 --- a/internal/pkg/cli/command/project/describe.go +++ b/internal/pkg/cli/command/project/describe.go @@ -50,7 +50,7 @@ func NewDescribeProjectCmd() *cobra.Command { if options.json { json := text.IndentJSON(project) - pcio.Println(json) + pcio.PrintJSON(json) } else { presenters.PrintDescribeProjectTable(project) } diff --git a/internal/pkg/cli/command/project/list.go b/internal/pkg/cli/command/project/list.go index 214e7c07..987af1d7 100644 --- a/internal/pkg/cli/command/project/list.go +++ b/internal/pkg/cli/command/project/list.go @@ -42,7 +42,7 @@ func NewListProjectsCmd() *cobra.Command { if options.json { json := text.IndentJSON(projects) - pcio.Println(json) + pcio.PrintJSON(json) } else { printTable(projects) } diff --git a/internal/pkg/cli/command/project/update.go b/internal/pkg/cli/command/project/update.go index 9d04dc58..ec29f085 100644 --- a/internal/pkg/cli/command/project/update.go +++ b/internal/pkg/cli/command/project/update.go @@ -67,7 +67,7 @@ func NewUpdateProjectCmd() *cobra.Command { if options.json { json := text.IndentJSON(project) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/cli/command/target/target.go b/internal/pkg/cli/command/target/target.go index dc870a70..c2e57335 100644 --- a/internal/pkg/cli/command/target/target.go +++ b/internal/pkg/cli/command/target/target.go @@ -101,7 +101,7 @@ func NewTargetCmd() *cobra.Command { defaultAPIKey := secrets.DefaultAPIKey.Get() targetContext.DefaultAPIKey = presenters.MaskHeadTail(defaultAPIKey, 4, 4) json := text.IndentJSON(targetContext) - pcio.Println(json) + pcio.PrintJSON(json) return } log.Info(). @@ -260,7 +260,7 @@ func NewTargetCmd() *cobra.Command { defaultAPIKey := secrets.DefaultAPIKey.Get() targetContext.DefaultAPIKey = presenters.MaskHeadTail(defaultAPIKey, 4, 4) json := text.IndentJSON(targetContext) - pcio.Println(json) + pcio.PrintJSON(json) return } diff --git a/internal/pkg/utils/pcio/print.go b/internal/pkg/utils/pcio/print.go index 043e4015..33b59182 100644 --- a/internal/pkg/utils/pcio/print.go +++ b/internal/pkg/utils/pcio/print.go @@ -3,6 +3,7 @@ package pcio import ( "fmt" "io" + "os" ) // The purpose of this package is to stub out the fmt package so that @@ -15,6 +16,13 @@ func SetQuiet(q bool) { quiet = q } +// PrintJSON writes s to stdout regardless of quiet mode. +// Use this instead of Println when outputting structured JSON so that +// --quiet does not suppress machine-readable output. +func PrintJSON(s string) { + fmt.Fprintln(os.Stdout, s) +} + func Println(a ...any) { if !quiet { fmt.Println(a...) diff --git a/internal/pkg/utils/pcio/print_test.go b/internal/pkg/utils/pcio/print_test.go new file mode 100644 index 00000000..2ea06140 --- /dev/null +++ b/internal/pkg/utils/pcio/print_test.go @@ -0,0 +1,64 @@ +package pcio + +import ( + "bytes" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func captureStdout(t *testing.T, fn func()) string { + t.Helper() + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + orig := os.Stdout + os.Stdout = w + + fn() + + w.Close() + os.Stdout = orig + + done := make(chan string) + go func() { + var b bytes.Buffer + b.ReadFrom(r) + done <- b.String() + }() + return <-done +} + +func TestPrintJSON_EmitsWhenQuiet(t *testing.T) { + SetQuiet(true) + defer SetQuiet(false) + + out := captureStdout(t, func() { + PrintJSON("{}") + }) + + assert.Equal(t, "{}\n", out) +} + +func TestPrintJSON_EmitsWhenNotQuiet(t *testing.T) { + SetQuiet(false) + + out := captureStdout(t, func() { + PrintJSON("{}") + }) + + assert.Equal(t, "{}\n", out) +} + +func TestPrintln_SuppressedWhenQuiet(t *testing.T) { + SetQuiet(true) + defer SetQuiet(false) + + out := captureStdout(t, func() { + Println("should not appear") + }) + + assert.Empty(t, out) +}