diff --git a/docs/stream-cli_chat_truncate-channel.md b/docs/stream-cli_chat_truncate-channel.md new file mode 100644 index 0000000..a98aa7e --- /dev/null +++ b/docs/stream-cli_chat_truncate-channel.md @@ -0,0 +1,51 @@ +## stream-cli chat truncate-channel + +Truncate a channel + +### Synopsis + +Truncates a channel by removing all messages but keeping the channel metadata and members. + +Optional flags allow you to perform a hard delete, add a system message, skip push notifications, +and define the truncating user ID (for server-side calls). + + +``` +stream-cli chat truncate-channel --type [channel-type] --id [channel-id] [flags] +``` + +### Examples + +``` +# Truncate messages in 'general' channel of type messaging +$ stream-cli chat truncate-channel --type messaging --id general + +# Truncate with hard delete and system message +$ stream-cli chat truncate-channel --type messaging --id general --hard --message "Channel reset" --user-id system-user + +``` + +### Options + +``` + --hard [optional] Permanently delete messages instead of hiding them + -h, --help help for truncate-channel + -i, --id string [required] Channel ID + --message string [optional] System message to include in truncation (requires --message-user-id) + --message-user-id string [optional] User id for the message to include in truncation (required if --message is set) + --skip-push [optional] Skip push notifications + -t, --type string [required] Channel type such as 'messaging' + --user-id string [optional] User ID who performs the truncation +``` + +### Options inherited from parent commands + +``` + --app string [optional] Application name to use as it's defined in the configuration file + --config string [optional] Explicit config file path +``` + +### SEE ALSO + +* [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications + diff --git a/pkg/cmd/chat/channel/channel.go b/pkg/cmd/chat/channel/channel.go index 3887ad0..e87caef 100644 --- a/pkg/cmd/chat/channel/channel.go +++ b/pkg/cmd/chat/channel/channel.go @@ -28,6 +28,7 @@ func NewCmds() []*cobra.Command { assignRoleCmd(), hideCmd(), showCmd(), + truncateChannelCmd(), } } @@ -610,3 +611,82 @@ func showCmd() *cobra.Command { return cmd } + +func truncateChannelCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "truncate-channel --type [channel-type] --id [channel-id] [flags]", + Short: "Truncate a channel", + Long: heredoc.Doc(` + Truncates a channel by removing all messages but keeping the channel metadata and members. + + Optional flags allow you to perform a hard delete, add a system message, skip push notifications, + and define the truncating user ID (for server-side calls). + `), + Example: heredoc.Doc(` + # Truncate messages in 'general' channel of type messaging + $ stream-cli chat truncate-channel --type messaging --id general + + # Truncate with hard delete and system message + $ stream-cli chat truncate-channel --type messaging --id general --hard --message "Channel reset" --user-id system-user + `), + RunE: func(cmd *cobra.Command, args []string) error { + client, err := config.GetConfig(cmd).GetClient(cmd) + if err != nil { + return err + } + + typ, _ := cmd.Flags().GetString("type") + id, _ := cmd.Flags().GetString("id") + hard, _ := cmd.Flags().GetBool("hard") + userID, _ := cmd.Flags().GetString("user-id") + messageUserID, _ := cmd.Flags().GetString("message-user-id") + msgText, _ := cmd.Flags().GetString("message") + skipPush, _ := cmd.Flags().GetBool("skip-push") + + // If a message is provided, a message user id must also be provided + if msgText != "" && messageUserID == "" { + return errors.New("when using --message, you must also supply --message-user-id") + } + + var opts []stream.TruncateOption + + if hard { + opts = append(opts, stream.TruncateWithHardDelete()) + } + if skipPush { + opts = append(opts, stream.TruncateWithSkipPush()) + } + if userID != "" { + opts = append(opts, stream.TruncateWithUserID(userID)) + } + if msgText != "" { + opts = append(opts, stream.TruncateWithMessage(&stream.Message{ + Text: msgText, + User: &stream.User{ID: messageUserID}, + })) + } + + ch := client.Channel(typ, id) + _, err = ch.Truncate(cmd.Context(), opts...) + if err != nil { + return err + } + cmd.Printf("Successfully truncated channel [%s]\n", id) + return nil + }, + } + + fl := cmd.Flags() + fl.StringP("type", "t", "", "[required] Channel type such as 'messaging'") + fl.StringP("id", "i", "", "[required] Channel ID") + fl.String("user-id", "", "[optional] User ID who performs the truncation") + fl.String("message", "", "[optional] System message to include in truncation (requires --message-user-id)") + fl.String("message-user-id", "", "[optional] User id for the message to include in truncation (required if --message is set)") + fl.Bool("hard", false, "[optional] Permanently delete messages instead of hiding them") + fl.Bool("skip-push", false, "[optional] Skip push notifications") + + _ = cmd.MarkFlagRequired("type") + _ = cmd.MarkFlagRequired("id") + + return cmd +} diff --git a/pkg/cmd/chat/channel/channel_test.go b/pkg/cmd/chat/channel/channel_test.go index fd300a7..66aff3f 100644 --- a/pkg/cmd/chat/channel/channel_test.go +++ b/pkg/cmd/chat/channel/channel_test.go @@ -204,3 +204,39 @@ func TestHideAndShowChannel(t *testing.T) { require.NoError(t, err) require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully shown channel") } + +func TestTruncateChannel(t *testing.T) { + cmd := test.GetRootCmdWithSubCommands(NewCmds()...) + ch := test.InitChannel(t) + u := test.CreateUser() + + t.Cleanup(func() { + test.DeleteChannel(ch) + test.DeleteUser(u) + }) + + // Add user to channel and send a message + cmd.SetArgs([]string{"add-members", "-t", "messaging", "-i", ch, u}) + _, _ = cmd.ExecuteC() + + client := test.InitClient() + ctx := context.Background() + _, err := client.Channel("messaging", ch).SendMessage(ctx, &stream.Message{ + Text: "Pre-truncate message", + User: &stream.User{ID: u}, + }, u) + require.NoError(t, err) + + // Truncate the channel + cmd.SetArgs([]string{"truncate-channel", "-t", "messaging", "-i", ch, "--hard", "--message", "Channel reset", "--message-user-id", u}) + _, err = cmd.ExecuteC() + require.NoError(t, err) + + out := cmd.OutOrStdout().(*bytes.Buffer).String() + require.Contains(t, out, "Successfully truncated channel") + + // Verify that message history is empty + resp, err := client.Channel("messaging", ch).Query(ctx, &stream.QueryRequest{}) + require.NoError(t, err) + require.Empty(t, resp.Messages) +}