From 9649a7a8c72aef23befe6bbe421a859076e4a934 Mon Sep 17 00:00:00 2001 From: shi yuhang <52435083+shiyuhang0@users.noreply.github.com> Date: Fri, 30 Jun 2023 17:13:48 +0800 Subject: [PATCH] Branch cli (#105) --- .github/license-checker-config.yml | 3 +- Makefile | 6 + internal/cli/branch/branch.go | 35 ++ internal/cli/branch/connectinfo.go | 214 ++++++++ internal/cli/branch/connectinfo_test.go | 231 +++++++++ internal/cli/branch/create.go | 280 +++++++++++ internal/cli/branch/create_test.go | 129 +++++ internal/cli/branch/delete.go | 182 +++++++ internal/cli/branch/delete_test.go | 117 +++++ internal/cli/branch/describe.go | 134 +++++ internal/cli/branch/describe_test.go | 142 ++++++ internal/cli/branch/list.go | 138 ++++++ internal/cli/branch/list_test.go | 212 ++++++++ internal/cli/cluster/connectinfo.go | 36 +- internal/cli/connect/connect.go | 92 +++- internal/cli/root.go | 2 + internal/flag/flag.go | 4 + internal/mock/api_client.go | 141 +++++- internal/mock/mysql_helper.go | 2 +- internal/mock/sender.go | 2 +- internal/service/cloud/api_client.go | 56 ++- internal/service/cloud/logic.go | 116 +++++ pkg/tidbcloud/branch/README.md | 54 ++ pkg/tidbcloud/branch/branch.swagger.json | 468 ++++++++++++++++++ .../branch_service/branch_service_client.go | 193 ++++++++ .../create_branch_parameters.go | 168 +++++++ .../branch_service/create_branch_responses.go | 243 +++++++++ .../delete_branch_parameters.go | 173 +++++++ .../branch_service/delete_branch_responses.go | 180 +++++++ .../branch_service/get_branch_parameters.go | 173 +++++++ .../branch_service/get_branch_responses.go | 182 +++++++ .../list_branches_parameters.go | 240 +++++++++ .../branch_service/list_branches_responses.go | 182 +++++++ .../branch/client/tidbcloud_branch_client.go | 112 +++++ .../branch/models/googlerpc_status.go | 222 +++++++++ .../branch/models/openapi_basic_branch.go | 265 ++++++++++ pkg/tidbcloud/branch/models/openapi_branch.go | 397 +++++++++++++++ .../branch/models/openapi_branch_state.go | 84 ++++ .../models/openapi_create_branch_resp.go | 72 +++ .../branch/models/openapi_endpoints.go | 109 ++++ .../models/openapi_list_branches_resp.go | 141 ++++++ .../branch/models/openapi_private_endpoint.go | 169 +++++++ .../models/openapi_private_endpoint_aws.go | 53 ++ .../models/openapi_private_endpoint_gcp.go | 50 ++ .../branch/models/openapi_public_endpoint.go | 56 +++ pkg/tidbcloud/branch/models/openapi_usages.go | 56 +++ pkg/tidbcloud/branch/models/protobuf_any.go | 50 ++ 47 files changed, 6315 insertions(+), 51 deletions(-) create mode 100644 internal/cli/branch/branch.go create mode 100644 internal/cli/branch/connectinfo.go create mode 100644 internal/cli/branch/connectinfo_test.go create mode 100644 internal/cli/branch/create.go create mode 100644 internal/cli/branch/create_test.go create mode 100644 internal/cli/branch/delete.go create mode 100644 internal/cli/branch/delete_test.go create mode 100644 internal/cli/branch/describe.go create mode 100644 internal/cli/branch/describe_test.go create mode 100644 internal/cli/branch/list.go create mode 100644 internal/cli/branch/list_test.go create mode 100644 pkg/tidbcloud/branch/README.md create mode 100644 pkg/tidbcloud/branch/branch.swagger.json create mode 100644 pkg/tidbcloud/branch/client/branch_service/branch_service_client.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/create_branch_parameters.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/create_branch_responses.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/delete_branch_parameters.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/delete_branch_responses.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/get_branch_parameters.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/get_branch_responses.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/list_branches_parameters.go create mode 100644 pkg/tidbcloud/branch/client/branch_service/list_branches_responses.go create mode 100644 pkg/tidbcloud/branch/client/tidbcloud_branch_client.go create mode 100644 pkg/tidbcloud/branch/models/googlerpc_status.go create mode 100644 pkg/tidbcloud/branch/models/openapi_basic_branch.go create mode 100644 pkg/tidbcloud/branch/models/openapi_branch.go create mode 100644 pkg/tidbcloud/branch/models/openapi_branch_state.go create mode 100644 pkg/tidbcloud/branch/models/openapi_create_branch_resp.go create mode 100644 pkg/tidbcloud/branch/models/openapi_endpoints.go create mode 100644 pkg/tidbcloud/branch/models/openapi_list_branches_resp.go create mode 100644 pkg/tidbcloud/branch/models/openapi_private_endpoint.go create mode 100644 pkg/tidbcloud/branch/models/openapi_private_endpoint_aws.go create mode 100644 pkg/tidbcloud/branch/models/openapi_private_endpoint_gcp.go create mode 100644 pkg/tidbcloud/branch/models/openapi_public_endpoint.go create mode 100644 pkg/tidbcloud/branch/models/openapi_usages.go create mode 100644 pkg/tidbcloud/branch/models/protobuf_any.go diff --git a/.github/license-checker-config.yml b/.github/license-checker-config.yml index 6e522c2e..4988261d 100644 --- a/.github/license-checker-config.yml +++ b/.github/license-checker-config.yml @@ -7,6 +7,5 @@ header: - '**/*.go' paths-ignore: - '**/mock/**' - - 'pkg/tidbcloud/import/**' - - 'pkg/tidbcloud/connect_info/**' + - 'pkg/tidbcloud/**' comment: on-failure diff --git a/Makefile b/Makefile index 84dd46f6..19a4e696 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,12 @@ generate-import-client: ## Generate import client addcopy: ## Add copyright to all files @scripts/add-copy.sh +.PHONY: generate-branch-client + generate-branch-client: + @echo "==> Generating branch client" + go install github.com/go-swagger/go-swagger/cmd/swagger@latest + swagger generate client -f pkg/tidbcloud/branch/branch.swagger.json -A tidbcloud-branch -t pkg/tidbcloud/branch + .PHONY: fmt fmt: ## Format changed go @scripts/fmt.sh diff --git a/internal/cli/branch/branch.go b/internal/cli/branch/branch.go new file mode 100644 index 00000000..45d33379 --- /dev/null +++ b/internal/cli/branch/branch.go @@ -0,0 +1,35 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "tidbcloud-cli/internal" + + "github.com/spf13/cobra" +) + +func BranchCmd(h *internal.Helper) *cobra.Command { + var branchCmd = &cobra.Command{ + Use: "branch", + Short: "Manage branches in your serverless cluster", + } + + branchCmd.AddCommand(CreateCmd(h)) + branchCmd.AddCommand(DeleteCmd(h)) + branchCmd.AddCommand(ListCmd(h)) + branchCmd.AddCommand(DescribeCmd(h)) + branchCmd.AddCommand(ConnectInfoCmd(h)) + return branchCmd +} diff --git a/internal/cli/branch/connectinfo.go b/internal/cli/branch/connectinfo.go new file mode 100644 index 00000000..efadab0a --- /dev/null +++ b/internal/cli/branch/connectinfo.go @@ -0,0 +1,214 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "fmt" + "runtime" + "strconv" + "strings" + + "tidbcloud-cli/internal" + clu "tidbcloud-cli/internal/cli/cluster" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/flag" + "tidbcloud-cli/internal/service/cloud" + "tidbcloud-cli/internal/util" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + + "github.com/juju/errors" + "github.com/spf13/cobra" +) + +type connectInfoOpts struct { + interactive bool +} + +func (c connectInfoOpts) NonInteractiveFlags() []string { + return []string{ + flag.ClusterID, + flag.BranchID, + flag.ClientName, + flag.OperatingSystem, + } +} + +func ConnectInfoCmd(h *internal.Helper) *cobra.Command { + opts := connectInfoOpts{ + interactive: true, + } + + cmd := &cobra.Command{ + Use: "connect-info", + Short: "Get connection string for the specified branch", + Example: fmt.Sprintf(` Get connection string in interactive mode: + $ %[1]s branch connect-info + + Get connection string in non-interactive mode: + $ %[1]s branch connect-info --cluster-id --branch-id --client --operating-system +`, config.CliName), + PreRunE: func(cmd *cobra.Command, args []string) error { + flags := opts.NonInteractiveFlags() + for _, fn := range flags { + f := cmd.Flags().Lookup(fn) + if f != nil && f.Changed { + opts.interactive = false + } + } + + // mark required flags in non-interactive mode + if !opts.interactive { + for _, fn := range flags { + err := cmd.MarkFlagRequired(fn) + if err != nil { + return errors.Trace(err) + } + } + } + + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + // flags + var branchID, clusterID, client, operatingSystem string + + // Get TiDBCloudClient + d, err := h.Client() + if err != nil { + return err + } + + if opts.interactive { // interactive mode + if !h.IOStreams.CanPrompt { + return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") + } + + // Get cluster id + project, err := cloud.GetSelectedProject(h.QueryPageSize, d) + if err != nil { + return err + } + cluster, err := cloud.GetSelectedCluster(project.ID, h.QueryPageSize, d) + if err != nil { + return err + } + clusterID = cluster.ID + + // Get branch id + branch, err := cloud.GetSelectedBranch(clusterID, h.QueryPageSize, d) + if err != nil { + return err + } + branchID = branch.ID + + // Get client + clientNameForInteractive, err := cloud.GetSelectedConnectClient(clu.ConnectClientsList) + if err != nil { + return err + } + client = clu.ClientsForInteractiveMap[clientNameForInteractive] + + // Detect operating system + // TODO: detect linux operating system name + goOS := runtime.GOOS + if goOS == "darwin" { + goOS = "macOS" + } else if goOS == "windows" { + goOS = "Windows" + } + if goOS != "" && goOS != "linux" { + for id, value := range clu.OperatingSystemList { + if strings.Contains(value, goOS) { + operatingSystemValueWithFlag := clu.OperatingSystemList[id] + " (Detected)" + clu.OperatingSystemList = append([]string{operatingSystemValueWithFlag}, + append(clu.OperatingSystemList[:id], clu.OperatingSystemList[id+1:]...)...) + break + } + } + } + + // Get operating system + operatingSystemCombination, err := cloud.GetSelectedConnectOs(clu.OperatingSystemList) + if err != nil { + return err + } + operatingSystem = strings.Split(operatingSystemCombination, "/")[0] + + } else { // non-interactive mode + clusterID, err = cmd.Flags().GetString(flag.ClusterID) + if err != nil { + return err + } + + branchID, err = cmd.Flags().GetString(flag.BranchID) + if err != nil { + return err + } + + clientNameForHelp, err := cmd.Flags().GetString(flag.ClientName) + if err != nil { + return err + } + if v, ok := clu.ClientsForHelpMap[strings.ToLower(clientNameForHelp)]; ok { + client = v + } else { + return errors.New(fmt.Sprintf("Unsupported client. Run \"%[1]s cluster connect-info -h\" to check supported clients list", config.CliName)) + } + + operatingSystem, err = cmd.Flags().GetString(flag.OperatingSystem) + if err != nil { + return err + } + if !clu.Contains(operatingSystem, clu.OperatingSystemListForHelp) { + return errors.New(fmt.Sprintf("Unsupported operating system. Run \"%[1]s cluster connect-info -h\" to check supported operating systems list", config.CliName)) + } + } + + // Get branch info + params := branchApi.NewGetBranchParams().WithBranchID(branchID).WithClusterID(clusterID) + branchInfo, err := d.GetBranch(params) + if err != nil { + return err + } + + // Get connect parameter + defaultUser := fmt.Sprintf("%s.root", *branchInfo.Payload.UserPrefix) + host := branchInfo.Payload.Endpoints.PublicEndpoint.Host + port := strconv.Itoa(int(branchInfo.Payload.Endpoints.PublicEndpoint.Port)) + + // Get connection string + connectInfo, err := cloud.RetrieveConnectInfo(d) + if err != nil { + return err + } + connectionString, err := util.GenerateConnectionString(connectInfo, client, host, defaultUser, port, clu.SERVERLESS, operatingSystem, util.Shell) + if err != nil { + return err + } + fmt.Fprintln(h.IOStreams.Out) + fmt.Fprintln(h.IOStreams.Out, connectionString) + + return nil + }, + } + + cmd.Flags().StringP(flag.BranchID, flag.BranchIDShort, "", "Branch ID") + cmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "Cluster ID") + cmd.Flags().String(flag.ClientName, "", fmt.Sprintf("Connected client. Supported clients: %q", clu.ConnectClientsListForHelp)) + cmd.Flags().String(flag.OperatingSystem, "", fmt.Sprintf("Operating system name. "+ + "Supported operating systems: %q", clu.OperatingSystemListForHelp)) + + return cmd +} diff --git a/internal/cli/branch/connectinfo_test.go b/internal/cli/branch/connectinfo_test.go new file mode 100644 index 00000000..02775153 --- /dev/null +++ b/internal/cli/branch/connectinfo_test.go @@ -0,0 +1,231 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "testing" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/iostream" + "tidbcloud-cli/internal/mock" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + connectInfoService "tidbcloud-cli/pkg/tidbcloud/connect_info/client/connect_info_service" + connectInfoModel "tidbcloud-cli/pkg/tidbcloud/connect_info/models" + + "github.com/juju/errors" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +const getConnectInfoResultStr = `{ + "ca_path": { + "Alpine": "/etc/ssl/cert.pem", + "Arch": "/etc/ssl/certs/ca-certificates.crt", + "CentOS": "/etc/pki/tls/certs/ca-bundle.crt", + "Debian": "/etc/ssl/certs/ca-certificates.crt", + "Fedora": "/etc/pki/tls/certs/ca-bundle.crt", + "OpenSUSE": "/etc/ssl/ca-bundle.pem", + "RedHat": "/etc/pki/tls/certs/ca-bundle.crt", + "Ubuntu": "/etc/ssl/certs/ca-certificates.crt", + "Windows": "", + "macOS": "/etc/ssl/cert.pem", + "others": "" + }, + "client_data": [ + { + "id": "mysql_cli", + "display_name": "MySQL CLI", + "language": "shell", + "content": [ + { + "type": "serverless", + "comment": "", + "connection_string": "mysql -u '${username}' -h ${host} -P ${port} -D test --ssl-mode=VERIFY_IDENTITY --ssl-ca=${ca_root_path} -p${password}", + "connection_example": "" + }, + { + "type": "dedicated", + "comment": "# version >= 5.7.11", + "connection_string": "mysql -u '${username}' -h ${host} -P ${port} -D test --ssl-mode=VERIFY_IDENTITY --ssl-ca=ca.pem -p${password}", + "connection_example": "" + } + ] + }, + { + "id": "mysqlclient", + "display_name": "mysqlclient", + "language": "python", + "content": [ + { + "type": "serverless", + "comment": "# Be sure to replace the parameters in the following connection string.\n# Requires mysqlclient package ('pip3 install mysqlclient'). Please check https://pypi.org/project/mysqlclient/ for install guide.", + "connection_string": "host=\"${host}\", \nuser=\"${username}\", \npassword=\"${password}\", \nport=${port}, \ndatabase=\"test\", \nssl={\"ca\": \"${ca_root_path}\"}", + "connection_example": "import MySQLdb\n\nconnection = MySQLdb.connect(\n host=\"${host}\",\n port=${port},\n user=\"${username}\",\n password=\"${password}\",\n database=\"test\",\n ssl={\n \"ca\": \"${ca_root_path}\"\n }\n)\n\nwith connection:\n with connection.cursor() as cursor:\n cursor.execute(\"SELECT DATABASE();\")\n m = cursor.fetchone()\n print(m[0])" + }, + { + "type": "dedicated", + "comment": "# Be sure to replace the parameters in the following connection string.\n# Requires mysqlclient package ('pip3 install mysqlclient'). Please check https://pypi.org/project/mysqlclient/ for install guide.", + "connection_string": "host=\"${host}\", \nuser=\"${username}\", \npassword=\"${password}\", \nport=${port}, \ndatabase=\"test\", \nssl={\"ca\": \"ca.pem\"}", + "connection_example": "import MySQLdb\n\nconnection = MySQLdb.connect(\n host=\"${host}\",\n port=${port},\n user=\"${username}\",\n password=\"${password}\",\n database=\"test\",\n ssl={\n \"ca\": \"ca.pem\"\n }\n)\n\nwith connection:\n with connection.cursor() as cursor:\n cursor.execute(\"SELECT DATABASE();\")\n m = cursor.fetchone()\n print(m[0])" + } + ] + }, + { + "id": "general", + "display_name": "General", + "language": "", + "content": [ + { + "type": "serverless", + "comment": "", + "connection_string": "Host: ${host}\nUsername: ${username}\nPort: ${port}\nPassword: ${password}", + "connection_example": "" + }, + { + "type": "dedicated", + "comment": "", + "connection_string": "Host: ${host}\nUsername: ${username}\nPort: ${port}\nPassword: ${password}", + "connection_example": "" + } + ] + } + ] +} +` + +type ConnectInfoSuite struct { + suite.Suite + h *internal.Helper + mockClient *mock.TiDBCloudClient +} + +func (suite *ConnectInfoSuite) SetupTest() { + if err := os.Setenv("NO_COLOR", "true"); err != nil { + suite.T().Error(err) + } + + suite.mockClient = new(mock.TiDBCloudClient) + suite.h = &internal.Helper{ + Client: func() (cloud.TiDBCloudClient, error) { + return suite.mockClient, nil + }, + IOStreams: iostream.Test(), + } +} + +func (suite *ConnectInfoSuite) TestConnectInfoArgs() { + assert := require.New(suite.T()) + + branchID := "12345" + clusterID := "12345" + clientCLI := "mysql_cli" + clientDriver := "python_mysqlclient" + clientParameter := "general" + operatingSystem := "macos" + + connectInfoBody := &connectInfoModel.ConnectInfo{} + err := json.Unmarshal([]byte(getConnectInfoResultStr), connectInfoBody) + assert.Nil(err) + getConnectInfoResult := &connectInfoService.GetInfoOK{ + Payload: connectInfoBody, + } + suite.mockClient.On("GetConnectInfo", connectInfoService.NewGetInfoParams()). + Return(getConnectInfoResult, nil) + + branchBody := &branchModel.OpenapiBranch{} + err = json.Unmarshal([]byte(getBranchResultStr), branchBody) + assert.Nil(err) + getClusterResult := &branchApi.GetBranchOK{ + Payload: branchBody, + } + suite.mockClient.On("GetBranch", branchApi.NewGetBranchParams(). + WithBranchID(branchID).WithClusterID(clusterID)). + Return(getClusterResult, nil) + + tests := []struct { + name string + args []string + err error + stdoutString string + stderrString string + }{ + { + name: "get MySQl CLI connecting string", + args: []string{"-b", branchID, "-c", clusterID, "--client", clientCLI, "--operating-system", operatingSystem}, + stdoutString: "\nmysql -u '49dDUPpoxGXdsY9.root' -h gateway01.us-east-1.dev.shared.aws.tidbcloud.com -P 4000 -D test --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/cert.pem -p${password}\n", + }, + { + name: "get mysqlclient connecting string", + args: []string{"-b", branchID, "-c", clusterID, "--client", clientDriver, "--operating-system", operatingSystem}, + stdoutString: ` +host="gateway01.us-east-1.dev.shared.aws.tidbcloud.com", +user="49dDUPpoxGXdsY9.root", +password="${password}", +port=4000, +database="test", +ssl={"ca": "/etc/ssl/cert.pem"} +`, + }, + { + name: "get standard connection parameter", + args: []string{"-b", branchID, "-c", clusterID, "--client", clientParameter, "--operating-system", operatingSystem}, + stdoutString: ` +Host: gateway01.us-east-1.dev.shared.aws.tidbcloud.com +Port: 4000 +User: 49dDUPpoxGXdsY9.root +`, + }, + { + name: "with unsupported client name", + args: []string{"-b", branchID, "-c", clusterID, "--client", "JAVA", "--operating-system", operatingSystem}, + err: errors.New(fmt.Sprintf("Unsupported client. Run \"%[1]s cluster connect-info -h\" to check supported clients list", config.CliName)), + }, + { + name: "with unsupported operating system", + args: []string{"-b", branchID, "-c", clusterID, "--client", "python_mysqlclient", "--operating-system", "Manjaro"}, + err: errors.New(fmt.Sprintf("Unsupported operating system. Run \"%[1]s cluster connect-info -h\" to check supported operating systems list", config.CliName)), + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cmd := ConnectInfoCmd(suite.h) + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err := cmd.Execute() + if err != nil { + assert.Equal(tt.err.Error(), err.Error()) + } + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + if tt.err == nil { + suite.mockClient.AssertExpectations(suite.T()) + } + }) + } +} + +func TestConnectInfoSuite(t *testing.T) { + suite.Run(t, new(ConnectInfoSuite)) +} diff --git a/internal/cli/branch/create.go b/internal/cli/branch/create.go new file mode 100644 index 00000000..d985cd28 --- /dev/null +++ b/internal/cli/branch/create.go @@ -0,0 +1,280 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "context" + "fmt" + "time" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/flag" + "tidbcloud-cli/internal/service/cloud" + "tidbcloud-cli/internal/ui" + "tidbcloud-cli/internal/util" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/fatih/color" + "github.com/juju/errors" + "github.com/spf13/cobra" +) + +var createBranchField = map[string]int{ + flag.BranchName: 0, +} + +const ( + WaitInterval = 1 * time.Second + WaitTimeout = 5 * time.Minute +) + +type CreateOpts struct { + interactive bool +} + +func (c CreateOpts) NonInteractiveFlags() []string { + return []string{ + flag.BranchName, + flag.ClusterID, + } +} + +func CreateCmd(h *internal.Helper) *cobra.Command { + opts := CreateOpts{ + interactive: true, + } + + var createCmd = &cobra.Command{ + Use: "create", + Short: "Create a branch in a specified serverless cluster", + Args: cobra.NoArgs, + Example: fmt.Sprintf(` Create a branch in interactive mode: + $ %[1]s branch create + + Create a branch in non-interactive mode: + $ %[1]s branch create --cluster-id --branch-name `, + config.CliName), + PreRunE: func(cmd *cobra.Command, args []string) error { + flags := opts.NonInteractiveFlags() + for _, fn := range flags { + f := cmd.Flags().Lookup(fn) + if f != nil && f.Changed { + opts.interactive = false + } + } + + // mark required flags in non-interactive mode + if !opts.interactive { + for _, fn := range flags { + err := cmd.MarkFlagRequired(fn) + if err != nil { + return errors.Trace(err) + } + } + } + + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + d, err := h.Client() + if err != nil { + return err + } + + var branchName string + var clusterId string + if opts.interactive { + if !h.IOStreams.CanPrompt { + return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") + } + + project, err := cloud.GetSelectedProject(h.QueryPageSize, d) + if err != nil { + return err + } + + cluster, err := cloud.GetSelectedCluster(project.ID, h.QueryPageSize, d) + if err != nil { + return err + } + clusterId = cluster.ID + + // variables for input + inputModel, err := GetCreateBranchInput() + if err != nil { + return err + } + branchName = inputModel.(ui.TextInputModel).Inputs[createBranchField[flag.BranchName]].Value() + if len(branchName) == 0 { + return errors.New("branch name is required") + } + } else { + // non-interactive mode, get values from flags + var err error + branchName, err = cmd.Flags().GetString(flag.BranchName) + if err != nil { + return errors.Trace(err) + } + clusterId, err = cmd.Flags().GetString(flag.ClusterID) + if err != nil { + return errors.Trace(err) + } + } + + params := branchApi.NewCreateBranchParams().WithClusterID(clusterId).WithBody(branchApi.CreateBranchBody{ + DisplayName: &branchName, + }) + + if h.IOStreams.CanPrompt { + err := CreateAndSpinnerWait(ctx, d, params, h) + if err != nil { + return errors.Trace(err) + } + } else { + err := CreateAndWaitReady(h, d, params) + if err != nil { + return err + } + } + + return nil + }, + } + + createCmd.Flags().String(flag.BranchName, "", "Name of the branch to de created") + createCmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The ID of the cluster, in which the branch will be created") + return createCmd +} + +func CreateAndWaitReady(h *internal.Helper, d cloud.TiDBCloudClient, params *branchApi.CreateBranchParams) error { + createBranchResult, err := d.CreateBranch(params) + if err != nil { + return errors.Trace(err) + } + newBranchID := *createBranchResult.GetPayload().ID + + fmt.Fprintln(h.IOStreams.Out, "... Waiting for branch to be ready") + ticker := time.NewTicker(WaitInterval) + defer ticker.Stop() + timer := time.After(WaitTimeout) + for { + select { + case <-timer: + return errors.New("Timeout waiting for branch to be ready, please check status on dashboard.") + case <-ticker.C: + clusterResult, err := d.GetBranch(branchApi.NewGetBranchParams(). + WithClusterID(params.ClusterID). + WithBranchID(newBranchID)) + if err != nil { + return errors.Trace(err) + } + s := clusterResult.GetPayload().State + if *s == branchModel.OpenapiBranchStateREADY { + fmt.Fprint(h.IOStreams.Out, color.GreenString("Branch %s is ready.", newBranchID)) + return nil + } + } + } +} + +func CreateAndSpinnerWait(ctx context.Context, d cloud.TiDBCloudClient, params *branchApi.CreateBranchParams, h *internal.Helper) error { + // use spinner to indicate that the cluster is being created + task := func() tea.Msg { + createBranchResult, err := d.CreateBranch(params) + if err != nil { + return errors.Trace(err) + } + newBranchID := *createBranchResult.GetPayload().ID + + ticker := time.NewTicker(WaitInterval) + defer ticker.Stop() + timer := time.After(WaitTimeout) + for { + select { + case <-timer: + return ui.Result("Timeout waiting for branch to be ready, please check status on dashboard.") + case <-ticker.C: + clusterResult, err := d.GetBranch(branchApi.NewGetBranchParams(). + WithClusterID(params.ClusterID). + WithBranchID(newBranchID)) + if err != nil { + return errors.Trace(err) + } + s := clusterResult.GetPayload().State + if *s == branchModel.OpenapiBranchStateREADY { + return ui.Result(fmt.Sprintf("Branch %s is ready.", newBranchID)) + } + case <-ctx.Done(): + return util.InterruptError + } + } + } + + p := tea.NewProgram(ui.InitialSpinnerModel(task, "Waiting for branch to be ready")) + createModel, err := p.StartReturningModel() + if err != nil { + return errors.Trace(err) + } + if m, _ := createModel.(ui.SpinnerModel); m.Interrupted { + return util.InterruptError + } + if m, _ := createModel.(ui.SpinnerModel); m.Err != nil { + return m.Err + } else { + fmt.Fprintln(h.IOStreams.Out, color.GreenString(m.Output)) + } + return nil +} + +func initialCreateBranchInputModel() ui.TextInputModel { + m := ui.TextInputModel{ + Inputs: make([]textinput.Model, len(createBranchField)), + } + + for k, v := range createBranchField { + t := textinput.New() + t.CursorStyle = config.CursorStyle + t.CharLimit = 64 + + switch k { + case flag.BranchName: + t.Placeholder = "Branch Name" + t.Focus() + t.PromptStyle = config.FocusedStyle + t.TextStyle = config.FocusedStyle + } + + m.Inputs[v] = t + } + + return m +} + +func GetCreateBranchInput() (tea.Model, error) { + p := tea.NewProgram(initialCreateBranchInputModel()) + inputModel, err := p.StartReturningModel() + if err != nil { + return nil, errors.Trace(err) + } + if inputModel.(ui.TextInputModel).Interrupted { + return nil, util.InterruptError + } + return inputModel, nil +} diff --git a/internal/cli/branch/create_test.go b/internal/cli/branch/create_test.go new file mode 100644 index 00000000..53d4ebd5 --- /dev/null +++ b/internal/cli/branch/create_test.go @@ -0,0 +1,129 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "testing" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/iostream" + "tidbcloud-cli/internal/mock" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type CreateBranchSuite struct { + suite.Suite + h *internal.Helper + mockClient *mock.TiDBCloudClient +} + +func (suite *CreateBranchSuite) SetupTest() { + if err := os.Setenv("NO_COLOR", "true"); err != nil { + suite.T().Error(err) + } + + var pageSize int64 = 10 + suite.mockClient = new(mock.TiDBCloudClient) + suite.h = &internal.Helper{ + Client: func() (cloud.TiDBCloudClient, error) { + return suite.mockClient, nil + }, + QueryPageSize: pageSize, + IOStreams: iostream.Test(), + } +} + +func (suite *CreateBranchSuite) TestCreateBranchArgs() { + assert := require.New(suite.T()) + + clusterID := "12345" + branchName := "test" + branchId := "12345" + + createBranchBody := branchApi.CreateBranchBody{ + DisplayName: &branchName, + } + suite.mockClient.On("CreateBranch", branchApi.NewCreateBranchParams(). + WithClusterID(clusterID).WithBody(createBranchBody)). + Return(&branchApi.CreateBranchOK{ + Payload: &branchModel.OpenapiCreateBranchResp{ + ID: &branchId, + }, + }, nil) + + body := &branchModel.OpenapiBranch{} + err := json.Unmarshal([]byte(getBranchResultStr), body) + assert.Nil(err) + result := &branchApi.GetBranchOK{ + Payload: body, + } + suite.mockClient.On("GetBranch", branchApi.NewGetBranchParams(). + WithClusterID(clusterID).WithBranchID(branchId)). + Return(result, nil) + + tests := []struct { + name string + args []string + err error + stdoutString string + stderrString string + }{ + { + name: "create branch success", + args: []string{"--cluster-id", clusterID, "--branch-name", branchName}, + stdoutString: fmt.Sprintf("... Waiting for branch to be ready\nBranch %s is ready.", branchId), + }, + { + name: "create branch with shorthand flag", + args: []string{"-c", clusterID, "--branch-name", branchName}, + stdoutString: fmt.Sprintf("... Waiting for branch to be ready\nBranch %s is ready.", branchId), + }, + { + name: "without required project id", + args: []string{"--branch-name", branchName}, + err: fmt.Errorf("required flag(s) \"cluster-id\" not set"), + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cmd := CreateCmd(suite.h) + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err := cmd.Execute() + assert.Equal(tt.err, err) + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + if tt.err == nil { + suite.mockClient.AssertExpectations(suite.T()) + } + }) + } +} + +func TestCreateBranchSuite(t *testing.T) { + suite.Run(t, new(CreateBranchSuite)) +} diff --git a/internal/cli/branch/delete.go b/internal/cli/branch/delete.go new file mode 100644 index 00000000..2212dc78 --- /dev/null +++ b/internal/cli/branch/delete.go @@ -0,0 +1,182 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "fmt" + "strings" + "time" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/flag" + "tidbcloud-cli/internal/service/cloud" + "tidbcloud-cli/internal/util" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + + "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" + "github.com/fatih/color" + "github.com/juju/errors" + "github.com/spf13/cobra" +) + +const confirmed = "yes" + +type DeleteOpts struct { + interactive bool +} + +func (c DeleteOpts) NonInteractiveFlags() []string { + return []string{ + flag.ClusterID, + flag.BranchID, + } +} + +func DeleteCmd(h *internal.Helper) *cobra.Command { + opts := DeleteOpts{ + interactive: true, + } + + var force bool + var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete a branch in a specified serverless cluster", + Args: cobra.NoArgs, + Example: fmt.Sprintf(` Delete a branch in interactive mode: + $ %[1]s branch delete + + Delete a branch in non-interactive mode: + $ %[1]s branch delete -c -b `, config.CliName), + Aliases: []string{"rm"}, + PreRunE: func(cmd *cobra.Command, args []string) error { + flags := opts.NonInteractiveFlags() + for _, fn := range flags { + f := cmd.Flags().Lookup(fn) + if f != nil && f.Changed { + opts.interactive = false + } + } + + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + d, err := h.Client() + if err != nil { + return err + } + + var clusterID string + var branchID string + if opts.interactive { + if !h.IOStreams.CanPrompt { + return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") + } + + // interactive mode + project, err := cloud.GetSelectedProject(h.QueryPageSize, d) + if err != nil { + return err + } + cluster, err := cloud.GetSelectedCluster(project.ID, h.QueryPageSize, d) + if err != nil { + return err + } + clusterID = cluster.ID + + branch, err := cloud.GetSelectedBranch(clusterID, h.QueryPageSize, d) + if err != nil { + return err + } + branchID = branch.ID + } else { + // non-interactive mode, get values from flags + bID, err := cmd.Flags().GetString(flag.BranchID) + if err != nil { + return errors.Trace(err) + } + + cID, err := cmd.Flags().GetString(flag.ClusterID) + if err != nil { + return errors.Trace(err) + } + branchID = bID + clusterID = cID + } + + if !force { + if !h.IOStreams.CanPrompt { + return fmt.Errorf("the terminal doesn't support prompt, please run with --force to delete the branch") + } + + confirmationMessage := fmt.Sprintf("%s %s %s", color.BlueString("Please type"), color.HiBlueString(confirmed), color.BlueString("to confirm:")) + + prompt := &survey.Input{ + Message: confirmationMessage, + } + + var userInput string + err := survey.AskOne(prompt, &userInput) + if err != nil { + if err == terminal.InterruptErr { + return util.InterruptError + } else { + return err + } + } + + if userInput != confirmed { + return errors.New("incorrect confirm string entered, skipping branch deletion") + } + } + + params := branchApi.NewDeleteBranchParams(). + WithClusterID(clusterID).WithBranchID(branchID) + _, err = d.DeleteBranch(params) + if err != nil { + return errors.Trace(err) + } + + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + timer := time.After(2 * time.Minute) + for { + select { + case <-timer: + return errors.New("timeout waiting for deleting branch, please check status on dashboard") + case <-ticker.C: + _, err := d.GetBranch(branchApi.NewGetBranchParams(). + WithClusterID(clusterID). + WithBranchID(branchID)) + if err != nil { + if strings.Contains(err.Error(), "404") { + fmt.Fprintln(h.IOStreams.Out, color.GreenString("branch %s deleted", branchID)) + return nil + } + return errors.Trace(err) + } + } + } + }, + } + + deleteCmd.Flags().BoolVar(&force, flag.Force, false, "Delete a cluster without confirmation") + deleteCmd.Flags().StringP(flag.BranchID, flag.BranchIDShort, "", "The ID of the branch to be deleted") + deleteCmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The cluster ID of the branch to be deleted") + deleteCmd.MarkFlagsRequiredTogether(flag.BranchID, flag.ClusterID) + + return deleteCmd +} diff --git a/internal/cli/branch/delete_test.go b/internal/cli/branch/delete_test.go new file mode 100644 index 00000000..f34185e9 --- /dev/null +++ b/internal/cli/branch/delete_test.go @@ -0,0 +1,117 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "bytes" + "errors" + "fmt" + "os" + "testing" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/iostream" + "tidbcloud-cli/internal/mock" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type DeleteBranchSuite struct { + suite.Suite + h *internal.Helper + mockClient *mock.TiDBCloudClient +} + +func (suite *DeleteBranchSuite) SetupTest() { + if err := os.Setenv("NO_COLOR", "true"); err != nil { + suite.T().Error(err) + } + + var pageSize int64 = 10 + suite.mockClient = new(mock.TiDBCloudClient) + suite.h = &internal.Helper{ + Client: func() (cloud.TiDBCloudClient, error) { + return suite.mockClient, nil + }, + QueryPageSize: pageSize, + IOStreams: iostream.Test(), + } +} + +func (suite *DeleteBranchSuite) TestDeleteBranchArgs() { + assert := require.New(suite.T()) + + clusterID := "12345" + branchID := "12345" + suite.mockClient.On("DeleteBranch", branchApi.NewDeleteBranchParams(). + WithBranchID(branchID).WithClusterID(clusterID)). + Return(&branchApi.DeleteBranchOK{}, nil) + suite.mockClient.On("GetBranch", branchApi.NewGetBranchParams(). + WithBranchID(branchID).WithClusterID(clusterID)). + Return(nil, errors.New("404")) + + tests := []struct { + name string + args []string + err error + stdoutString string + stderrString string + }{ + { + name: "delete branch success", + args: []string{"--cluster-id", clusterID, "--branch-id", branchID, "--force"}, + stdoutString: fmt.Sprintf("branch %s deleted\n", branchID), + }, + { + name: "delete branch without force", + args: []string{"--cluster-id", clusterID, "--branch-id", branchID}, + err: fmt.Errorf("the terminal doesn't support prompt, please run with --force to delete the branch"), + }, + { + name: "delete branch with simple flag", + args: []string{"-c", clusterID, "-b", branchID, "--force"}, + stdoutString: fmt.Sprintf("branch %s deleted\n", branchID), + }, + { + name: "delete branch without required branch id", + args: []string{"-c", clusterID, "--force"}, + err: fmt.Errorf("if any flags in the group [branch-id cluster-id] are set they must all be set; missing [branch-id]"), + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cmd := DeleteCmd(suite.h) + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err := cmd.Execute() + assert.Equal(tt.err, err) + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + if tt.err == nil { + suite.mockClient.AssertExpectations(suite.T()) + } + }) + } +} + +func TestDeleteBranchSuite(t *testing.T) { + suite.Run(t, new(DeleteBranchSuite)) +} diff --git a/internal/cli/branch/describe.go b/internal/cli/branch/describe.go new file mode 100644 index 00000000..9f65e802 --- /dev/null +++ b/internal/cli/branch/describe.go @@ -0,0 +1,134 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "encoding/json" + "fmt" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/flag" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + + "github.com/juju/errors" + "github.com/spf13/cobra" +) + +type DescribeOpts struct { + interactive bool +} + +func (c DescribeOpts) NonInteractiveFlags() []string { + return []string{ + flag.ClusterID, + flag.BranchID, + } +} + +func DescribeCmd(h *internal.Helper) *cobra.Command { + opts := DescribeOpts{ + interactive: true, + } + + var describeCmd = &cobra.Command{ + Use: "describe", + Short: "Describe a branch in a specified serverless cluster", + Aliases: []string{"get"}, + Args: cobra.NoArgs, + Example: fmt.Sprintf(` Get the branch info in interactive mode: + $ %[1]s branch describe + + Get the branch info in non-interactive mode: + $ %[1]s branch describe -c -b `, config.CliName), + PreRunE: func(cmd *cobra.Command, args []string) error { + flags := opts.NonInteractiveFlags() + for _, fn := range flags { + f := cmd.Flags().Lookup(fn) + if f != nil && f.Changed { + opts.interactive = false + } + } + if len(args) > 0 { + opts.interactive = false + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + d, err := h.Client() + if err != nil { + return err + } + + var branchID string + var clusterID string + if opts.interactive { + if !h.IOStreams.CanPrompt { + return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") + } + + // interactive mode + project, err := cloud.GetSelectedProject(h.QueryPageSize, d) + if err != nil { + return err + } + cluster, err := cloud.GetSelectedCluster(project.ID, h.QueryPageSize, d) + if err != nil { + return err + } + clusterID = cluster.ID + + branch, err := cloud.GetSelectedBranch(clusterID, h.QueryPageSize, d) + if err != nil { + return err + } + branchID = branch.ID + } else { + // non-interactive mode, get values from flags + bID, err := cmd.Flags().GetString(flag.BranchID) + if err != nil { + return errors.Trace(err) + } + + cID, err := cmd.Flags().GetString(flag.ClusterID) + if err != nil { + return errors.Trace(err) + } + branchID = bID + clusterID = cID + } + params := branchApi.NewGetBranchParams(). + WithClusterID(clusterID).WithBranchID(branchID) + branch, err := d.GetBranch(params) + if err != nil { + return errors.Trace(err) + } + + v, err := json.MarshalIndent(branch.Payload, "", " ") + if err != nil { + return errors.Trace(err) + } + + fmt.Fprintln(h.IOStreams.Out, string(v)) + return nil + }, + } + + describeCmd.Flags().StringP(flag.BranchID, flag.BranchIDShort, "", "The ID of the branch to be deleted") + describeCmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The cluster ID of the branch to be deleted") + describeCmd.MarkFlagsRequiredTogether(flag.BranchID, flag.ClusterID) + return describeCmd +} diff --git a/internal/cli/branch/describe_test.go b/internal/cli/branch/describe_test.go new file mode 100644 index 00000000..8a68abed --- /dev/null +++ b/internal/cli/branch/describe_test.go @@ -0,0 +1,142 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "testing" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/iostream" + "tidbcloud-cli/internal/mock" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +const getBranchResultStr = `{ + "annotations": {}, + "cluster_id": "3478958", + "create_time": "2023-06-05T05:05:33.000Z", + "delete_time": null, + "display_name": "ru-test", + "endpoints": { + "public_endpoint": { + "host": "gateway01.us-east-1.dev.shared.aws.tidbcloud.com", + "port": 4000 + } + }, + "id": "branch-sm4ee5usauqfgsftywrapd", + "name": "clusters/3478958/branches/branch-sm4ee5usauqfgsftywrapd", + "parent_id": "3478958", + "state": "READY", + "update_time": "2023-06-05T05:06:41.000Z", + "usages": { + "column_storage": "0", + "request_unit": "1300000", + "row_storage": "261260139" + }, + "user_prefix": "49dDUPpoxGXdsY9" +} +` + +type DescribeBranchSuite struct { + suite.Suite + h *internal.Helper + mockClient *mock.TiDBCloudClient +} + +func (suite *DescribeBranchSuite) SetupTest() { + if err := os.Setenv("NO_COLOR", "true"); err != nil { + suite.T().Error(err) + } + + var pageSize int64 = 10 + suite.mockClient = new(mock.TiDBCloudClient) + suite.h = &internal.Helper{ + Client: func() (cloud.TiDBCloudClient, error) { + return suite.mockClient, nil + }, + QueryPageSize: pageSize, + IOStreams: iostream.Test(), + } +} + +func (suite *DescribeBranchSuite) TestDescribeBranchArgs() { + assert := require.New(suite.T()) + + body := &branchModel.OpenapiBranch{} + err := json.Unmarshal([]byte(getBranchResultStr), body) + assert.Nil(err) + result := &branchApi.GetBranchOK{ + Payload: body, + } + clusterID := "12345" + branchID := "12345" + suite.mockClient.On("GetBranch", branchApi.NewGetBranchParams(). + WithBranchID(branchID).WithClusterID(clusterID)). + Return(result, nil) + + tests := []struct { + name string + args []string + err error + stdoutString string + stderrString string + }{ + { + name: "describe branch success", + args: []string{"--cluster-id", clusterID, "--branch-id", branchID}, + stdoutString: getBranchResultStr, + }, + { + name: "describe branch with shorthand flag", + args: []string{"-c", clusterID, "-b", branchID}, + stdoutString: getBranchResultStr, + }, + { + name: "describe branch without required cluster id", + args: []string{"-b", branchID}, + err: fmt.Errorf("if any flags in the group [branch-id cluster-id] are set they must all be set; missing [cluster-id]"), + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cmd := DescribeCmd(suite.h) + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err = cmd.Execute() + assert.Equal(tt.err, err) + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + if tt.err == nil { + suite.mockClient.AssertExpectations(suite.T()) + } + }) + } +} + +func TestDescribeBranchSuite(t *testing.T) { + suite.Run(t, new(DescribeBranchSuite)) +} diff --git a/internal/cli/branch/list.go b/internal/cli/branch/list.go new file mode 100644 index 00000000..227152b5 --- /dev/null +++ b/internal/cli/branch/list.go @@ -0,0 +1,138 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "fmt" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/config" + "tidbcloud-cli/internal/flag" + "tidbcloud-cli/internal/output" + "tidbcloud-cli/internal/service/cloud" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + + "github.com/juju/errors" + "github.com/spf13/cobra" +) + +type ListOpts struct { + interactive bool +} + +func ListCmd(h *internal.Helper) *cobra.Command { + opts := ListOpts{ + interactive: true, + } + + var listCmd = &cobra.Command{ + Use: "list ", + Short: "List branches in a specified serverless cluster", + Example: fmt.Sprintf(` List all branches(interactive mode): + $ %[1]s branch list + + List the branches(non-interactive mode): + $ %[1]s branch list + + List the branches with json format: + $ %[1]s branch list -o json`, config.CliName), + Aliases: []string{"ls"}, + Args: cobra.MaximumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) > 0 { + opts.interactive = false + } + }, + RunE: func(cmd *cobra.Command, args []string) error { + d, err := h.Client() + if err != nil { + return err + } + + var clusterID string + if opts.interactive { + if !h.IOStreams.CanPrompt { + return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode") + } + + project, err := cloud.GetSelectedProject(h.QueryPageSize, d) + if err != nil { + return err + } + cluster, err := cloud.GetSelectedCluster(project.ID, h.QueryPageSize, d) + if err != nil { + return err + } + clusterID = cluster.ID + } else { + clusterID = args[0] + } + + total, items, err := cloud.RetrieveBranches(clusterID, h.QueryPageSize, d) + if err != nil { + return err + } + + format, err := cmd.Flags().GetString(flag.Output) + if err != nil { + return errors.Trace(err) + } + + // for terminal which can prompt, humanFormat is the default format. + // for other terminals, json format is the default format. + if format == output.JsonFormat || !h.IOStreams.CanPrompt { + res := &branchModel.OpenapiListBranchesResp{ + Branches: items, + Total: &total, + } + err := output.PrintJson(h.IOStreams.Out, res) + if err != nil { + return errors.Trace(err) + } + } else if format == output.HumanFormat { + columns := []output.Column{ + "ID", + "DisplayName", + "State", + "CreateTime", + "UpdateTime", + } + + var rows []output.Row + for _, item := range items { + rows = append(rows, output.Row{ + *item.ID, + *item.DisplayName, + string(*item.State), + item.CreateTime.String(), + item.UpdateTime.String(), + }) + } + + err := output.PrintHumanTable(h.IOStreams.Out, columns, rows) + if err != nil { + return errors.Trace(err) + } + return nil + } else { + return fmt.Errorf("unsupported output format: %s", format) + } + return nil + }, + } + + listCmd.Flags().StringP(flag.Output, flag.OutputShort, output.HumanFormat, "Output format. One of: human, json. For the complete result, please use json format.") + return listCmd +} diff --git a/internal/cli/branch/list_test.go b/internal/cli/branch/list_test.go new file mode 100644 index 00000000..7f6604de --- /dev/null +++ b/internal/cli/branch/list_test.go @@ -0,0 +1,212 @@ +// Copyright 2023 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package branch + +import ( + "bytes" + "encoding/json" + "os" + "strings" + "testing" + + "tidbcloud-cli/internal" + "tidbcloud-cli/internal/iostream" + "tidbcloud-cli/internal/mock" + "tidbcloud-cli/internal/service/cloud" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +const listResultStr = `{ + "branches": [ + { + "cluster_id": "3478958", + "create_time": "2023-06-05T05:05:33.000Z", + "delete_time": null, + "display_name": "ru-test", + "id": "branch-sm4ee5usauqfgsftywrapd", + "name": "clusters/3478958/branches/branch-sm4ee5usauqfgsftywrapd", + "parent_id": "3478958", + "state": "READY", + "update_time": "2023-06-05T05:06:41.000Z" + } + ], + "total": 1 +} +` + +const listResultMultiPageStr = `{ + "branches": [ + { + "cluster_id": "3478958", + "create_time": "2023-06-05T05:05:33.000Z", + "delete_time": null, + "display_name": "ru-test", + "id": "branch-sm4ee5usauqfgsftywrapd", + "name": "clusters/3478958/branches/branch-sm4ee5usauqfgsftywrapd", + "parent_id": "3478958", + "state": "READY", + "update_time": "2023-06-05T05:06:41.000Z" + }, + { + "cluster_id": "3478958", + "create_time": "2023-06-05T05:05:33.000Z", + "delete_time": null, + "display_name": "ru-test", + "id": "branch-sm4ee5usauqfgsftywrapd", + "name": "clusters/3478958/branches/branch-sm4ee5usauqfgsftywrapd", + "parent_id": "3478958", + "state": "READY", + "update_time": "2023-06-05T05:06:41.000Z" + } + ], + "total": 2 +} +` + +type ListBranchSuite struct { + suite.Suite + h *internal.Helper + mockClient *mock.TiDBCloudClient +} + +func (suite *ListBranchSuite) SetupTest() { + if err := os.Setenv("NO_COLOR", "true"); err != nil { + suite.T().Error(err) + } + + var pageSize int64 = 10 + suite.mockClient = new(mock.TiDBCloudClient) + suite.h = &internal.Helper{ + Client: func() (cloud.TiDBCloudClient, error) { + return suite.mockClient, nil + }, + QueryPageSize: pageSize, + IOStreams: iostream.Test(), + } +} + +func (suite *ListBranchSuite) TestListBranchesArgs() { + assert := require.New(suite.T()) + var page int64 = 1 + + body := &branchModel.OpenapiListBranchesResp{} + err := json.Unmarshal([]byte(listResultStr), body) + assert.Nil(err) + result := &branchApi.ListBranchesOK{ + Payload: body, + } + clusterID := "12345" + suite.mockClient.On("ListBranches", branchApi.NewListBranchesParams(). + WithClusterID(clusterID).WithPageToken(&page).WithPageSize(&suite.h.QueryPageSize)). + Return(result, nil) + + tests := []struct { + name string + args []string + err error + stdoutString string + stderrString string + }{ + { + name: "list branches with default format(json when without tty)", + args: []string{clusterID}, + stdoutString: listResultStr, + }, + { + name: "list branches with output flag", + args: []string{clusterID, "--output", "json"}, + stdoutString: listResultStr, + }, + { + name: "list branches with output shorthand flag", + args: []string{clusterID, "-o", "json"}, + stdoutString: listResultStr, + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + cmd := ListCmd(suite.h) + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err = cmd.Execute() + assert.Equal(tt.err, err) + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + if tt.err == nil { + suite.mockClient.AssertExpectations(suite.T()) + } + }) + } +} + +func (suite *ListBranchSuite) TestListBranchesWithMultiPages() { + assert := require.New(suite.T()) + var pageOne int64 = 1 + var pageTwo int64 = 2 + suite.h.QueryPageSize = 1 + + body := &branchModel.OpenapiListBranchesResp{} + err := json.Unmarshal([]byte(strings.ReplaceAll(listResultStr, `"total": 1`, `"total": 2`)), body) + assert.Nil(err) + result := &branchApi.ListBranchesOK{ + Payload: body, + } + clusterID := "12345" + suite.mockClient.On("ListBranches", branchApi.NewListBranchesParams(). + WithClusterID(clusterID).WithPageToken(&pageOne).WithPageSize(&suite.h.QueryPageSize)). + Return(result, nil) + suite.mockClient.On("ListBranches", branchApi.NewListBranchesParams(). + WithClusterID(clusterID).WithPageToken(&pageTwo).WithPageSize(&suite.h.QueryPageSize)). + Return(result, nil) + cmd := ListCmd(suite.h) + + tests := []struct { + name string + args []string + stdoutString string + stderrString string + }{ + { + name: "query with multi pages", + args: []string{clusterID, "--output", "json"}, + stdoutString: listResultMultiPageStr, + }, + } + + for _, tt := range tests { + suite.T().Run(tt.name, func(t *testing.T) { + suite.h.IOStreams.Out.(*bytes.Buffer).Reset() + suite.h.IOStreams.Err.(*bytes.Buffer).Reset() + cmd.SetArgs(tt.args) + err = cmd.Execute() + assert.Nil(err) + + assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String()) + assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String()) + suite.mockClient.AssertExpectations(suite.T()) + }) + } +} + +func TestListBranchSuite(t *testing.T) { + suite.Run(t, new(ListBranchSuite)) +} diff --git a/internal/cli/cluster/connectinfo.go b/internal/cli/cluster/connectinfo.go index 3fb30432..c84eee32 100644 --- a/internal/cli/cluster/connectinfo.go +++ b/internal/cli/cluster/connectinfo.go @@ -46,7 +46,7 @@ func (c connectInfoOpts) NonInteractiveFlags() []string { } // Display clients name orderly in interactive mode -var connectClientsList = []string{ +var ConnectClientsList = []string{ // pure parameter util.GeneralParameterDisplayName, @@ -79,7 +79,7 @@ var connectClientsList = []string{ } // Display clients name orderly in help message -var connectClientsListForHelp = []string{ +var ConnectClientsListForHelp = []string{ // pure parameter util.GeneralParameterInputName, @@ -111,7 +111,7 @@ var connectClientsListForHelp = []string{ util.ActiveRecordInputName, } -var clientsForInteractiveMap = map[string]string{ +var ClientsForInteractiveMap = map[string]string{ // pure parameter util.GeneralParameterDisplayName: util.GeneralParameterID, @@ -143,7 +143,7 @@ var clientsForInteractiveMap = map[string]string{ util.ActiveRecordDisplayName: util.ActiveRecordID, } -var clientsForHelpMap = map[string]string{ +var ClientsForHelpMap = map[string]string{ // pure parameter util.GeneralParameterInputName: util.GeneralParameterID, @@ -176,7 +176,7 @@ var clientsForHelpMap = map[string]string{ } // Display operating system orderly in interactive mode -var operatingSystemList = []string{ +var OperatingSystemList = []string{ "macOS/Alpine", "CentOS/RedHat/Fedora", "Debian/Ubuntu/Arch", @@ -186,7 +186,7 @@ var operatingSystemList = []string{ } // Display operating system orderly in help message -var operatingSystemListForHelp = []string{ +var OperatingSystemListForHelp = []string{ "macOS", "Windows", "Ubuntu", @@ -271,11 +271,11 @@ func ConnectInfoCmd(h *internal.Helper) *cobra.Command { clusterID = cluster.ID // Get client - clientNameForInteractive, err := cloud.GetSelectedConnectClient(connectClientsList) + clientNameForInteractive, err := cloud.GetSelectedConnectClient(ConnectClientsList) if err != nil { return err } - client = clientsForInteractiveMap[clientNameForInteractive] + client = ClientsForInteractiveMap[clientNameForInteractive] // Detect operating system // TODO: detect linux operating system name @@ -286,17 +286,18 @@ func ConnectInfoCmd(h *internal.Helper) *cobra.Command { goOS = "Windows" } if goOS != "" && goOS != "linux" { - for id, value := range operatingSystemList { + for id, value := range OperatingSystemList { if strings.Contains(value, goOS) { - operatingSystemValueWithFlag := operatingSystemList[id] + " (Detected)" - operatingSystemList = append([]string{operatingSystemValueWithFlag}, append(operatingSystemList[:id], operatingSystemList[id+1:]...)...) + operatingSystemValueWithFlag := OperatingSystemList[id] + " (Detected)" + OperatingSystemList = append([]string{operatingSystemValueWithFlag}, + append(OperatingSystemList[:id], OperatingSystemList[id+1:]...)...) break } } } // Get operating system - operatingSystemCombination, err := cloud.GetSelectedConnectOs(operatingSystemList) + operatingSystemCombination, err := cloud.GetSelectedConnectOs(OperatingSystemList) if err != nil { return err } @@ -317,7 +318,7 @@ func ConnectInfoCmd(h *internal.Helper) *cobra.Command { if err != nil { return err } - if v, ok := clientsForHelpMap[strings.ToLower(clientNameForHelp)]; ok { + if v, ok := ClientsForHelpMap[strings.ToLower(clientNameForHelp)]; ok { client = v } else { return errors.New(fmt.Sprintf("Unsupported client. Run \"%[1]s cluster connect-info -h\" to check supported clients list", config.CliName)) @@ -327,7 +328,7 @@ func ConnectInfoCmd(h *internal.Helper) *cobra.Command { if err != nil { return err } - if !contains(operatingSystem, operatingSystemListForHelp) { + if !Contains(operatingSystem, OperatingSystemListForHelp) { return errors.New(fmt.Sprintf("Unsupported operating system. Run \"%[1]s cluster connect-info -h\" to check supported operating systems list", config.CliName)) } } @@ -367,13 +368,14 @@ func ConnectInfoCmd(h *internal.Helper) *cobra.Command { cmd.Flags().StringP(flag.ProjectID, flag.ProjectIDShort, "", "Project ID") cmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "Cluster ID") - cmd.Flags().String(flag.ClientName, "", fmt.Sprintf("Connected client. Supported clients: %q", connectClientsListForHelp)) - cmd.Flags().String(flag.OperatingSystem, "", fmt.Sprintf("Operating system name. Supported operating systems: %q", operatingSystemListForHelp)) + cmd.Flags().String(flag.ClientName, "", fmt.Sprintf("Connected client. Supported clients: %q", ConnectClientsListForHelp)) + cmd.Flags().String(flag.OperatingSystem, "", fmt.Sprintf("Operating system name. "+ + "Supported operating systems: %q", OperatingSystemListForHelp)) return cmd } -func contains(str string, vec []string) bool { +func Contains(str string, vec []string) bool { for _, v := range vec { if strings.EqualFold(str, v) { return true diff --git a/internal/cli/connect/connect.go b/internal/cli/connect/connect.go index 3c591cd8..55eff181 100644 --- a/internal/cli/connect/connect.go +++ b/internal/cli/connect/connect.go @@ -22,12 +22,14 @@ import ( "os" "os/user" "strconv" + "strings" "tidbcloud-cli/internal" "tidbcloud-cli/internal/config" "tidbcloud-cli/internal/flag" "tidbcloud-cli/internal/service/cloud" "tidbcloud-cli/internal/util" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" @@ -66,20 +68,24 @@ func ConnectCmd(h *internal.Helper) *cobra.Command { var connectCmd = &cobra.Command{ Use: "connect", - Short: "Connect to a TiDB Cloud cluster", - Long: `Connect to a TiDB Cloud cluster; + Short: "Connect to TiDB Cloud", + Long: `Connect to TiDB Cloud; the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_ansi) for the session.`, - Example: fmt.Sprintf(` Connect to the TiDB Cloud cluster in interactive mode: + Example: fmt.Sprintf(` Connect to the TiDB Cloud in interactive mode: $ %[1]s connect Use the default user to connect to the TiDB Cloud cluster in non-interactive mode: $ %[1]s connect -p -c + Use the default user to connect to the TiDB Cloud branch in non-interactive mode: + $ %[1]s connect -p -c -b + Use the default user to connect to the TiDB Cloud cluster with password in non-interactive mode: - $ %[1]s connect -p -c --password + $ %[1]s connect -p -c --password Use a specific user to connect to the TiDB Cloud cluster in non-interactive mode: $ %[1]s connect -p -c -u `, config.CliName), + PreRunE: func(cmd *cobra.Command, args []string) error { flags := opts.NonInteractiveFlags() for _, fn := range flags { @@ -112,7 +118,7 @@ the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/e return err } - var projectID, clusterID, userName string + var projectID, clusterID, branchID, userName string var pass *string if opts.interactive { // interactive mode @@ -128,6 +134,14 @@ the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/e } clusterID = cluster.ID + branch, err := cloud.GetSelectedBranchIfExist(clusterID, h.QueryPageSize, d) + if err != nil { + return err + } + if branch != nil { + branchID = branch.ID + } + useDefaultUser := false prompt := &survey.Confirm{ Message: "Use the default user?", @@ -168,10 +182,17 @@ the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/e if err != nil { return errors.Trace(err) } + projectID = pID clusterID = cID // options flags + bID, err := cmd.Flags().GetString(flag.BranchID) + if err != nil { + return errors.Trace(err) + } + branchID = bID + uName, err := cmd.Flags().GetString(flag.User) if err != nil { return errors.Trace(err) @@ -187,28 +208,47 @@ the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/e } } - params := clusterApi.NewGetClusterParams(). - WithProjectID(projectID). - WithClusterID(clusterID) - cluster, err := d.GetCluster(params) - if err != nil { - return errors.Trace(err) - } - defaultUser := cluster.Payload.Status.ConnectionStrings.DefaultUser - host := cluster.Payload.Status.ConnectionStrings.Standard.Host - clusterName := cluster.Payload.Name - port := strconv.Itoa(int(cluster.Payload.Status.ConnectionStrings.Standard.Port)) - clusterType := cluster.Payload.ClusterType - if userName == "" { - userName = defaultUser - fmt.Fprintln(h.IOStreams.Out, color.GreenString("Current user: ")+color.HiGreenString(userName)) - } - if clusterType == DEVELOPER { + var host, name, port, clusterType string + if !isBranch(branchID) { + // cluster + params := clusterApi.NewGetClusterParams(). + WithProjectID(projectID). + WithClusterID(clusterID) + cluster, err := d.GetCluster(params) + if err != nil { + return errors.Trace(err) + } + defaultUser := cluster.Payload.Status.ConnectionStrings.DefaultUser + host = cluster.Payload.Status.ConnectionStrings.Standard.Host + name = cluster.Payload.Name + port = strconv.Itoa(int(cluster.Payload.Status.ConnectionStrings.Standard.Port)) + clusterType = cluster.Payload.ClusterType + if userName == "" { + userName = defaultUser + fmt.Fprintln(h.IOStreams.Out, color.GreenString("Current user: ")+color.HiGreenString(userName)) + } + if clusterType == DEVELOPER { + clusterType = SERVERLESS + } + } else { + // branch + params := branchApi.NewGetBranchParams().WithClusterID(clusterID).WithBranchID(branchID) + branchInfo, err := d.GetBranch(params) + if err != nil { + return errors.Trace(err) + } + host = branchInfo.Payload.Endpoints.PublicEndpoint.Host + port = strconv.Itoa(int(branchInfo.Payload.Endpoints.PublicEndpoint.Port)) + name = *branchInfo.Payload.DisplayName + if userName == "" { + userName = fmt.Sprintf("%s.root", *branchInfo.Payload.UserPrefix) + fmt.Fprintln(h.IOStreams.Out, color.GreenString("Current user: ")+color.HiGreenString(userName)) + } clusterType = SERVERLESS } // Set prompt style, see https://github.com/xo/usql/commit/d5db12eaa6fe48cd0a697831ad03d61611290576 - err = env.Set("PROMPT1", "%n"+"@"+clusterName+"%/%R%#") + err = env.Set("PROMPT1", "%n"+"@"+name+"%/%R%#") if err != nil { return err } @@ -217,12 +257,14 @@ the connection forces the [ANSI SQL mode](https://dev.mysql.com/doc/refman/8.0/e if err != nil { return err } + return nil }, } connectCmd.Flags().StringP(flag.ProjectID, flag.ProjectIDShort, "", "The project ID of the cluster") connectCmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The ID of the cluster") + connectCmd.Flags().StringP(flag.BranchID, flag.BranchIDShort, "", "The ID of the branch") connectCmd.Flags().String(flag.Password, "", "The password of the user") connectCmd.Flags().StringP(flag.User, flag.UserShort, "", "A specific user for login if not using the default user") return connectCmd @@ -309,3 +351,7 @@ func generateDsnWithoutPassword(clusterType string, userName string, host string } return dsn, nil } + +func isBranch(branchID string) bool { + return branchID != "" && strings.Contains(branchID, "bran-") +} diff --git a/internal/cli/root.go b/internal/cli/root.go index aeabad75..3b5c56ef 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -19,6 +19,7 @@ import ( "fmt" "os" "strings" + "tidbcloud-cli/internal/cli/branch" "tidbcloud-cli/internal" "tidbcloud-cli/internal/cli/cluster" @@ -166,6 +167,7 @@ func RootCmd(h *internal.Helper) *cobra.Command { rootCmd.AddCommand(update.UpdateCmd(h)) rootCmd.AddCommand(dataimport.ImportCmd(h)) rootCmd.AddCommand(connect.ConnectCmd(h)) + rootCmd.AddCommand(branch.BranchCmd(h)) rootCmd.PersistentFlags().BoolVarP(&debugMode, flag.Debug, flag.DebugShort, false, "Enable debug mode") rootCmd.PersistentFlags().Bool(flag.NoColor, false, "Disable color output") diff --git a/internal/flag/flag.go b/internal/flag/flag.go index 4db9fe19..03009842 100644 --- a/internal/flag/flag.go +++ b/internal/flag/flag.go @@ -23,6 +23,10 @@ const ( ClusterIDShort string = "c" ClusterName string = "cluster-name" ClusterType string = "cluster-type" + BranchID string = "branch-id" + BranchIDShort string = "b" + BranchName string = "branch-name" + Database string = "database" DataFormat string = "data-format" Debug string = "debug" DebugShort string = "D" diff --git a/internal/mock/api_client.go b/internal/mock/api_client.go index 789a00a9..48181742 100644 --- a/internal/mock/api_client.go +++ b/internal/mock/api_client.go @@ -1,13 +1,16 @@ -// Code generated by mockery v2.27.1. DO NOT EDIT. +// Code generated by mockery v2.28.2. DO NOT EDIT. package mock import ( - connect_info_service "tidbcloud-cli/pkg/tidbcloud/connect_info/client/connect_info_service" - import_service "tidbcloud-cli/pkg/tidbcloud/import/client/import_service" + branch_service "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" cluster "github.com/c4pt0r/go-tidbcloud-sdk-v1/client/cluster" + connect_info_service "tidbcloud-cli/pkg/tidbcloud/connect_info/client/connect_info_service" + + import_service "tidbcloud-cli/pkg/tidbcloud/import/client/import_service" + mock "github.com/stretchr/testify/mock" os "os" @@ -53,6 +56,39 @@ func (_m *TiDBCloudClient) CancelImport(params *import_service.CancelImportParam return r0, r1 } +// CreateBranch provides a mock function with given fields: params, opts +func (_m *TiDBCloudClient) CreateBranch(params *branch_service.CreateBranchParams, opts ...branch_service.ClientOption) (*branch_service.CreateBranchOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *branch_service.CreateBranchOK + var r1 error + if rf, ok := ret.Get(0).(func(*branch_service.CreateBranchParams, ...branch_service.ClientOption) (*branch_service.CreateBranchOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*branch_service.CreateBranchParams, ...branch_service.ClientOption) *branch_service.CreateBranchOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*branch_service.CreateBranchOK) + } + } + + if rf, ok := ret.Get(1).(func(*branch_service.CreateBranchParams, ...branch_service.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateCluster provides a mock function with given fields: params, opts func (_m *TiDBCloudClient) CreateCluster(params *cluster.CreateClusterParams, opts ...cluster.ClientOption) (*cluster.CreateClusterOK, error) { _va := make([]interface{}, len(opts)) @@ -119,6 +155,39 @@ func (_m *TiDBCloudClient) CreateImport(params *import_service.CreateImportParam return r0, r1 } +// DeleteBranch provides a mock function with given fields: params, opts +func (_m *TiDBCloudClient) DeleteBranch(params *branch_service.DeleteBranchParams, opts ...branch_service.ClientOption) (*branch_service.DeleteBranchOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *branch_service.DeleteBranchOK + var r1 error + if rf, ok := ret.Get(0).(func(*branch_service.DeleteBranchParams, ...branch_service.ClientOption) (*branch_service.DeleteBranchOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*branch_service.DeleteBranchParams, ...branch_service.ClientOption) *branch_service.DeleteBranchOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*branch_service.DeleteBranchOK) + } + } + + if rf, ok := ret.Get(1).(func(*branch_service.DeleteBranchParams, ...branch_service.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // DeleteCluster provides a mock function with given fields: params, opts func (_m *TiDBCloudClient) DeleteCluster(params *cluster.DeleteClusterParams, opts ...cluster.ClientOption) (*cluster.DeleteClusterOK, error) { _va := make([]interface{}, len(opts)) @@ -185,6 +254,39 @@ func (_m *TiDBCloudClient) GenerateUploadURL(params *import_service.GenerateUplo return r0, r1 } +// GetBranch provides a mock function with given fields: params, opts +func (_m *TiDBCloudClient) GetBranch(params *branch_service.GetBranchParams, opts ...branch_service.ClientOption) (*branch_service.GetBranchOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *branch_service.GetBranchOK + var r1 error + if rf, ok := ret.Get(0).(func(*branch_service.GetBranchParams, ...branch_service.ClientOption) (*branch_service.GetBranchOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*branch_service.GetBranchParams, ...branch_service.ClientOption) *branch_service.GetBranchOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*branch_service.GetBranchOK) + } + } + + if rf, ok := ret.Get(1).(func(*branch_service.GetBranchParams, ...branch_service.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetCluster provides a mock function with given fields: params, opts func (_m *TiDBCloudClient) GetCluster(params *cluster.GetClusterParams, opts ...cluster.ClientOption) (*cluster.GetClusterOK, error) { _va := make([]interface{}, len(opts)) @@ -284,6 +386,39 @@ func (_m *TiDBCloudClient) GetImport(params *import_service.GetImportParams, opt return r0, r1 } +// ListBranches provides a mock function with given fields: params, opts +func (_m *TiDBCloudClient) ListBranches(params *branch_service.ListBranchesParams, opts ...branch_service.ClientOption) (*branch_service.ListBranchesOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *branch_service.ListBranchesOK + var r1 error + if rf, ok := ret.Get(0).(func(*branch_service.ListBranchesParams, ...branch_service.ClientOption) (*branch_service.ListBranchesOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*branch_service.ListBranchesParams, ...branch_service.ClientOption) *branch_service.ListBranchesOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*branch_service.ListBranchesOK) + } + } + + if rf, ok := ret.Get(1).(func(*branch_service.ListBranchesParams, ...branch_service.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListClustersOfProject provides a mock function with given fields: params, opts func (_m *TiDBCloudClient) ListClustersOfProject(params *cluster.ListClustersOfProjectParams, opts ...cluster.ClientOption) (*cluster.ListClustersOfProjectOK, error) { _va := make([]interface{}, len(opts)) diff --git a/internal/mock/mysql_helper.go b/internal/mock/mysql_helper.go index c363d0eb..a5701d5e 100644 --- a/internal/mock/mysql_helper.go +++ b/internal/mock/mysql_helper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.27.1. DO NOT EDIT. +// Code generated by mockery v2.28.2. DO NOT EDIT. package mock diff --git a/internal/mock/sender.go b/internal/mock/sender.go index 8eb56004..454293e0 100644 --- a/internal/mock/sender.go +++ b/internal/mock/sender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.27.1. DO NOT EDIT. +// Code generated by mockery v2.28.2. DO NOT EDIT. package mock diff --git a/internal/service/cloud/api_client.go b/internal/service/cloud/api_client.go index 852e6957..6a510c7f 100644 --- a/internal/service/cloud/api_client.go +++ b/internal/service/cloud/api_client.go @@ -22,6 +22,8 @@ import ( "tidbcloud-cli/internal/config" "tidbcloud-cli/internal/prop" "tidbcloud-cli/internal/version" + branchClient "tidbcloud-cli/pkg/tidbcloud/branch/client" + branchOp "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" connectInfoClient "tidbcloud-cli/pkg/tidbcloud/connect_info/client" connectInfoOp "tidbcloud-cli/pkg/tidbcloud/connect_info/client/connect_info_service" importClient "tidbcloud-cli/pkg/tidbcloud/import/client" @@ -66,16 +68,25 @@ type TiDBCloudClient interface { PreSignedUrlUpload(url *string, uploadFile *os.File, size int64) error GetConnectInfo(params *connectInfoOp.GetInfoParams, opts ...connectInfoOp.ClientOption) (*connectInfoOp.GetInfoOK, error) + + GetBranch(params *branchOp.GetBranchParams, opts ...branchOp.ClientOption) (*branchOp.GetBranchOK, error) + + ListBranches(params *branchOp.ListBranchesParams, opts ...branchOp.ClientOption) (*branchOp.ListBranchesOK, error) + + CreateBranch(params *branchOp.CreateBranchParams, opts ...branchOp.ClientOption) (*branchOp.CreateBranchOK, error) + + DeleteBranch(params *branchOp.DeleteBranchParams, opts ...branchOp.ClientOption) (*branchOp.DeleteBranchOK, error) } type ClientDelegate struct { c *apiClient.GoTidbcloud ic *importClient.TidbcloudImport cc *connectInfoClient.TidbcloudConnectInfo + bc *branchClient.TidbcloudBranch } func NewClientDelegate(publicKey string, privateKey string, apiUrl string) (*ClientDelegate, error) { - c, ic, cc, err := NewApiClient(publicKey, privateKey, apiUrl) + c, ic, cc, bc, err := NewApiClient(publicKey, privateKey, apiUrl) if err != nil { return nil, err } @@ -83,6 +94,7 @@ func NewClientDelegate(publicKey string, privateKey string, apiUrl string) (*Cli c: c, ic: ic, cc: cc, + bc: bc, }, nil } @@ -154,7 +166,43 @@ func (d *ClientDelegate) GetConnectInfo(params *connectInfoOp.GetInfoParams, opt return d.cc.ConnectInfoService.GetInfo(params, opts...) } -func NewApiClient(publicKey string, privateKey string, apiUrl string) (*apiClient.GoTidbcloud, *importClient.TidbcloudImport, *connectInfoClient.TidbcloudConnectInfo, error) { +func (d *ClientDelegate) GetBranch(params *branchOp.GetBranchParams, opts ...branchOp.ClientOption) (*branchOp.GetBranchOK, error) { + r, err := d.bc.BranchService.GetBranch(params, opts...) + if err != nil { + errorPayload := err.(*branchOp.GetBranchDefault).Payload.Error + return nil, fmt.Errorf("[GET /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] GetBranch %+v", errorPayload.Code, errorPayload.Message) + } + return r, err +} + +func (d *ClientDelegate) ListBranches(params *branchOp.ListBranchesParams, opts ...branchOp.ClientOption) (*branchOp.ListBranchesOK, error) { + r, err := d.bc.BranchService.ListBranches(params, opts...) + if err != nil { + errorPayload := err.(*branchOp.ListBranchesDefault).Payload.Error + return nil, fmt.Errorf("[GET /api/v1beta/clusters/{cluster_id}/branches][%d] ListBranches %+v", errorPayload.Code, errorPayload.Message) + } + return r, err +} + +func (d *ClientDelegate) CreateBranch(params *branchOp.CreateBranchParams, opts ...branchOp.ClientOption) (*branchOp.CreateBranchOK, error) { + r, err := d.bc.BranchService.CreateBranch(params, opts...) + if err != nil { + errorPayload := err.(*branchOp.CreateBranchDefault).Payload.Error + return nil, fmt.Errorf("[POST /api/v1beta/clusters/{cluster_id}/branches][%d] CreateBranch %+v", errorPayload.Code, errorPayload.Message) + } + return r, err +} + +func (d *ClientDelegate) DeleteBranch(params *branchOp.DeleteBranchParams, opts ...branchOp.ClientOption) (*branchOp.DeleteBranchOK, error) { + r, err := d.bc.BranchService.DeleteBranch(params, opts...) + if err != nil { + errorPayload := err.(*branchOp.DeleteBranchDefault).Payload.Error + return nil, fmt.Errorf("[DELETE /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] DeleteBranch %+v", errorPayload.Code, errorPayload.Message) + } + return r, err +} + +func NewApiClient(publicKey string, privateKey string, apiUrl string) (*apiClient.GoTidbcloud, *importClient.TidbcloudImport, *connectInfoClient.TidbcloudConnectInfo, *branchClient.TidbcloudBranch, error) { httpclient := &http.Client{ Transport: NewTransportWithAgent(&digest.Transport{ Username: publicKey, @@ -165,11 +213,11 @@ func NewApiClient(publicKey string, privateKey string, apiUrl string) (*apiClien // Parse the URL u, err := prop.ValidateApiUrl(apiUrl) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } transport := httpTransport.NewWithClient(u.Host, u.Path, []string{u.Scheme}, httpclient) - return apiClient.New(transport, strfmt.Default), importClient.New(transport, strfmt.Default), connectInfoClient.New(transport, strfmt.Default), nil + return apiClient.New(transport, strfmt.Default), importClient.New(transport, strfmt.Default), connectInfoClient.New(transport, strfmt.Default), branchClient.New(transport, strfmt.Default), nil } // NewTransportWithAgent returns a new http.RoundTripper that add the User-Agent header, diff --git a/internal/service/cloud/logic.go b/internal/service/cloud/logic.go index db69d748..d624fc91 100644 --- a/internal/service/cloud/logic.go +++ b/internal/service/cloud/logic.go @@ -21,6 +21,8 @@ import ( "tidbcloud-cli/internal/ui" "tidbcloud-cli/internal/util" + branchApi "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" + branchModel "tidbcloud-cli/pkg/tidbcloud/branch/models" connectInfoApi "tidbcloud-cli/pkg/tidbcloud/connect_info/client/connect_info_service" connectInfoModel "tidbcloud-cli/pkg/tidbcloud/connect_info/models" importApi "tidbcloud-cli/pkg/tidbcloud/import/client/import_service" @@ -46,6 +48,19 @@ type Cluster struct { Name string } +type Branch struct { + ID string + DisplayName string + IsCluster bool +} + +func (b Branch) String() string { + if b.IsCluster { + return "main(the cluster)" + } + return fmt.Sprintf("%s(%s)", b.DisplayName, b.ID) +} + func (c Cluster) String() string { return fmt.Sprintf("%s(%s)", c.Name, c.ID) } @@ -137,6 +152,88 @@ func GetSelectedCluster(projectID string, pageSize int64, client TiDBCloudClient return cluster, nil } +func GetSelectedBranch(clusterID string, pageSize int64, client TiDBCloudClient) (*Branch, error) { + _, branchItems, err := RetrieveBranches(clusterID, pageSize, client) + if err != nil { + return nil, err + } + + var items = make([]interface{}, 0, len(branchItems)) + for _, item := range branchItems { + items = append(items, &Branch{ + ID: *item.ID, + DisplayName: *item.DisplayName, + IsCluster: false, + }) + } + if len(items) == 0 { + return nil, fmt.Errorf("no available branches found") + } + + model, err := ui.InitialSelectModel(items, "Choose the branch") + if err != nil { + return nil, errors.Trace(err) + } + itemsPerPage := 6 + model.EnablePagination(itemsPerPage) + model.EnableFilter() + + p := tea.NewProgram(model) + bModel, err := p.StartReturningModel() + if err != nil { + return nil, errors.Trace(err) + } + if m, _ := bModel.(ui.SelectModel); m.Interrupted { + return nil, util.InterruptError + } + branch := bModel.(ui.SelectModel).GetSelectedItem().(*Branch) + return branch, nil +} + +func GetSelectedBranchIfExist(clusterID string, pageSize int64, client TiDBCloudClient) (*Branch, error) { + _, branchItems, err := RetrieveBranches(clusterID, pageSize, client) + if err != nil { + return nil, err + } + + var items = make([]interface{}, 0, len(branchItems)+1) + // Add main item + items = append(items, &Branch{ + ID: clusterID, + DisplayName: "main", + IsCluster: true, + }) + for _, item := range branchItems { + items = append(items, &Branch{ + ID: *item.ID, + DisplayName: *item.DisplayName, + IsCluster: false, + }) + } + if len(items) == 1 { + return nil, nil + } + + model, err := ui.InitialSelectModel(items, "Choose the branch") + if err != nil { + return nil, errors.Trace(err) + } + itemsPerPage := 6 + model.EnablePagination(itemsPerPage) + model.EnableFilter() + + p := tea.NewProgram(model) + bModel, err := p.StartReturningModel() + if err != nil { + return nil, errors.Trace(err) + } + if m, _ := bModel.(ui.SelectModel); m.Interrupted { + return nil, util.InterruptError + } + branch := bModel.(ui.SelectModel).GetSelectedItem().(*Branch) + return branch, nil +} + // GetSelectedImport get the selected import task. statusFilter is used to filter the available options, only imports has status in statusFilter will be available. // statusFilter with no filter will mark all the import tasks as available options just like statusFilter with all status. func GetSelectedImport(pID string, cID string, pageSize int64, client TiDBCloudClient, statusFilter []importModel.OpenapiGetImportRespStatus) (*Import, error) { @@ -214,6 +311,25 @@ func RetrieveClusters(pID string, pageSize int64, d TiDBCloudClient) (int64, []* return total, items, nil } +func RetrieveBranches(cID string, pageSize int64, d TiDBCloudClient) (int64, []*branchModel.OpenapiBasicBranch, error) { + params := branchApi.NewListBranchesParams().WithClusterID(cID) + var total int64 = math.MaxInt64 + var page int64 = 1 + var items []*branchModel.OpenapiBasicBranch + // loop to get all branches + for (page-1)*pageSize < total { + branches, err := d.ListBranches(params.WithPageToken(&page).WithPageSize(&pageSize)) + if err != nil { + return 0, nil, errors.Trace(err) + } + + total = *branches.Payload.Total + page += 1 + items = append(items, branches.Payload.Branches...) + } + return total, items, nil +} + func RetrieveImports(pID string, cID string, pageSize int64, d TiDBCloudClient) (uint64, []*importModel.OpenapiGetImportResp, error) { params := importApi.NewListImportsParams().WithProjectID(pID).WithClusterID(cID) ps := int32(pageSize) diff --git a/pkg/tidbcloud/branch/README.md b/pkg/tidbcloud/branch/README.md new file mode 100644 index 00000000..668020fe --- /dev/null +++ b/pkg/tidbcloud/branch/README.md @@ -0,0 +1,54 @@ +We need to change the googlerpcStatus in branch_openapi.swagger.json + +``` + "googlerpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + } +``` + +to + +``` + "googlerpcStatus": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + } + } + } +``` + +Reason: open API's error output is different from `googlerpcStatus` (default error output) diff --git a/pkg/tidbcloud/branch/branch.swagger.json b/pkg/tidbcloud/branch/branch.swagger.json new file mode 100644 index 00000000..99e55aba --- /dev/null +++ b/pkg/tidbcloud/branch/branch.swagger.json @@ -0,0 +1,468 @@ +{ + "swagger": "2.0", + "info": { + "title": "Branch OpenAPIs for TiDB Cloud", + "description": "The TiDB Cloud API uses HTTP Digest Authentication. It protects your private key from being sent over the network.The API key contains a public key and a private key, which act as the username and password required in the HTTP Digest Authentication. The private key only displays upon the key creation.", + "version": "v1beta" + }, + "tags": [ + { + "name": "BranchService" + } + ], + "host": "api.tidbcloud.com", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/v1beta/clusters/{cluster_id}/branches": { + "get": { + "summary": "List all branches in the cluster.", + "operationId": "ListBranches", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/openapiListBranchesResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "cluster_id", + "description": "The ID of the cluster.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "page_token", + "description": "The number of pages.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64", + "default": 1 + }, + { + "name": "page_size", + "description": "The size of a page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64", + "default": 10 + } + ], + "tags": [ + "BranchService" + ] + }, + "post": { + "summary": "Create a branch.", + "operationId": "CreateBranch", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/openapiCreateBranchResp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "cluster_id", + "description": "The ID of the cluster.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "description": "The name of creating branch. It must be 4-64 characters long." + } + }, + "description": "CreateBranchReq is the request message for CreateBranch.", + "required": [ + "display_name" + ] + } + } + ], + "tags": [ + "BranchService" + ] + } + }, + "/api/v1beta/clusters/{cluster_id}/branches/{branch_id}": { + "get": { + "summary": "Get a branch.", + "operationId": "GetBranch", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/openapiBranch" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "cluster_id", + "description": "The ID of the cluster.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "branch_id", + "description": "The ID of the branch.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "BranchService" + ] + }, + "delete": { + "summary": "Delete a branch.", + "operationId": "DeleteBranch", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "cluster_id", + "description": "The ID of the cluster.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "branch_id", + "description": "The ID of the branch.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "BranchService" + ] + } + } + }, + "definitions": { + "googlerpcStatus": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/protobufAny" + } + } + } + } + } + }, + "openapiBasicBranch": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the branch." + }, + "name": { + "type": "string", + "description": "The name of the branch." + }, + "display_name": { + "type": "string", + "description": "The display name of the branch." + }, + "cluster_id": { + "type": "string", + "description": "The ID of the cluster." + }, + "parent_id": { + "type": "string", + "description": "The ID of the parent branch." + }, + "state": { + "$ref": "#/definitions/openapiBranchState", + "description": "The status of the branch." + }, + "create_time": { + "type": "string", + "format": "date-time", + "description": "The creation timestamp of the branch." + }, + "update_time": { + "type": "string", + "format": "date-time", + "description": "The update timestamp of the branch." + }, + "delete_time": { + "type": "string", + "format": "date-time", + "description": "The delete timestamp of the branch." + } + }, + "description": "BranchItem is the information of branch.", + "title": "BranchItem", + "required": [ + "id", + "name", + "display_name", + "cluster_id", + "parent_id", + "state", + "create_time", + "update_time", + "delete_time" + ] + }, + "openapiBranch": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the branch." + }, + "name": { + "type": "string", + "description": "The name of the branch." + }, + "display_name": { + "type": "string", + "description": "The display name of the branch." + }, + "cluster_id": { + "type": "string", + "description": "The ID of the cluster." + }, + "parent_id": { + "type": "string", + "description": "The ID of the parent branch." + }, + "state": { + "$ref": "#/definitions/openapiBranchState", + "description": "The status of the branch." + }, + "endpoints": { + "$ref": "#/definitions/openapiEndpoints", + "description": "The endpoint of the branch." + }, + "user_prefix": { + "type": "string", + "description": "The userPrefix of the branch." + }, + "usages": { + "$ref": "#/definitions/openapiUsages", + "description": "The usages of the branch." + }, + "create_time": { + "type": "string", + "format": "date-time", + "description": "The creation timestamp of the branch." + }, + "update_time": { + "type": "string", + "format": "date-time", + "description": "The update timestamp of the branch." + }, + "delete_time": { + "type": "string", + "format": "date-time", + "description": "The delete timestamp of the branch." + }, + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "The annotations of the branch." + } + }, + "description": "BranchItem is the information of branch.", + "title": "BranchItem", + "required": [ + "id", + "name", + "display_name", + "cluster_id", + "parent_id", + "state", + "endpoints", + "user_prefix", + "usages", + "create_time", + "update_time", + "delete_time", + "annotations" + ] + }, + "openapiBranchState": { + "type": "string", + "enum": [ + "CREATING", + "READY", + "DELETING", + "MAINTENANCE" + ] + }, + "openapiCreateBranchResp": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "branch-SDUAOISD", + "description": "The ID of the branch." + } + }, + "description": "CreateBranchResp is the response message for CreateBranch.", + "required": [ + "id" + ] + }, + "openapiEndpoints": { + "type": "object", + "properties": { + "public_endpoint": { + "$ref": "#/definitions/openapiPublicEndpoint", + "description": "The public endpoint of the branch." + } + }, + "description": "Endpoints is the endpoint of the branch." + }, + "openapiListBranchesResp": { + "type": "object", + "properties": { + "branches": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/openapiBasicBranch" + }, + "description": "The items of branches in the cluster." + }, + "total": { + "type": "integer", + "format": "int64", + "description": "The total number of branches in the cluster." + } + }, + "description": "ListBranchesResp is the response of ListBranches.", + "required": [ + "branches", + "total" + ] + }, + "openapiPublicEndpoint": { + "type": "object", + "properties": { + "host": { + "type": "string", + "description": "The host of the public endpoint." + }, + "port": { + "type": "integer", + "format": "int32", + "description": "The port of the public endpoint." + }, + "disabled": { + "type": "boolean", + "description": "The disabled of the public endpoint." + } + }, + "description": "PublicEndpoint is the public endpoint of the branch." + }, + "openapiUsages": { + "type": "object", + "properties": { + "request_unit": { + "type": "string", + "format": "int64", + "description": "The request unit of the branch." + }, + "row_storage": { + "type": "string", + "format": "int64", + "description": "The storage of the branch." + }, + "column_storage": { + "type": "string", + "format": "int64", + "description": "The column storage of the branch." + } + }, + "description": "Usages is the usages of the branch." + }, + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + } + } +} diff --git a/pkg/tidbcloud/branch/client/branch_service/branch_service_client.go b/pkg/tidbcloud/branch/client/branch_service/branch_service_client.go new file mode 100644 index 00000000..303d4790 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/branch_service_client.go @@ -0,0 +1,193 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new branch service API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for branch service API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + CreateBranch(params *CreateBranchParams, opts ...ClientOption) (*CreateBranchOK, error) + + DeleteBranch(params *DeleteBranchParams, opts ...ClientOption) (*DeleteBranchOK, error) + + GetBranch(params *GetBranchParams, opts ...ClientOption) (*GetBranchOK, error) + + ListBranches(params *ListBranchesParams, opts ...ClientOption) (*ListBranchesOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +CreateBranch creates a branch +*/ +func (a *Client) CreateBranch(params *CreateBranchParams, opts ...ClientOption) (*CreateBranchOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewCreateBranchParams() + } + op := &runtime.ClientOperation{ + ID: "CreateBranch", + Method: "POST", + PathPattern: "/api/v1beta/clusters/{cluster_id}/branches", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"https"}, + Params: params, + Reader: &CreateBranchReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*CreateBranchOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*CreateBranchDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +DeleteBranch deletes a branch +*/ +func (a *Client) DeleteBranch(params *DeleteBranchParams, opts ...ClientOption) (*DeleteBranchOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewDeleteBranchParams() + } + op := &runtime.ClientOperation{ + ID: "DeleteBranch", + Method: "DELETE", + PathPattern: "/api/v1beta/clusters/{cluster_id}/branches/{branch_id}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"https"}, + Params: params, + Reader: &DeleteBranchReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*DeleteBranchOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*DeleteBranchDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +GetBranch gets a branch +*/ +func (a *Client) GetBranch(params *GetBranchParams, opts ...ClientOption) (*GetBranchOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetBranchParams() + } + op := &runtime.ClientOperation{ + ID: "GetBranch", + Method: "GET", + PathPattern: "/api/v1beta/clusters/{cluster_id}/branches/{branch_id}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"https"}, + Params: params, + Reader: &GetBranchReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetBranchOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetBranchDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +ListBranches lists all branches in the cluster +*/ +func (a *Client) ListBranches(params *ListBranchesParams, opts ...ClientOption) (*ListBranchesOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListBranchesParams() + } + op := &runtime.ClientOperation{ + ID: "ListBranches", + Method: "GET", + PathPattern: "/api/v1beta/clusters/{cluster_id}/branches", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"https"}, + Params: params, + Reader: &ListBranchesReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ListBranchesOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ListBranchesDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/pkg/tidbcloud/branch/client/branch_service/create_branch_parameters.go b/pkg/tidbcloud/branch/client/branch_service/create_branch_parameters.go new file mode 100644 index 00000000..8f4d7a94 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/create_branch_parameters.go @@ -0,0 +1,168 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewCreateBranchParams creates a new CreateBranchParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewCreateBranchParams() *CreateBranchParams { + return &CreateBranchParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewCreateBranchParamsWithTimeout creates a new CreateBranchParams object +// with the ability to set a timeout on a request. +func NewCreateBranchParamsWithTimeout(timeout time.Duration) *CreateBranchParams { + return &CreateBranchParams{ + timeout: timeout, + } +} + +// NewCreateBranchParamsWithContext creates a new CreateBranchParams object +// with the ability to set a context for a request. +func NewCreateBranchParamsWithContext(ctx context.Context) *CreateBranchParams { + return &CreateBranchParams{ + Context: ctx, + } +} + +// NewCreateBranchParamsWithHTTPClient creates a new CreateBranchParams object +// with the ability to set a custom HTTPClient for a request. +func NewCreateBranchParamsWithHTTPClient(client *http.Client) *CreateBranchParams { + return &CreateBranchParams{ + HTTPClient: client, + } +} + +/* +CreateBranchParams contains all the parameters to send to the API endpoint + + for the create branch operation. + + Typically these are written to a http.Request. +*/ +type CreateBranchParams struct { + + // Body. + Body CreateBranchBody + + /* ClusterID. + + The ID of the cluster. + */ + ClusterID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the create branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateBranchParams) WithDefaults() *CreateBranchParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the create branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *CreateBranchParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the create branch params +func (o *CreateBranchParams) WithTimeout(timeout time.Duration) *CreateBranchParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the create branch params +func (o *CreateBranchParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the create branch params +func (o *CreateBranchParams) WithContext(ctx context.Context) *CreateBranchParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the create branch params +func (o *CreateBranchParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the create branch params +func (o *CreateBranchParams) WithHTTPClient(client *http.Client) *CreateBranchParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the create branch params +func (o *CreateBranchParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the create branch params +func (o *CreateBranchParams) WithBody(body CreateBranchBody) *CreateBranchParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the create branch params +func (o *CreateBranchParams) SetBody(body CreateBranchBody) { + o.Body = body +} + +// WithClusterID adds the clusterID to the create branch params +func (o *CreateBranchParams) WithClusterID(clusterID string) *CreateBranchParams { + o.SetClusterID(clusterID) + return o +} + +// SetClusterID adds the clusterId to the create branch params +func (o *CreateBranchParams) SetClusterID(clusterID string) { + o.ClusterID = clusterID +} + +// WriteToRequest writes these params to a swagger request +func (o *CreateBranchParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + // path param cluster_id + if err := r.SetPathParam("cluster_id", o.ClusterID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/create_branch_responses.go b/pkg/tidbcloud/branch/client/branch_service/create_branch_responses.go new file mode 100644 index 00000000..a51610bc --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/create_branch_responses.go @@ -0,0 +1,243 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "fmt" + "io" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" + + "tidbcloud-cli/pkg/tidbcloud/branch/models" +) + +// CreateBranchReader is a Reader for the CreateBranch structure. +type CreateBranchReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *CreateBranchReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewCreateBranchOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewCreateBranchDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewCreateBranchOK creates a CreateBranchOK with default headers values +func NewCreateBranchOK() *CreateBranchOK { + return &CreateBranchOK{} +} + +/* +CreateBranchOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type CreateBranchOK struct { + Payload *models.OpenapiCreateBranchResp +} + +// IsSuccess returns true when this create branch o k response has a 2xx status code +func (o *CreateBranchOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this create branch o k response has a 3xx status code +func (o *CreateBranchOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this create branch o k response has a 4xx status code +func (o *CreateBranchOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this create branch o k response has a 5xx status code +func (o *CreateBranchOK) IsServerError() bool { + return false +} + +// IsCode returns true when this create branch o k response a status code equal to that given +func (o *CreateBranchOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the create branch o k response +func (o *CreateBranchOK) Code() int { + return 200 +} + +func (o *CreateBranchOK) Error() string { + return fmt.Sprintf("[POST /api/v1beta/clusters/{cluster_id}/branches][%d] createBranchOK %+v", 200, o.Payload) +} + +func (o *CreateBranchOK) String() string { + return fmt.Sprintf("[POST /api/v1beta/clusters/{cluster_id}/branches][%d] createBranchOK %+v", 200, o.Payload) +} + +func (o *CreateBranchOK) GetPayload() *models.OpenapiCreateBranchResp { + return o.Payload +} + +func (o *CreateBranchOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.OpenapiCreateBranchResp) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewCreateBranchDefault creates a CreateBranchDefault with default headers values +func NewCreateBranchDefault(code int) *CreateBranchDefault { + return &CreateBranchDefault{ + _statusCode: code, + } +} + +/* +CreateBranchDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type CreateBranchDefault struct { + _statusCode int + + Payload *models.GooglerpcStatus +} + +// IsSuccess returns true when this create branch default response has a 2xx status code +func (o *CreateBranchDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this create branch default response has a 3xx status code +func (o *CreateBranchDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this create branch default response has a 4xx status code +func (o *CreateBranchDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this create branch default response has a 5xx status code +func (o *CreateBranchDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this create branch default response a status code equal to that given +func (o *CreateBranchDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the create branch default response +func (o *CreateBranchDefault) Code() int { + return o._statusCode +} + +func (o *CreateBranchDefault) Error() string { + return fmt.Sprintf("[POST /api/v1beta/clusters/{cluster_id}/branches][%d] CreateBranch default %+v", o._statusCode, o.Payload) +} + +func (o *CreateBranchDefault) String() string { + return fmt.Sprintf("[POST /api/v1beta/clusters/{cluster_id}/branches][%d] CreateBranch default %+v", o._statusCode, o.Payload) +} + +func (o *CreateBranchDefault) GetPayload() *models.GooglerpcStatus { + return o.Payload +} + +func (o *CreateBranchDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.GooglerpcStatus) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +CreateBranchBody CreateBranchReq is the request message for CreateBranch. +swagger:model CreateBranchBody +*/ +type CreateBranchBody struct { + + // The name of creating branch. It must be 4-64 characters long. + // Required: true + DisplayName *string `json:"display_name"` +} + +// Validate validates this create branch body +func (o *CreateBranchBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateDisplayName(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *CreateBranchBody) validateDisplayName(formats strfmt.Registry) error { + + if err := validate.Required("body"+"."+"display_name", "body", o.DisplayName); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this create branch body based on context it is used +func (o *CreateBranchBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *CreateBranchBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *CreateBranchBody) UnmarshalBinary(b []byte) error { + var res CreateBranchBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/delete_branch_parameters.go b/pkg/tidbcloud/branch/client/branch_service/delete_branch_parameters.go new file mode 100644 index 00000000..d61d31a9 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/delete_branch_parameters.go @@ -0,0 +1,173 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewDeleteBranchParams creates a new DeleteBranchParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewDeleteBranchParams() *DeleteBranchParams { + return &DeleteBranchParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewDeleteBranchParamsWithTimeout creates a new DeleteBranchParams object +// with the ability to set a timeout on a request. +func NewDeleteBranchParamsWithTimeout(timeout time.Duration) *DeleteBranchParams { + return &DeleteBranchParams{ + timeout: timeout, + } +} + +// NewDeleteBranchParamsWithContext creates a new DeleteBranchParams object +// with the ability to set a context for a request. +func NewDeleteBranchParamsWithContext(ctx context.Context) *DeleteBranchParams { + return &DeleteBranchParams{ + Context: ctx, + } +} + +// NewDeleteBranchParamsWithHTTPClient creates a new DeleteBranchParams object +// with the ability to set a custom HTTPClient for a request. +func NewDeleteBranchParamsWithHTTPClient(client *http.Client) *DeleteBranchParams { + return &DeleteBranchParams{ + HTTPClient: client, + } +} + +/* +DeleteBranchParams contains all the parameters to send to the API endpoint + + for the delete branch operation. + + Typically these are written to a http.Request. +*/ +type DeleteBranchParams struct { + + /* BranchID. + + The ID of the branch. + */ + BranchID string + + /* ClusterID. + + The ID of the cluster. + */ + ClusterID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the delete branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteBranchParams) WithDefaults() *DeleteBranchParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the delete branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteBranchParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the delete branch params +func (o *DeleteBranchParams) WithTimeout(timeout time.Duration) *DeleteBranchParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the delete branch params +func (o *DeleteBranchParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the delete branch params +func (o *DeleteBranchParams) WithContext(ctx context.Context) *DeleteBranchParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the delete branch params +func (o *DeleteBranchParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the delete branch params +func (o *DeleteBranchParams) WithHTTPClient(client *http.Client) *DeleteBranchParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the delete branch params +func (o *DeleteBranchParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBranchID adds the branchID to the delete branch params +func (o *DeleteBranchParams) WithBranchID(branchID string) *DeleteBranchParams { + o.SetBranchID(branchID) + return o +} + +// SetBranchID adds the branchId to the delete branch params +func (o *DeleteBranchParams) SetBranchID(branchID string) { + o.BranchID = branchID +} + +// WithClusterID adds the clusterID to the delete branch params +func (o *DeleteBranchParams) WithClusterID(clusterID string) *DeleteBranchParams { + o.SetClusterID(clusterID) + return o +} + +// SetClusterID adds the clusterId to the delete branch params +func (o *DeleteBranchParams) SetClusterID(clusterID string) { + o.ClusterID = clusterID +} + +// WriteToRequest writes these params to a swagger request +func (o *DeleteBranchParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param branch_id + if err := r.SetPathParam("branch_id", o.BranchID); err != nil { + return err + } + + // path param cluster_id + if err := r.SetPathParam("cluster_id", o.ClusterID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/delete_branch_responses.go b/pkg/tidbcloud/branch/client/branch_service/delete_branch_responses.go new file mode 100644 index 00000000..529a0512 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/delete_branch_responses.go @@ -0,0 +1,180 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "tidbcloud-cli/pkg/tidbcloud/branch/models" +) + +// DeleteBranchReader is a Reader for the DeleteBranch structure. +type DeleteBranchReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *DeleteBranchReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewDeleteBranchOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewDeleteBranchDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewDeleteBranchOK creates a DeleteBranchOK with default headers values +func NewDeleteBranchOK() *DeleteBranchOK { + return &DeleteBranchOK{} +} + +/* +DeleteBranchOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type DeleteBranchOK struct { + Payload interface{} +} + +// IsSuccess returns true when this delete branch o k response has a 2xx status code +func (o *DeleteBranchOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this delete branch o k response has a 3xx status code +func (o *DeleteBranchOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this delete branch o k response has a 4xx status code +func (o *DeleteBranchOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this delete branch o k response has a 5xx status code +func (o *DeleteBranchOK) IsServerError() bool { + return false +} + +// IsCode returns true when this delete branch o k response a status code equal to that given +func (o *DeleteBranchOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the delete branch o k response +func (o *DeleteBranchOK) Code() int { + return 200 +} + +func (o *DeleteBranchOK) Error() string { + return fmt.Sprintf("[DELETE /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] deleteBranchOK %+v", 200, o.Payload) +} + +func (o *DeleteBranchOK) String() string { + return fmt.Sprintf("[DELETE /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] deleteBranchOK %+v", 200, o.Payload) +} + +func (o *DeleteBranchOK) GetPayload() interface{} { + return o.Payload +} + +func (o *DeleteBranchOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewDeleteBranchDefault creates a DeleteBranchDefault with default headers values +func NewDeleteBranchDefault(code int) *DeleteBranchDefault { + return &DeleteBranchDefault{ + _statusCode: code, + } +} + +/* +DeleteBranchDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type DeleteBranchDefault struct { + _statusCode int + + Payload *models.GooglerpcStatus +} + +// IsSuccess returns true when this delete branch default response has a 2xx status code +func (o *DeleteBranchDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this delete branch default response has a 3xx status code +func (o *DeleteBranchDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this delete branch default response has a 4xx status code +func (o *DeleteBranchDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this delete branch default response has a 5xx status code +func (o *DeleteBranchDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this delete branch default response a status code equal to that given +func (o *DeleteBranchDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the delete branch default response +func (o *DeleteBranchDefault) Code() int { + return o._statusCode +} + +func (o *DeleteBranchDefault) Error() string { + return fmt.Sprintf("[DELETE /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] DeleteBranch default %+v", o._statusCode, o.Payload) +} + +func (o *DeleteBranchDefault) String() string { + return fmt.Sprintf("[DELETE /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] DeleteBranch default %+v", o._statusCode, o.Payload) +} + +func (o *DeleteBranchDefault) GetPayload() *models.GooglerpcStatus { + return o.Payload +} + +func (o *DeleteBranchDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.GooglerpcStatus) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/get_branch_parameters.go b/pkg/tidbcloud/branch/client/branch_service/get_branch_parameters.go new file mode 100644 index 00000000..f4308f34 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/get_branch_parameters.go @@ -0,0 +1,173 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetBranchParams creates a new GetBranchParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetBranchParams() *GetBranchParams { + return &GetBranchParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetBranchParamsWithTimeout creates a new GetBranchParams object +// with the ability to set a timeout on a request. +func NewGetBranchParamsWithTimeout(timeout time.Duration) *GetBranchParams { + return &GetBranchParams{ + timeout: timeout, + } +} + +// NewGetBranchParamsWithContext creates a new GetBranchParams object +// with the ability to set a context for a request. +func NewGetBranchParamsWithContext(ctx context.Context) *GetBranchParams { + return &GetBranchParams{ + Context: ctx, + } +} + +// NewGetBranchParamsWithHTTPClient creates a new GetBranchParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetBranchParamsWithHTTPClient(client *http.Client) *GetBranchParams { + return &GetBranchParams{ + HTTPClient: client, + } +} + +/* +GetBranchParams contains all the parameters to send to the API endpoint + + for the get branch operation. + + Typically these are written to a http.Request. +*/ +type GetBranchParams struct { + + /* BranchID. + + The ID of the branch. + */ + BranchID string + + /* ClusterID. + + The ID of the cluster. + */ + ClusterID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetBranchParams) WithDefaults() *GetBranchParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get branch params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetBranchParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get branch params +func (o *GetBranchParams) WithTimeout(timeout time.Duration) *GetBranchParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get branch params +func (o *GetBranchParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get branch params +func (o *GetBranchParams) WithContext(ctx context.Context) *GetBranchParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get branch params +func (o *GetBranchParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get branch params +func (o *GetBranchParams) WithHTTPClient(client *http.Client) *GetBranchParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get branch params +func (o *GetBranchParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBranchID adds the branchID to the get branch params +func (o *GetBranchParams) WithBranchID(branchID string) *GetBranchParams { + o.SetBranchID(branchID) + return o +} + +// SetBranchID adds the branchId to the get branch params +func (o *GetBranchParams) SetBranchID(branchID string) { + o.BranchID = branchID +} + +// WithClusterID adds the clusterID to the get branch params +func (o *GetBranchParams) WithClusterID(clusterID string) *GetBranchParams { + o.SetClusterID(clusterID) + return o +} + +// SetClusterID adds the clusterId to the get branch params +func (o *GetBranchParams) SetClusterID(clusterID string) { + o.ClusterID = clusterID +} + +// WriteToRequest writes these params to a swagger request +func (o *GetBranchParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param branch_id + if err := r.SetPathParam("branch_id", o.BranchID); err != nil { + return err + } + + // path param cluster_id + if err := r.SetPathParam("cluster_id", o.ClusterID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/get_branch_responses.go b/pkg/tidbcloud/branch/client/branch_service/get_branch_responses.go new file mode 100644 index 00000000..2783d7a0 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/get_branch_responses.go @@ -0,0 +1,182 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "tidbcloud-cli/pkg/tidbcloud/branch/models" +) + +// GetBranchReader is a Reader for the GetBranch structure. +type GetBranchReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetBranchReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetBranchOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewGetBranchDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetBranchOK creates a GetBranchOK with default headers values +func NewGetBranchOK() *GetBranchOK { + return &GetBranchOK{} +} + +/* +GetBranchOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type GetBranchOK struct { + Payload *models.OpenapiBranch +} + +// IsSuccess returns true when this get branch o k response has a 2xx status code +func (o *GetBranchOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get branch o k response has a 3xx status code +func (o *GetBranchOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get branch o k response has a 4xx status code +func (o *GetBranchOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get branch o k response has a 5xx status code +func (o *GetBranchOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get branch o k response a status code equal to that given +func (o *GetBranchOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the get branch o k response +func (o *GetBranchOK) Code() int { + return 200 +} + +func (o *GetBranchOK) Error() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] getBranchOK %+v", 200, o.Payload) +} + +func (o *GetBranchOK) String() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] getBranchOK %+v", 200, o.Payload) +} + +func (o *GetBranchOK) GetPayload() *models.OpenapiBranch { + return o.Payload +} + +func (o *GetBranchOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.OpenapiBranch) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetBranchDefault creates a GetBranchDefault with default headers values +func NewGetBranchDefault(code int) *GetBranchDefault { + return &GetBranchDefault{ + _statusCode: code, + } +} + +/* +GetBranchDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type GetBranchDefault struct { + _statusCode int + + Payload *models.GooglerpcStatus +} + +// IsSuccess returns true when this get branch default response has a 2xx status code +func (o *GetBranchDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this get branch default response has a 3xx status code +func (o *GetBranchDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this get branch default response has a 4xx status code +func (o *GetBranchDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this get branch default response has a 5xx status code +func (o *GetBranchDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this get branch default response a status code equal to that given +func (o *GetBranchDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the get branch default response +func (o *GetBranchDefault) Code() int { + return o._statusCode +} + +func (o *GetBranchDefault) Error() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] GetBranch default %+v", o._statusCode, o.Payload) +} + +func (o *GetBranchDefault) String() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches/{branch_id}][%d] GetBranch default %+v", o._statusCode, o.Payload) +} + +func (o *GetBranchDefault) GetPayload() *models.GooglerpcStatus { + return o.Payload +} + +func (o *GetBranchDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.GooglerpcStatus) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/list_branches_parameters.go b/pkg/tidbcloud/branch/client/branch_service/list_branches_parameters.go new file mode 100644 index 00000000..aeeab25a --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/list_branches_parameters.go @@ -0,0 +1,240 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewListBranchesParams creates a new ListBranchesParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewListBranchesParams() *ListBranchesParams { + return &ListBranchesParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListBranchesParamsWithTimeout creates a new ListBranchesParams object +// with the ability to set a timeout on a request. +func NewListBranchesParamsWithTimeout(timeout time.Duration) *ListBranchesParams { + return &ListBranchesParams{ + timeout: timeout, + } +} + +// NewListBranchesParamsWithContext creates a new ListBranchesParams object +// with the ability to set a context for a request. +func NewListBranchesParamsWithContext(ctx context.Context) *ListBranchesParams { + return &ListBranchesParams{ + Context: ctx, + } +} + +// NewListBranchesParamsWithHTTPClient creates a new ListBranchesParams object +// with the ability to set a custom HTTPClient for a request. +func NewListBranchesParamsWithHTTPClient(client *http.Client) *ListBranchesParams { + return &ListBranchesParams{ + HTTPClient: client, + } +} + +/* +ListBranchesParams contains all the parameters to send to the API endpoint + + for the list branches operation. + + Typically these are written to a http.Request. +*/ +type ListBranchesParams struct { + + /* ClusterID. + + The ID of the cluster. + */ + ClusterID string + + /* PageSize. + + The size of a page. + + Format: int64 + Default: 10 + */ + PageSize *int64 + + /* PageToken. + + The number of pages. + + Format: int64 + Default: 1 + */ + PageToken *int64 + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list branches params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListBranchesParams) WithDefaults() *ListBranchesParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list branches params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListBranchesParams) SetDefaults() { + var ( + pageSizeDefault = int64(10) + + pageTokenDefault = int64(1) + ) + + val := ListBranchesParams{ + PageSize: &pageSizeDefault, + PageToken: &pageTokenDefault, + } + + val.timeout = o.timeout + val.Context = o.Context + val.HTTPClient = o.HTTPClient + *o = val +} + +// WithTimeout adds the timeout to the list branches params +func (o *ListBranchesParams) WithTimeout(timeout time.Duration) *ListBranchesParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list branches params +func (o *ListBranchesParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list branches params +func (o *ListBranchesParams) WithContext(ctx context.Context) *ListBranchesParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list branches params +func (o *ListBranchesParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list branches params +func (o *ListBranchesParams) WithHTTPClient(client *http.Client) *ListBranchesParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list branches params +func (o *ListBranchesParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithClusterID adds the clusterID to the list branches params +func (o *ListBranchesParams) WithClusterID(clusterID string) *ListBranchesParams { + o.SetClusterID(clusterID) + return o +} + +// SetClusterID adds the clusterId to the list branches params +func (o *ListBranchesParams) SetClusterID(clusterID string) { + o.ClusterID = clusterID +} + +// WithPageSize adds the pageSize to the list branches params +func (o *ListBranchesParams) WithPageSize(pageSize *int64) *ListBranchesParams { + o.SetPageSize(pageSize) + return o +} + +// SetPageSize adds the pageSize to the list branches params +func (o *ListBranchesParams) SetPageSize(pageSize *int64) { + o.PageSize = pageSize +} + +// WithPageToken adds the pageToken to the list branches params +func (o *ListBranchesParams) WithPageToken(pageToken *int64) *ListBranchesParams { + o.SetPageToken(pageToken) + return o +} + +// SetPageToken adds the pageToken to the list branches params +func (o *ListBranchesParams) SetPageToken(pageToken *int64) { + o.PageToken = pageToken +} + +// WriteToRequest writes these params to a swagger request +func (o *ListBranchesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param cluster_id + if err := r.SetPathParam("cluster_id", o.ClusterID); err != nil { + return err + } + + if o.PageSize != nil { + + // query param page_size + var qrPageSize int64 + + if o.PageSize != nil { + qrPageSize = *o.PageSize + } + qPageSize := swag.FormatInt64(qrPageSize) + if qPageSize != "" { + + if err := r.SetQueryParam("page_size", qPageSize); err != nil { + return err + } + } + } + + if o.PageToken != nil { + + // query param page_token + var qrPageToken int64 + + if o.PageToken != nil { + qrPageToken = *o.PageToken + } + qPageToken := swag.FormatInt64(qrPageToken) + if qPageToken != "" { + + if err := r.SetQueryParam("page_token", qPageToken); err != nil { + return err + } + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/tidbcloud/branch/client/branch_service/list_branches_responses.go b/pkg/tidbcloud/branch/client/branch_service/list_branches_responses.go new file mode 100644 index 00000000..6fef9965 --- /dev/null +++ b/pkg/tidbcloud/branch/client/branch_service/list_branches_responses.go @@ -0,0 +1,182 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package branch_service + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "tidbcloud-cli/pkg/tidbcloud/branch/models" +) + +// ListBranchesReader is a Reader for the ListBranches structure. +type ListBranchesReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListBranchesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListBranchesOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewListBranchesDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewListBranchesOK creates a ListBranchesOK with default headers values +func NewListBranchesOK() *ListBranchesOK { + return &ListBranchesOK{} +} + +/* +ListBranchesOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type ListBranchesOK struct { + Payload *models.OpenapiListBranchesResp +} + +// IsSuccess returns true when this list branches o k response has a 2xx status code +func (o *ListBranchesOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this list branches o k response has a 3xx status code +func (o *ListBranchesOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list branches o k response has a 4xx status code +func (o *ListBranchesOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this list branches o k response has a 5xx status code +func (o *ListBranchesOK) IsServerError() bool { + return false +} + +// IsCode returns true when this list branches o k response a status code equal to that given +func (o *ListBranchesOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the list branches o k response +func (o *ListBranchesOK) Code() int { + return 200 +} + +func (o *ListBranchesOK) Error() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches][%d] listBranchesOK %+v", 200, o.Payload) +} + +func (o *ListBranchesOK) String() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches][%d] listBranchesOK %+v", 200, o.Payload) +} + +func (o *ListBranchesOK) GetPayload() *models.OpenapiListBranchesResp { + return o.Payload +} + +func (o *ListBranchesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.OpenapiListBranchesResp) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListBranchesDefault creates a ListBranchesDefault with default headers values +func NewListBranchesDefault(code int) *ListBranchesDefault { + return &ListBranchesDefault{ + _statusCode: code, + } +} + +/* +ListBranchesDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type ListBranchesDefault struct { + _statusCode int + + Payload *models.GooglerpcStatus +} + +// IsSuccess returns true when this list branches default response has a 2xx status code +func (o *ListBranchesDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this list branches default response has a 3xx status code +func (o *ListBranchesDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this list branches default response has a 4xx status code +func (o *ListBranchesDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this list branches default response has a 5xx status code +func (o *ListBranchesDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this list branches default response a status code equal to that given +func (o *ListBranchesDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the list branches default response +func (o *ListBranchesDefault) Code() int { + return o._statusCode +} + +func (o *ListBranchesDefault) Error() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches][%d] ListBranches default %+v", o._statusCode, o.Payload) +} + +func (o *ListBranchesDefault) String() string { + return fmt.Sprintf("[GET /api/v1beta/clusters/{cluster_id}/branches][%d] ListBranches default %+v", o._statusCode, o.Payload) +} + +func (o *ListBranchesDefault) GetPayload() *models.GooglerpcStatus { + return o.Payload +} + +func (o *ListBranchesDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(models.GooglerpcStatus) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/tidbcloud/branch/client/tidbcloud_branch_client.go b/pkg/tidbcloud/branch/client/tidbcloud_branch_client.go new file mode 100644 index 00000000..e43604e1 --- /dev/null +++ b/pkg/tidbcloud/branch/client/tidbcloud_branch_client.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package client + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/runtime" + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "tidbcloud-cli/pkg/tidbcloud/branch/client/branch_service" +) + +// Default tidbcloud branch HTTP client. +var Default = NewHTTPClient(nil) + +const ( + // DefaultHost is the default Host + // found in Meta (info) section of spec file + DefaultHost string = "api.tidbcloud.com" + // DefaultBasePath is the default BasePath + // found in Meta (info) section of spec file + DefaultBasePath string = "/" +) + +// DefaultSchemes are the default schemes found in Meta (info) section of spec file +var DefaultSchemes = []string{"https"} + +// NewHTTPClient creates a new tidbcloud branch HTTP client. +func NewHTTPClient(formats strfmt.Registry) *TidbcloudBranch { + return NewHTTPClientWithConfig(formats, nil) +} + +// NewHTTPClientWithConfig creates a new tidbcloud branch HTTP client, +// using a customizable transport config. +func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *TidbcloudBranch { + // ensure nullable parameters have default + if cfg == nil { + cfg = DefaultTransportConfig() + } + + // create transport and client + transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes) + return New(transport, formats) +} + +// New creates a new tidbcloud branch client +func New(transport runtime.ClientTransport, formats strfmt.Registry) *TidbcloudBranch { + // ensure nullable parameters have default + if formats == nil { + formats = strfmt.Default + } + + cli := new(TidbcloudBranch) + cli.Transport = transport + cli.BranchService = branch_service.New(transport, formats) + return cli +} + +// DefaultTransportConfig creates a TransportConfig with the +// default settings taken from the meta section of the spec file. +func DefaultTransportConfig() *TransportConfig { + return &TransportConfig{ + Host: DefaultHost, + BasePath: DefaultBasePath, + Schemes: DefaultSchemes, + } +} + +// TransportConfig contains the transport related info, +// found in the meta section of the spec file. +type TransportConfig struct { + Host string + BasePath string + Schemes []string +} + +// WithHost overrides the default host, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithHost(host string) *TransportConfig { + cfg.Host = host + return cfg +} + +// WithBasePath overrides the default basePath, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig { + cfg.BasePath = basePath + return cfg +} + +// WithSchemes overrides the default schemes, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig { + cfg.Schemes = schemes + return cfg +} + +// TidbcloudBranch is a client for tidbcloud branch +type TidbcloudBranch struct { + BranchService branch_service.ClientService + + Transport runtime.ClientTransport +} + +// SetTransport changes the transport on the client and all its subresources +func (c *TidbcloudBranch) SetTransport(transport runtime.ClientTransport) { + c.Transport = transport + c.BranchService.SetTransport(transport) +} diff --git a/pkg/tidbcloud/branch/models/googlerpc_status.go b/pkg/tidbcloud/branch/models/googlerpc_status.go new file mode 100644 index 00000000..fd14346e --- /dev/null +++ b/pkg/tidbcloud/branch/models/googlerpc_status.go @@ -0,0 +1,222 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// GooglerpcStatus googlerpc status +// +// swagger:model googlerpcStatus +type GooglerpcStatus struct { + + // error + Error *GooglerpcStatusError `json:"error,omitempty"` +} + +// Validate validates this googlerpc status +func (m *GooglerpcStatus) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateError(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *GooglerpcStatus) validateError(formats strfmt.Registry) error { + if swag.IsZero(m.Error) { // not required + return nil + } + + if m.Error != nil { + if err := m.Error.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error") + } + return err + } + } + + return nil +} + +// ContextValidate validate this googlerpc status based on the context it is used +func (m *GooglerpcStatus) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateError(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *GooglerpcStatus) contextValidateError(ctx context.Context, formats strfmt.Registry) error { + + if m.Error != nil { + + if swag.IsZero(m.Error) { // not required + return nil + } + + if err := m.Error.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *GooglerpcStatus) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *GooglerpcStatus) UnmarshalBinary(b []byte) error { + var res GooglerpcStatus + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} + +// GooglerpcStatusError googlerpc status error +// +// swagger:model GooglerpcStatusError +type GooglerpcStatusError struct { + + // code + Code int32 `json:"code,omitempty"` + + // details + Details []*ProtobufAny `json:"details"` + + // message + Message string `json:"message,omitempty"` +} + +// Validate validates this googlerpc status error +func (m *GooglerpcStatusError) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateDetails(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *GooglerpcStatusError) validateDetails(formats strfmt.Registry) error { + if swag.IsZero(m.Details) { // not required + return nil + } + + for i := 0; i < len(m.Details); i++ { + if swag.IsZero(m.Details[i]) { // not required + continue + } + + if m.Details[i] != nil { + if err := m.Details[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this googlerpc status error based on the context it is used +func (m *GooglerpcStatusError) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateDetails(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *GooglerpcStatusError) contextValidateDetails(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Details); i++ { + + if m.Details[i] != nil { + + if swag.IsZero(m.Details[i]) { // not required + return nil + } + + if err := m.Details[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *GooglerpcStatusError) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *GooglerpcStatusError) UnmarshalBinary(b []byte) error { + var res GooglerpcStatusError + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_basic_branch.go b/pkg/tidbcloud/branch/models/openapi_basic_branch.go new file mode 100644 index 00000000..4a306f3c --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_basic_branch.go @@ -0,0 +1,265 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// OpenapiBasicBranch BranchItem +// +// BranchItem is the information of branch. +// +// swagger:model openapiBasicBranch +type OpenapiBasicBranch struct { + + // The ID of the cluster. + // Required: true + ClusterID *string `json:"cluster_id"` + + // The creation timestamp of the branch. + // Required: true + // Format: date-time + CreateTime *strfmt.DateTime `json:"create_time"` + + // The delete timestamp of the branch. + // Required: true + // Format: date-time + DeleteTime *strfmt.DateTime `json:"delete_time"` + + // The display name of the branch. + // Required: true + DisplayName *string `json:"display_name"` + + // The ID of the branch. + // Required: true + ID *string `json:"id"` + + // The name of the branch. + // Required: true + Name *string `json:"name"` + + // The ID of the parent branch. + // Required: true + ParentID *string `json:"parent_id"` + + // The status of the branch. + // Required: true + State *OpenapiBranchState `json:"state"` + + // The update timestamp of the branch. + // Required: true + // Format: date-time + UpdateTime *strfmt.DateTime `json:"update_time"` +} + +// Validate validates this openapi basic branch +func (m *OpenapiBasicBranch) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateClusterID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCreateTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDeleteTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDisplayName(formats); err != nil { + res = append(res, err) + } + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateName(formats); err != nil { + res = append(res, err) + } + + if err := m.validateParentID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateState(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpdateTime(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiBasicBranch) validateClusterID(formats strfmt.Registry) error { + + if err := validate.Required("cluster_id", "body", m.ClusterID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateCreateTime(formats strfmt.Registry) error { + + if err := validate.Required("create_time", "body", m.CreateTime); err != nil { + return err + } + + if err := validate.FormatOf("create_time", "body", "date-time", m.CreateTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateDeleteTime(formats strfmt.Registry) error { + + if err := validate.Required("delete_time", "body", m.DeleteTime); err != nil { + return err + } + + if err := validate.FormatOf("delete_time", "body", "date-time", m.DeleteTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateDisplayName(formats strfmt.Registry) error { + + if err := validate.Required("display_name", "body", m.DisplayName); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateID(formats strfmt.Registry) error { + + if err := validate.Required("id", "body", m.ID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateName(formats strfmt.Registry) error { + + if err := validate.Required("name", "body", m.Name); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateParentID(formats strfmt.Registry) error { + + if err := validate.Required("parent_id", "body", m.ParentID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBasicBranch) validateState(formats strfmt.Registry) error { + + if err := validate.Required("state", "body", m.State); err != nil { + return err + } + + if err := validate.Required("state", "body", m.State); err != nil { + return err + } + + if m.State != nil { + if err := m.State.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("state") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("state") + } + return err + } + } + + return nil +} + +func (m *OpenapiBasicBranch) validateUpdateTime(formats strfmt.Registry) error { + + if err := validate.Required("update_time", "body", m.UpdateTime); err != nil { + return err + } + + if err := validate.FormatOf("update_time", "body", "date-time", m.UpdateTime.String(), formats); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this openapi basic branch based on the context it is used +func (m *OpenapiBasicBranch) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateState(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiBasicBranch) contextValidateState(ctx context.Context, formats strfmt.Registry) error { + + if m.State != nil { + + if err := m.State.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("state") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("state") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiBasicBranch) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiBasicBranch) UnmarshalBinary(b []byte) error { + var res OpenapiBasicBranch + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_branch.go b/pkg/tidbcloud/branch/models/openapi_branch.go new file mode 100644 index 00000000..c5150eb7 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_branch.go @@ -0,0 +1,397 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// OpenapiBranch BranchItem +// +// BranchItem is the information of branch. +// +// swagger:model openapiBranch +type OpenapiBranch struct { + + // The annotations of the branch. + // Required: true + Annotations map[string]string `json:"annotations"` + + // The ID of the cluster. + // Required: true + ClusterID *string `json:"cluster_id"` + + // The creation timestamp of the branch. + // Required: true + // Format: date-time + CreateTime *strfmt.DateTime `json:"create_time"` + + // The delete timestamp of the branch. + // Required: true + // Format: date-time + DeleteTime *strfmt.DateTime `json:"delete_time"` + + // The display name of the branch. + // Required: true + DisplayName *string `json:"display_name"` + + // The endpoint of the branch. + // Required: true + Endpoints *OpenapiEndpoints `json:"endpoints"` + + // The ID of the branch. + // Required: true + ID *string `json:"id"` + + // The name of the branch. + // Required: true + Name *string `json:"name"` + + // The ID of the parent branch. + // Required: true + ParentID *string `json:"parent_id"` + + // The status of the branch. + // Required: true + State *OpenapiBranchState `json:"state"` + + // The update timestamp of the branch. + // Required: true + // Format: date-time + UpdateTime *strfmt.DateTime `json:"update_time"` + + // The usages of the branch. + // Required: true + Usages *OpenapiUsages `json:"usages"` + + // The userPrefix of the branch. + // Required: true + UserPrefix *string `json:"user_prefix"` +} + +// Validate validates this openapi branch +func (m *OpenapiBranch) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAnnotations(formats); err != nil { + res = append(res, err) + } + + if err := m.validateClusterID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateCreateTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDeleteTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDisplayName(formats); err != nil { + res = append(res, err) + } + + if err := m.validateEndpoints(formats); err != nil { + res = append(res, err) + } + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateName(formats); err != nil { + res = append(res, err) + } + + if err := m.validateParentID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateState(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUpdateTime(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUsages(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUserPrefix(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiBranch) validateAnnotations(formats strfmt.Registry) error { + + if err := validate.Required("annotations", "body", m.Annotations); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateClusterID(formats strfmt.Registry) error { + + if err := validate.Required("cluster_id", "body", m.ClusterID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateCreateTime(formats strfmt.Registry) error { + + if err := validate.Required("create_time", "body", m.CreateTime); err != nil { + return err + } + + if err := validate.FormatOf("create_time", "body", "date-time", m.CreateTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateDeleteTime(formats strfmt.Registry) error { + + if err := validate.Required("delete_time", "body", m.DeleteTime); err != nil { + return err + } + + if err := validate.FormatOf("delete_time", "body", "date-time", m.DeleteTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateDisplayName(formats strfmt.Registry) error { + + if err := validate.Required("display_name", "body", m.DisplayName); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateEndpoints(formats strfmt.Registry) error { + + if err := validate.Required("endpoints", "body", m.Endpoints); err != nil { + return err + } + + if m.Endpoints != nil { + if err := m.Endpoints.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("endpoints") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("endpoints") + } + return err + } + } + + return nil +} + +func (m *OpenapiBranch) validateID(formats strfmt.Registry) error { + + if err := validate.Required("id", "body", m.ID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateName(formats strfmt.Registry) error { + + if err := validate.Required("name", "body", m.Name); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateParentID(formats strfmt.Registry) error { + + if err := validate.Required("parent_id", "body", m.ParentID); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateState(formats strfmt.Registry) error { + + if err := validate.Required("state", "body", m.State); err != nil { + return err + } + + if err := validate.Required("state", "body", m.State); err != nil { + return err + } + + if m.State != nil { + if err := m.State.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("state") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("state") + } + return err + } + } + + return nil +} + +func (m *OpenapiBranch) validateUpdateTime(formats strfmt.Registry) error { + + if err := validate.Required("update_time", "body", m.UpdateTime); err != nil { + return err + } + + if err := validate.FormatOf("update_time", "body", "date-time", m.UpdateTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (m *OpenapiBranch) validateUsages(formats strfmt.Registry) error { + + if err := validate.Required("usages", "body", m.Usages); err != nil { + return err + } + + if m.Usages != nil { + if err := m.Usages.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("usages") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("usages") + } + return err + } + } + + return nil +} + +func (m *OpenapiBranch) validateUserPrefix(formats strfmt.Registry) error { + + if err := validate.Required("user_prefix", "body", m.UserPrefix); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this openapi branch based on the context it is used +func (m *OpenapiBranch) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateEndpoints(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateState(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateUsages(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiBranch) contextValidateEndpoints(ctx context.Context, formats strfmt.Registry) error { + + if m.Endpoints != nil { + + if err := m.Endpoints.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("endpoints") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("endpoints") + } + return err + } + } + + return nil +} + +func (m *OpenapiBranch) contextValidateState(ctx context.Context, formats strfmt.Registry) error { + + if m.State != nil { + + if err := m.State.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("state") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("state") + } + return err + } + } + + return nil +} + +func (m *OpenapiBranch) contextValidateUsages(ctx context.Context, formats strfmt.Registry) error { + + if m.Usages != nil { + + if err := m.Usages.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("usages") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("usages") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiBranch) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiBranch) UnmarshalBinary(b []byte) error { + var res OpenapiBranch + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_branch_state.go b/pkg/tidbcloud/branch/models/openapi_branch_state.go new file mode 100644 index 00000000..8c8b27f6 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_branch_state.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" +) + +// OpenapiBranchState openapi branch state +// +// swagger:model openapiBranchState +type OpenapiBranchState string + +func NewOpenapiBranchState(value OpenapiBranchState) *OpenapiBranchState { + return &value +} + +// Pointer returns a pointer to a freshly-allocated OpenapiBranchState. +func (m OpenapiBranchState) Pointer() *OpenapiBranchState { + return &m +} + +const ( + + // OpenapiBranchStateCREATING captures enum value "CREATING" + OpenapiBranchStateCREATING OpenapiBranchState = "CREATING" + + // OpenapiBranchStateREADY captures enum value "READY" + OpenapiBranchStateREADY OpenapiBranchState = "READY" + + // OpenapiBranchStateDELETING captures enum value "DELETING" + OpenapiBranchStateDELETING OpenapiBranchState = "DELETING" + + // OpenapiBranchStateMAINTENANCE captures enum value "MAINTENANCE" + OpenapiBranchStateMAINTENANCE OpenapiBranchState = "MAINTENANCE" +) + +// for schema +var openapiBranchStateEnum []interface{} + +func init() { + var res []OpenapiBranchState + if err := json.Unmarshal([]byte(`["CREATING","READY","DELETING","MAINTENANCE"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + openapiBranchStateEnum = append(openapiBranchStateEnum, v) + } +} + +func (m OpenapiBranchState) validateOpenapiBranchStateEnum(path, location string, value OpenapiBranchState) error { + if err := validate.EnumCase(path, location, value, openapiBranchStateEnum, true); err != nil { + return err + } + return nil +} + +// Validate validates this openapi branch state +func (m OpenapiBranchState) Validate(formats strfmt.Registry) error { + var res []error + + // value enum + if err := m.validateOpenapiBranchStateEnum("", "body", m); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validates this openapi branch state based on context it is used +func (m OpenapiBranchState) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_create_branch_resp.go b/pkg/tidbcloud/branch/models/openapi_create_branch_resp.go new file mode 100644 index 00000000..a044f2ad --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_create_branch_resp.go @@ -0,0 +1,72 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// OpenapiCreateBranchResp CreateBranchResp is the response message for CreateBranch. +// +// swagger:model openapiCreateBranchResp +type OpenapiCreateBranchResp struct { + + // The ID of the branch. + // Example: branch-SDUAOISD + // Required: true + ID *string `json:"id"` +} + +// Validate validates this openapi create branch resp +func (m *OpenapiCreateBranchResp) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateID(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiCreateBranchResp) validateID(formats strfmt.Registry) error { + + if err := validate.Required("id", "body", m.ID); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this openapi create branch resp based on context it is used +func (m *OpenapiCreateBranchResp) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiCreateBranchResp) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiCreateBranchResp) UnmarshalBinary(b []byte) error { + var res OpenapiCreateBranchResp + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_endpoints.go b/pkg/tidbcloud/branch/models/openapi_endpoints.go new file mode 100644 index 00000000..97c85740 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_endpoints.go @@ -0,0 +1,109 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiEndpoints Endpoints is the endpoint of the branch. +// +// swagger:model openapiEndpoints +type OpenapiEndpoints struct { + + // The public endpoint of the branch. + PublicEndpoint *OpenapiPublicEndpoint `json:"public_endpoint,omitempty"` +} + +// Validate validates this openapi endpoints +func (m *OpenapiEndpoints) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validatePublicEndpoint(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiEndpoints) validatePublicEndpoint(formats strfmt.Registry) error { + if swag.IsZero(m.PublicEndpoint) { // not required + return nil + } + + if m.PublicEndpoint != nil { + if err := m.PublicEndpoint.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("public_endpoint") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("public_endpoint") + } + return err + } + } + + return nil +} + +// ContextValidate validate this openapi endpoints based on the context it is used +func (m *OpenapiEndpoints) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidatePublicEndpoint(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiEndpoints) contextValidatePublicEndpoint(ctx context.Context, formats strfmt.Registry) error { + + if m.PublicEndpoint != nil { + + if swag.IsZero(m.PublicEndpoint) { // not required + return nil + } + + if err := m.PublicEndpoint.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("public_endpoint") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("public_endpoint") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiEndpoints) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiEndpoints) UnmarshalBinary(b []byte) error { + var res OpenapiEndpoints + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_list_branches_resp.go b/pkg/tidbcloud/branch/models/openapi_list_branches_resp.go new file mode 100644 index 00000000..91b8389b --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_list_branches_resp.go @@ -0,0 +1,141 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// OpenapiListBranchesResp ListBranchesResp is the response of ListBranches. +// +// swagger:model openapiListBranchesResp +type OpenapiListBranchesResp struct { + + // The items of branches in the cluster. + // Required: true + Branches []*OpenapiBasicBranch `json:"branches"` + + // The total number of branches in the cluster. + // Required: true + Total *int64 `json:"total"` +} + +// Validate validates this openapi list branches resp +func (m *OpenapiListBranchesResp) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateBranches(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTotal(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiListBranchesResp) validateBranches(formats strfmt.Registry) error { + + if err := validate.Required("branches", "body", m.Branches); err != nil { + return err + } + + for i := 0; i < len(m.Branches); i++ { + if swag.IsZero(m.Branches[i]) { // not required + continue + } + + if m.Branches[i] != nil { + if err := m.Branches[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("branches" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("branches" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *OpenapiListBranchesResp) validateTotal(formats strfmt.Registry) error { + + if err := validate.Required("total", "body", m.Total); err != nil { + return err + } + + return nil +} + +// ContextValidate validate this openapi list branches resp based on the context it is used +func (m *OpenapiListBranchesResp) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateBranches(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiListBranchesResp) contextValidateBranches(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Branches); i++ { + + if m.Branches[i] != nil { + + if swag.IsZero(m.Branches[i]) { // not required + return nil + } + + if err := m.Branches[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("branches" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("branches" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiListBranchesResp) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiListBranchesResp) UnmarshalBinary(b []byte) error { + var res OpenapiListBranchesResp + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_private_endpoint.go b/pkg/tidbcloud/branch/models/openapi_private_endpoint.go new file mode 100644 index 00000000..c9be8b24 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_private_endpoint.go @@ -0,0 +1,169 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiPrivateEndpoint PrivateEndpoint is the private endpoint of the branch. +// +// swagger:model openapiPrivateEndpoint +type OpenapiPrivateEndpoint struct { + + // The provider of the private endpoint is AWS. + Aws *OpenapiPrivateEndpointAws `json:"aws,omitempty"` + + // The disabled of the private endpoint. + Disabled bool `json:"disabled,omitempty"` + + // The provider of the private endpoint is GCP. + Gcp *OpenapiPrivateEndpointGcp `json:"gcp,omitempty"` + + // The host of the private endpoint. + Host string `json:"host,omitempty"` + + // The port of the private endpoint. + Port int32 `json:"port,omitempty"` +} + +// Validate validates this openapi private endpoint +func (m *OpenapiPrivateEndpoint) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateAws(formats); err != nil { + res = append(res, err) + } + + if err := m.validateGcp(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiPrivateEndpoint) validateAws(formats strfmt.Registry) error { + if swag.IsZero(m.Aws) { // not required + return nil + } + + if m.Aws != nil { + if err := m.Aws.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("aws") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("aws") + } + return err + } + } + + return nil +} + +func (m *OpenapiPrivateEndpoint) validateGcp(formats strfmt.Registry) error { + if swag.IsZero(m.Gcp) { // not required + return nil + } + + if m.Gcp != nil { + if err := m.Gcp.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("gcp") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("gcp") + } + return err + } + } + + return nil +} + +// ContextValidate validate this openapi private endpoint based on the context it is used +func (m *OpenapiPrivateEndpoint) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateAws(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateGcp(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *OpenapiPrivateEndpoint) contextValidateAws(ctx context.Context, formats strfmt.Registry) error { + + if m.Aws != nil { + + if swag.IsZero(m.Aws) { // not required + return nil + } + + if err := m.Aws.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("aws") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("aws") + } + return err + } + } + + return nil +} + +func (m *OpenapiPrivateEndpoint) contextValidateGcp(ctx context.Context, formats strfmt.Registry) error { + + if m.Gcp != nil { + + if swag.IsZero(m.Gcp) { // not required + return nil + } + + if err := m.Gcp.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("gcp") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("gcp") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiPrivateEndpoint) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiPrivateEndpoint) UnmarshalBinary(b []byte) error { + var res OpenapiPrivateEndpoint + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_private_endpoint_aws.go b/pkg/tidbcloud/branch/models/openapi_private_endpoint_aws.go new file mode 100644 index 00000000..08228804 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_private_endpoint_aws.go @@ -0,0 +1,53 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiPrivateEndpointAws PrivateEndpointAws is the private endpoint of the branch on AWS. +// +// swagger:model openapiPrivateEndpointAws +type OpenapiPrivateEndpointAws struct { + + // availability zone + AvailabilityZone []string `json:"availability_zone"` + + // The service name of the branch on AWS. + ServiceMame string `json:"service_mame,omitempty"` +} + +// Validate validates this openapi private endpoint aws +func (m *OpenapiPrivateEndpointAws) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this openapi private endpoint aws based on context it is used +func (m *OpenapiPrivateEndpointAws) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiPrivateEndpointAws) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiPrivateEndpointAws) UnmarshalBinary(b []byte) error { + var res OpenapiPrivateEndpointAws + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_private_endpoint_gcp.go b/pkg/tidbcloud/branch/models/openapi_private_endpoint_gcp.go new file mode 100644 index 00000000..f36523d7 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_private_endpoint_gcp.go @@ -0,0 +1,50 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiPrivateEndpointGcp PrivateEndpointGcp is the private endpoint of the branch on GCP. +// +// swagger:model openapiPrivateEndpointGcp +type OpenapiPrivateEndpointGcp struct { + + // The service name of the branch on GCP. + TargetServiceAttachment string `json:"target_service_attachment,omitempty"` +} + +// Validate validates this openapi private endpoint gcp +func (m *OpenapiPrivateEndpointGcp) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this openapi private endpoint gcp based on context it is used +func (m *OpenapiPrivateEndpointGcp) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiPrivateEndpointGcp) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiPrivateEndpointGcp) UnmarshalBinary(b []byte) error { + var res OpenapiPrivateEndpointGcp + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_public_endpoint.go b/pkg/tidbcloud/branch/models/openapi_public_endpoint.go new file mode 100644 index 00000000..ffec69c1 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_public_endpoint.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiPublicEndpoint PublicEndpoint is the public endpoint of the branch. +// +// swagger:model openapiPublicEndpoint +type OpenapiPublicEndpoint struct { + + // The disabled of the public endpoint. + Disabled bool `json:"disabled,omitempty"` + + // The host of the public endpoint. + Host string `json:"host,omitempty"` + + // The port of the public endpoint. + Port int32 `json:"port,omitempty"` +} + +// Validate validates this openapi public endpoint +func (m *OpenapiPublicEndpoint) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this openapi public endpoint based on context it is used +func (m *OpenapiPublicEndpoint) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiPublicEndpoint) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiPublicEndpoint) UnmarshalBinary(b []byte) error { + var res OpenapiPublicEndpoint + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/openapi_usages.go b/pkg/tidbcloud/branch/models/openapi_usages.go new file mode 100644 index 00000000..b59f9240 --- /dev/null +++ b/pkg/tidbcloud/branch/models/openapi_usages.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// OpenapiUsages Usages is the usages of the branch. +// +// swagger:model openapiUsages +type OpenapiUsages struct { + + // The column storage of the branch. + ColumnStorage string `json:"column_storage,omitempty"` + + // The request unit of the branch. + RequestUnit string `json:"request_unit,omitempty"` + + // The storage of the branch. + RowStorage string `json:"row_storage,omitempty"` +} + +// Validate validates this openapi usages +func (m *OpenapiUsages) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this openapi usages based on context it is used +func (m *OpenapiUsages) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *OpenapiUsages) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *OpenapiUsages) UnmarshalBinary(b []byte) error { + var res OpenapiUsages + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/tidbcloud/branch/models/protobuf_any.go b/pkg/tidbcloud/branch/models/protobuf_any.go new file mode 100644 index 00000000..cbc97997 --- /dev/null +++ b/pkg/tidbcloud/branch/models/protobuf_any.go @@ -0,0 +1,50 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// ProtobufAny protobuf any +// +// swagger:model protobufAny +type ProtobufAny struct { + + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this protobuf any +func (m *ProtobufAny) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this protobuf any based on context it is used +func (m *ProtobufAny) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *ProtobufAny) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ProtobufAny) UnmarshalBinary(b []byte) error { + var res ProtobufAny + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +}