diff --git a/docs/generate_doc/ticloud_serverless_branch_create.md b/docs/generate_doc/ticloud_serverless_branch_create.md index c3545ab1..88d27fb2 100644 --- a/docs/generate_doc/ticloud_serverless_branch_create.md +++ b/docs/generate_doc/ticloud_serverless_branch_create.md @@ -19,10 +19,11 @@ ticloud serverless branch create [flags] ### Options ``` - -c, --cluster-id string The ID of the cluster, in which the branch will be created. - -n, --display-name string The displayName of the branch to be created. - -h, --help help for create - --parent-id string The ID of the branch parent, default is cluster id. + -c, --cluster-id string The ID of the cluster, in which the branch will be created. + -n, --display-name string The displayName of the branch to be created. + -h, --help help for create + --parent-id string The ID of the branch parent, default is cluster id. + --parent-timestamp string The timestamp of the parent branch, default is current time. (RFC3339 format, e.g., 2024-01-01T00:00:00Z) ``` ### Options inherited from parent commands diff --git a/internal/cli/serverless/branch/create.go b/internal/cli/serverless/branch/create.go index c91850ca..c43159fe 100644 --- a/internal/cli/serverless/branch/create.go +++ b/internal/cli/serverless/branch/create.go @@ -31,12 +31,14 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/fatih/color" + "github.com/go-openapi/strfmt" "github.com/juju/errors" "github.com/spf13/cobra" ) var createBranchField = map[string]int{ - flag.DisplayName: 0, + flag.DisplayName: 0, + flag.ParentTimestamp: 1, } const ( @@ -52,6 +54,14 @@ func (c CreateOpts) NonInteractiveFlags() []string { return []string{ flag.DisplayName, flag.ClusterID, + flag.ParentTimestamp, + } +} + +func (c CreateOpts) RequiredFlags() []string { + return []string{ + flag.ClusterID, + flag.DisplayName, } } @@ -66,7 +76,7 @@ func (c *CreateOpts) MarkInteractive(cmd *cobra.Command) error { } // Mark required flags if !c.interactive { - for _, fn := range flags { + for _, fn := range c.RequiredFlags() { err := cmd.MarkFlagRequired(fn) if err != nil { return err @@ -108,6 +118,8 @@ func CreateCmd(h *internal.Helper) *cobra.Command { var branchName string var clusterId string var parentID string + var parentTimestampStr string + var parentTimestamp *strfmt.DateTime if opts.interactive { if !h.IOStreams.CanPrompt { return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") @@ -138,6 +150,7 @@ func CreateCmd(h *internal.Helper) *cobra.Command { if len(branchName) == 0 { return errors.New("branch name is required") } + parentTimestampStr = inputModel.(ui.TextInputModel).Inputs[createBranchField[flag.ParentTimestamp]].Value() } else { // non-interactive mode, get values from flags var err error @@ -153,11 +166,25 @@ func CreateCmd(h *internal.Helper) *cobra.Command { if err != nil { return errors.Trace(err) } + parentTimestampStr, err = cmd.Flags().GetString(flag.ParentTimestamp) + if err != nil { + return errors.Trace(err) + } + } + + if len(parentTimestampStr) != 0 { + t, err := time.Parse(time.RFC3339, parentTimestampStr) + if err != nil { + return errors.New("Invalid parent timestamp format, please use RFC3339 format") + } + tFormated := strfmt.DateTime(t) + parentTimestamp = &tFormated } params := branchApi.NewBranchServiceCreateBranchParams().WithClusterID(clusterId).WithBranch(&branchModel.V1beta1Branch{ - DisplayName: &branchName, - ParentID: parentID, + DisplayName: &branchName, + ParentID: parentID, + ParentTimestamp: parentTimestamp, }).WithContext(ctx) if h.IOStreams.CanPrompt { @@ -179,6 +206,7 @@ func CreateCmd(h *internal.Helper) *cobra.Command { createCmd.Flags().StringP(flag.DisplayName, flag.DisplayNameShort, "", "The displayName of the branch to be created.") createCmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The ID of the cluster, in which the branch will be created.") createCmd.Flags().StringP(flag.ParentID, "", "", "The ID of the branch parent, default is cluster id.") + createCmd.Flags().StringP(flag.ParentTimestamp, "", "", "The timestamp of the parent branch, default is current time. (RFC3339 format, e.g., 2024-01-01T00:00:00Z)") return createCmd } @@ -280,9 +308,11 @@ func initialCreateBranchInputModel() ui.TextInputModel { t.Focus() t.PromptStyle = config.FocusedStyle t.TextStyle = config.FocusedStyle - - m.Inputs[v] = t + case flag.ParentTimestamp: + timestampExample := time.Now().Format(time.RFC3339) + t.Placeholder = fmt.Sprintf("Parent Timestamp (optional, e.g., %s)", timestampExample) } + m.Inputs[v] = t } return m } diff --git a/internal/flag/flag.go b/internal/flag/flag.go index d4b6d3b8..be6ea94e 100644 --- a/internal/flag/flag.go +++ b/internal/flag/flag.go @@ -84,6 +84,7 @@ const ( TableWhere string = "where" TableFilter string = "filter" ParentID string = "parent-id" + ParentTimestamp string = "parent-timestamp" PublicEndpointDisabled string = "disable-public-endpoint" ParquetCompression string = "parquet.compression" ) diff --git a/pkg/tidbcloud/v1beta1/branch/branch.swagger.json b/pkg/tidbcloud/v1beta1/branch/branch.swagger.json index ab87a5d7..d1447b60 100644 --- a/pkg/tidbcloud/v1beta1/branch/branch.swagger.json +++ b/pkg/tidbcloud/v1beta1/branch/branch.swagger.json @@ -419,6 +419,12 @@ "parentDisplayName": { "type": "string", "description": "OPTIONAL. The parent display name of this branch." + }, + "parentTimestamp": { + "type": "string", + "format": "date-time", + "x-nullable": true, + "description": "Optional. The point in time on the parent branch the branch will be created from." } }, "title": "Message for branch", diff --git a/pkg/tidbcloud/v1beta1/branch/models/branch_state.go b/pkg/tidbcloud/v1beta1/branch/models/branch_state.go index 9bbbf458..0789137d 100644 --- a/pkg/tidbcloud/v1beta1/branch/models/branch_state.go +++ b/pkg/tidbcloud/v1beta1/branch/models/branch_state.go @@ -16,13 +16,12 @@ import ( // BranchState Output Only. Branch State. // -// - STATE_UNSPECIFIED: The state of the branch is unknown. // - CREATING: The branch is being created. // - ACTIVE: The branch is active and running. // - DELETED: The branch is being deleted. // - MAINTENANCE: The branch is under maintenance. // -// swagger:model BranchState +// swagger:model Branch.State type BranchState string func NewBranchState(value BranchState) *BranchState { diff --git a/pkg/tidbcloud/v1beta1/branch/models/v1beta1_branch.go b/pkg/tidbcloud/v1beta1/branch/models/v1beta1_branch.go index 7e8cd230..50af2ec8 100644 --- a/pkg/tidbcloud/v1beta1/branch/models/v1beta1_branch.go +++ b/pkg/tidbcloud/v1beta1/branch/models/v1beta1_branch.go @@ -56,6 +56,10 @@ type V1beta1Branch struct { // OPTIONAL. The parent ID of this branch. ParentID string `json:"parentId,omitempty"` + // Optional. The point in time on the parent branch the branch will be created from. + // Format: date-time + ParentTimestamp *strfmt.DateTime `json:"parentTimestamp,omitempty"` + // Output only. The state of this branch. // Read Only: true State V1beta1BranchState `json:"state,omitempty"` @@ -91,6 +95,10 @@ func (m *V1beta1Branch) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateParentTimestamp(formats); err != nil { + res = append(res, err) + } + if err := m.validateState(formats); err != nil { res = append(res, err) } @@ -149,6 +157,18 @@ func (m *V1beta1Branch) validateEndpoints(formats strfmt.Registry) error { return nil } +func (m *V1beta1Branch) validateParentTimestamp(formats strfmt.Registry) error { + if swag.IsZero(m.ParentTimestamp) { // not required + return nil + } + + if err := validate.FormatOf("parentTimestamp", "body", "date-time", m.ParentTimestamp.String(), formats); err != nil { + return err + } + + return nil +} + func (m *V1beta1Branch) validateState(formats strfmt.Registry) error { if swag.IsZero(m.State) { // not required return nil