From 7d08070e9f6fa287f9d8d67848993359168aaf5b Mon Sep 17 00:00:00 2001 From: ARYANPATEL-BIT Date: Fri, 1 May 2026 18:44:59 +0530 Subject: [PATCH] fix(fxconfig/cli): enforce --submit and --wait flag dependencies Signed-off-by: ARYANPATEL-BIT --- tools/fxconfig/internal/cli/v1/flags.go | 17 +++- tools/fxconfig/internal/cli/v1/flags_test.go | 84 +++++++++++++++++++ .../internal/cli/v1/namespace_create.go | 14 +++- .../internal/cli/v1/namespace_update.go | 15 +++- 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/tools/fxconfig/internal/cli/v1/flags.go b/tools/fxconfig/internal/cli/v1/flags.go index 34f882b4..27923b5b 100644 --- a/tools/fxconfig/internal/cli/v1/flags.go +++ b/tools/fxconfig/internal/cli/v1/flags.go @@ -4,7 +4,11 @@ package v1 -import "github.com/spf13/cobra" +import ( + "fmt" + + "github.com/spf13/cobra" +) // outputFlag represents an output file path flag. type outputFlag string @@ -48,6 +52,17 @@ func (f *namespaceDeployFlags) bind(cmd *cobra.Command) { "Wait for transaction finalization (implies --submit)") } +// Validate checks that the flag combinations are valid. +func (f *namespaceDeployFlags) Validate() error { + if f.submit && !f.endorse { + return fmt.Errorf("the --submit flag requires --endorse") + } + if f.wait && !f.submit { + return fmt.Errorf("the --wait flag requires --submit") + } + return nil +} + // waitFlag represents a flag to wait for transaction finalization. type waitFlag bool diff --git a/tools/fxconfig/internal/cli/v1/flags_test.go b/tools/fxconfig/internal/cli/v1/flags_test.go index 734ba6e7..94350c49 100644 --- a/tools/fxconfig/internal/cli/v1/flags_test.go +++ b/tools/fxconfig/internal/cli/v1/flags_test.go @@ -88,3 +88,87 @@ func TestWaitFlag_Bind(t *testing.T) { require.NotNil(t, flag) require.Equal(t, "false", flag.DefValue) } + +func TestNamespaceDeployFlags_Validate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags namespaceDeployFlags + wantErr string + }{ + { + name: "valid: none", + flags: namespaceDeployFlags{ + endorse: false, + submit: false, + wait: false, + }, + }, + { + name: "valid: endorse only", + flags: namespaceDeployFlags{ + endorse: true, + submit: false, + wait: false, + }, + }, + { + name: "valid: endorse and submit", + flags: namespaceDeployFlags{ + endorse: true, + submit: true, + wait: false, + }, + }, + { + name: "valid: all flags", + flags: namespaceDeployFlags{ + endorse: true, + submit: true, + wait: true, + }, + }, + { + name: "invalid: submit without endorse", + flags: namespaceDeployFlags{ + endorse: false, + submit: true, + wait: false, + }, + wantErr: "the --submit flag requires --endorse", + }, + { + name: "invalid: wait without submit", + flags: namespaceDeployFlags{ + endorse: true, + submit: false, + wait: true, + }, + wantErr: "the --wait flag requires --submit", + }, + { + name: "invalid: wait without submit and endorse", + flags: namespaceDeployFlags{ + endorse: false, + submit: false, + wait: true, + }, + wantErr: "the --wait flag requires --submit", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := tt.flags.Validate() + if tt.wantErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/tools/fxconfig/internal/cli/v1/namespace_create.go b/tools/fxconfig/internal/cli/v1/namespace_create.go index ee42c063..34024989 100644 --- a/tools/fxconfig/internal/cli/v1/namespace_create.go +++ b/tools/fxconfig/internal/cli/v1/namespace_create.go @@ -39,9 +39,9 @@ Policy Syntax: • OutOf(2, 'Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member') - 2 of 3 orgs Transaction Lifecycle Flags: - --endorse Collect endorsement from local MSP - --submit Submit transaction to ordering service - --wait Wait for transaction finalization (implies --submit) + --endorse Collect endorsement from local MSP (if used without --submit, only saves the endorsed tx) + --submit Submit transaction to ordering service (requires --endorse) + --wait Wait for transaction finalization (requires --submit) Examples: # Create namespace with single org policy (save to file) @@ -61,6 +61,10 @@ Examples: --output=tx.json`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + if err := namespace.Validate(); err != nil { + return err + } + p := app.PolicyConfig{} p.Set(string(policy)) @@ -85,6 +89,10 @@ Examples: return nil } + if namespace.endorse && !namespace.submit { + ctx.Printer.Print("Transaction successfully endorsed and saved. You can submit it later using 'fxconfig tx submit'.") + } + o, err := ctx.IOTransactionCodec.Encode(res.TxID, res.Tx) if err != nil { return err diff --git a/tools/fxconfig/internal/cli/v1/namespace_update.go b/tools/fxconfig/internal/cli/v1/namespace_update.go index 12ad550d..15ad88d5 100644 --- a/tools/fxconfig/internal/cli/v1/namespace_update.go +++ b/tools/fxconfig/internal/cli/v1/namespace_update.go @@ -57,9 +57,18 @@ Examples: fxconfig namespace update payments \ --policy="OutOf(2, 'Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member')" \ --version=2 \ - --output=update_tx.json`, + --output=update_tx.json + +Transaction Lifecycle Flags: + --endorse Collect endorsement from local MSP (if used without --submit, only saves the endorsed tx) + --submit Submit transaction to ordering service (requires --endorse) + --wait Wait for transaction finalization (requires --submit)`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + if err := namespace.Validate(); err != nil { + return err + } + p := app.PolicyConfig{} p.Set(string(policy)) @@ -84,6 +93,10 @@ Examples: return nil } + if namespace.endorse && !namespace.submit { + ctx.Printer.Print("Transaction successfully endorsed and saved. You can submit it later using 'fxconfig tx submit'.") + } + o, err := ctx.IOTransactionCodec.Encode(res.TxID, res.Tx) if err != nil { return err