syntax = "proto3";

package tidb_cloud_open_api.dedicated.v1beta1;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
// buf:lint:ignore IMPORT_USED
import "google/rpc/status.proto"; // This is used by `responses.value.schema.json_schema.ref` in the swagger definition.
import "protoc-gen-openapiv2/options/annotations.proto";
import "tidb_cloud_open_api/common/v1beta1/clusterstate.proto";
import "tidb_cloud_open_api/common/v1beta1/region.proto";
import "tidb_cloud_open_api/common/v1beta1/tag.proto";
import "tidb_cloud_open_api/dedicated/v1beta1/resources.proto";

option go_package = "tidb_cloud_open_api/dedicated/v1beta1";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
  swagger: "2.0"
  host: "dedicated.tidbapi.com"
  base_path: "/v1beta1"
  info: {
    version: "v1beta1"
    title: "TiDB Cloud Dedicated API"
    description: "*TiDB Cloud API is in beta.*\n\nThis API manages [TiDB Cloud Dedicated](https://docs.pingcap.com/tidbcloud/select-cluster-tier/#tidb-cloud-dedicated) clusters. For TiDB Cloud Starter or TiDB Cloud Essential clusters, use the [TiDB Cloud Starter and Essential API](). For more information about TiDB Cloud API, see [TiDB Cloud API Overview](https://docs.pingcap.com/tidbcloud/api-overview/).\n\n# Overview\n\nThe TiDB Cloud API is a [REST interface](https://en.wikipedia.org/wiki/Representational_state_transfer) that provides you with programmatic access to manage clusters and related resources within TiDB Cloud.\n\nThe API has the following features:\n\n- **JSON entities.** All entities are expressed in JSON.\n- **HTTPS-only.** You can only access the API via HTTPS, ensuring all the data sent over the network is encrypted with TLS.\n- **Key-based access and digest authentication.** Before you access TiDB Cloud API, you must generate an API key. All requests are authenticated through [HTTP Digest Authentication](https://en.wikipedia.org/wiki/Digest_access_authentication), ensuring the API key is never sent over the network.\n\n# Get Started\n\nThis guide helps you make your first API call to TiDB Cloud API. You'll learn how to authenticate a request, build a request, and interpret the response.\n\n## Prerequisites\n\nTo complete this guide, you need to perform the following tasks:\n\n- Create a [TiDB Cloud account](https://tidbcloud.com/free-trial)\n- Install [curl](https://curl.se/)\n\n## Step 1. Create an API key\n\nTo create an API key, log in to your TiDB Cloud console. Navigate to the [**API Keys**](https://tidbcloud.com/org-settings/api-keys) page of your organization, and create an API key.\n\nAn API key contains a public key and a private key. Copy and save them in a secure location. You will need to use the API key later in this guide.\n\nFor more details about creating API key, refer to [API Key Management](#section/Authentication/API-Key-Management).\n\n## Step 2. Make your first API call\n\n### Build an API call\n\nTiDB Cloud API call consists of the following components:\n\n- **A host**. The host for TiDB Cloud API is <https://dedicated.tidbapi.com>.\n- **An API Key**. The public key and the private key are required for authentication.\n- **A request**. When submitting data to a resource via `POST`, `PATCH`, or `PUT`, you must submit your payload in JSON.\n\nIn this guide, you call the [List clusters](#tag/Cluster/operation/ClusterService_ListClusters) endpoint. For the detailed description of the endpoint, see the [API reference](#tag/Cluster/operation/ClusterService_ListClusters).\n\n### Call an API endpoint\n\nTo get all clusters in your organization, run the following command in your terminal. Remember to change `YOUR_PUBLIC_KEY` to your public key and `YOUR_PRIVATE_KEY` to your private key.\n\n```shell\ncurl --digest \\\n --user 'YOUR_PUBLIC_KEY:YOUR_PRIVATE_KEY' \\\n --request GET \\\n --url 'https://dedicated.tidbapi.com/v1beta1/clusters'\n```\n\n## Step 3. Check the response\n\nAfter making the API call, if the status code in response is `200` and you see details about all clusters in your organization, your request is successful.\n\n# Authentication\n\nThe TiDB Cloud API uses [HTTP Digest Authentication](https://en.wikipedia.org/wiki/Digest_access_authentication). It protects your private key from being sent over the network. For more details about HTTP Digest Authentication, refer to the [IETF RFC](https://datatracker.ietf.org/doc/html/rfc7616).\n\n## API key overview\n\n- 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.\n- The API key belongs to your organization and acts as the `Organization Owner` role. You can check [permissions of owner](https://docs.pingcap.com/tidbcloud/manage-user-access#configure-member-roles).\n- You must provide the correct API key in every request. Otherwise, the TiDB Cloud responds with a `401` error.\n\n## API key management\n\n### Create an API key\n\nOnly the **owner** of an organization can create an API key.\n\nTo create an API key in an organization, perform the following steps:\n\n1. In the [TiDB Cloud console](https://tidbcloud.com), switch to your target organization using the combo box in the upper-left corner.\n2. In the left navigation pane, click **Organization Settings** > **API Keys**.\n3. On the **API Keys** page, click **Create API Key**.\n4. Enter a description for your API key. The role of the API key is always `Organization Owner` currently.\n5. Click **Next**. Copy and save the public key and the private key.\n6. Make sure that you have copied and saved the private key in a secure location. The private key only displays upon the creation. After leaving this page, you will not be able to get the full private key again.\n7. Click **Done**.\n\n### View details of an API key\n\nTo view details of an API key, perform the following steps:\n\n1. In the [TiDB Cloud console](https://tidbcloud.com), switch to your target organization using the combo box in the upper-left corner.\n2. In the left navigation pane, click **Organization Settings** > **API Keys**.\n3. You can view the details of the API keys on the page.\n\n### Edit an API key\n\nOnly the **owner** of an organization can modify an API key.\n\nTo edit an API key in an organization, perform the following steps:\n\n1. In the [TiDB Cloud console](https://tidbcloud.com), switch to your target organization using the combo box in the upper-left corner.\n2. In the left navigation pane, click **Organization Settings** > **API Keys**.\n3. On the **API Keys** page, click **...** in the API key row that you want to change, and then click **Edit**.\n4. You can update the API key description.\n5. Click **Update**.\n\n### Delete an API key\n\nOnly the **owner** of an organization can delete an API key.\n\nTo delete an API key in an organization, perform the following steps:\n\n1. In the [TiDB Cloud console](https://tidbcloud.com), switch to your target organization using the combo box in the upper-left corner.\n2. In the left navigation pane, click **Organization Settings** > **API Keys**.\n3. On the **API Keys** page, click **...** in the API key row that you want to delete, and then click **Delete**.\n4. Click **I understand, delete it.**\n\n# Rate Limiting\n\nThe TiDB Cloud API allows up to 100 requests per minute per API key. If you exceed the rate limit, the API returns a `429` error. For more quota, you can [submit a request](https://support.pingcap.com/hc/en-us/requests/new?ticket_form_id=7800003722519) to contact our support team.\n\nEach API request returns the following headers about the limit.\n\n- `X-Ratelimit-Limit-Minute`: The number of requests allowed per minute. It is 100 currently.\n- `X-Ratelimit-Remaining-Minute`: The number of remaining requests in the current minute. When it reaches `0`, the API returns a `429` error and indicates that you exceed the rate limit.\n- `X-Ratelimit-Reset`: The time in seconds at which the current rate limit resets.\n\nIf you exceed the rate limit, an error response returns like this.\n\n```\n> HTTP/2 429\n> date: Fri, 22 Jul 2022 05:28:37 GMT\n> content-type: application/json\n> content-length: 66\n> x-ratelimit-reset: 23\n> x-ratelimit-remaining-minute: 0\n> x-ratelimit-limit-minute: 100\n> x-kong-response-latency: 2\n> server: kong/2.8.1\n\n> {\"details\":[],\"code\":49900007,\"message\":\"The request exceeded the limit of 100 times per apikey per minute. For more quota, please contact us: https://support.pingcap.com/hc/en-us/requests/new?ticket_form_id=7800003722519\"}\n```\n\n# API Changelog\n\nThis changelog lists all changes to the TiDB Cloud API.\n\n<!-- In reverse chronological order -->\n\n## 20250812\n\n- Initial release of the TiDB Cloud Dedicated API, including the following resources and endpoints:\n\t* Cluster\r\n\t\t* [List clusters](#tag/Cluster/operation/ClusterService_ListClusters)\r\n\t\t* [Create a cluster](#tag/Cluster/operation/ClusterService_CreateCluster)\r\n\t\t* [Get a cluster](#tag/Cluster/operation/ClusterService_GetCluster)\r\n\t\t* [Delete a cluster](#tag/Cluster/operation/ClusterService_DeleteCluster)\r\n\t\t* [Update a cluster](#tag/Cluster/operation/ClusterService_UpdateCluster)\r\n\t\t* [Pause a cluster](#tag/Cluster/operation/ClusterService_PauseCluster)\r\n\t\t* [Resume a cluster](#tag/Cluster/operation/ClusterService_ResumeCluster)\r\n\t\t* [Reset the root password of a cluster](#tag/Cluster/operation/ClusterService_ResetRootPassword)\r\n\t\t* [List node quotas for your organization](#tag/Cluster/operation/ClusterService_ShowNodeQuota)\r\n\t\t* [Get log redaction policy](#tag/Cluster/operation/ClusterService_GetLogRedactionPolicy)\r\n\t* Region\r\n\t\t* [List regions](#tag/Region/operation/RegionService_ListRegions)\r\n\t\t* [Get a region](#tag/Region/operation/RegionService_GetRegion)\r\n\t\t* [List cloud providers](#tag/Region/operation/RegionService_ShowCloudProviders)\r\n\t\t* [List node specs](#tag/Region/operation/RegionService_ListNodeSpecs)\r\n\t\t* [Get a node spec](#tag/Region/operation/RegionService_GetNodeSpec)\r\n\t* Private Endpoint Connection\r\n\t\t* [Get private link service for a TiDB node group](#tag/Private-Endpoint-Connection/operation/PrivateEndpointConnectionService_GetPrivateLinkService)\r\n\t\t* [Create a private endpoint connection](#tag/Private-Endpoint-Connection/operation/PrivateEndpointConnectionService_CreatePrivateEndpointConnection)\r\n\t\t* [List private endpoint connections](#tag/Private-Endpoint-Connection/operation/PrivateEndpointConnectionService_ListPrivateEndpointConnections)\r\n\t\t* [Get a private endpoint connection](#tag/Private-Endpoint-Connection/operation/PrivateEndpointConnectionService_GetPrivateEndpointConnection)\r\n\t\t* [Delete a private endpoint connection](#tag/Private-Endpoint-Connection/operation/PrivateEndpointConnectionService_DeletePrivateEndpointConnection)\r\n\t* Import\r\n\t\t* [List import tasks](#tag/Import/operation/ListImports)\r\n\t\t* [Create an import task](#tag/Import/operation/CreateImport)\r\n\t\t* [Get an import task](#tag/Import/operation/GetImport)\r\n\t\t* [Cancel an import task](#tag/Import/operation/CancelImport)\r\n"
  }
  schemes: HTTPS
  consumes: "application/json"
  produces: "application/json"

  // Define the common error responses.
  responses: {
    key: "400"
    value: {
      description: "A request field is invalid."
      schema: {
        json_schema: {ref: ".google.rpc.Status"}
      }
    }
  }
  responses: {
    key: "401"
    value: {
      description: "The API key cannot be authenticated."
      schema: {
        json_schema: {ref: ".google.rpc.Status"}
      }
    }
  }
  responses: {
    key: "403"
    value: {
      description: "The API key does not have permission to access the resource."
      schema: {
        json_schema: {ref: ".google.rpc.Status"}
      }
    }
  }
  responses: {
    key: "429"
    value: {
      description: "You have exceed the rate limit."
      schema: {
        json_schema: {ref: ".google.rpc.Status"}
      }
    }
  }
  responses: {
    key: "500"
    value: {
      description: "Internal server error."
      schema: {
        json_schema: {ref: ".google.rpc.Status"}
      }
    }
  }
};

// ClusterService provides APIs for managing the lifecycle of TiDB Cloud Dedicated clusters.
service ClusterService {
  rpc CreateCluster(CreateClusterRequest) returns (Cluster) {
    option (google.api.http) = {
      post: "/clusters"
      body: "cluster"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Create a cluster"
      description: "Creates a new [TiDB Cloud Dedicated](https://docs.pingcap.com/tidbcloud/select-cluster-tier/#tidb-cloud-dedicated) cluster. Before creating a TiDB Cloud Dedicated cluster, you must set a [Project CIDR on TiDB Cloud console](https://docs.pingcap.com/tidbcloud/set-up-vpc-peering-connections/#prerequisite-set-a-cidr-for-a-region)."
    };
  }

  rpc ListClusters(ListClustersRequest) returns (ListClustersResponse) {
    option (google.api.http) = {get: "/clusters"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List clusters"
      description: "Lists all clusters in your organization. You can filter results by project, region, cluster state, or specific cluster IDs."
    };
  }

  rpc GetCluster(GetClusterRequest) returns (Cluster) {
    option (google.api.http) = {get: "/clusters/{cluster_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get a cluster"
      description: "Retrieves details of a specific cluster."
    };
  }

  rpc BindTags(BindTagsRequest) returns (BindTagsResponse) {
    option (google.api.http) = {
      post: "/clusters/{cluster_id}:bindTags"
      body: "*"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Bind tags to a cluster"
      description: "Binds full key:value tags to a specific cluster."
    };
  }

  rpc UnbindTags(UnbindTagsRequest) returns (UnbindTagsResponse) {
    option (google.api.http) = {
      post: "/clusters/{cluster_id}:unbindTags"
      body: "*"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Unbind tags from a cluster"
      description: "Unbinds full key:value tags from a specific cluster."
    };
  }

  rpc DeleteCluster(DeleteClusterRequest) returns (Cluster) {
    option (google.api.http) = {delete: "/clusters/{cluster_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Delete a cluster"
      description: "Deletes a cluster by `clusterId`."
    };
  }

  rpc UpdateCluster(UpdateClusterRequest) returns (Cluster) {
    option (google.api.http) = {
      patch: "/clusters/{cluster.cluster_id}"
      body: "cluster"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Update a cluster"
      description: "Updates the configuration of a specific cluster. You can modify the following fields: `tidbNodeSetting`, `tikvNodeSetting`, `tiflashNodeSetting` and `displayName`."
    };
  }

  rpc PauseCluster(PauseClusterRequest) returns (PauseClusterResponse) {
    option (google.api.http) = {post: "/clusters/{cluster_id}:pauseCluster"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Pause a cluster"
      description: "Pauses a cluster by ID."
    };
  }

  rpc ResumeCluster(ResumeClusterRequest) returns (ResumeClusterResponse) {
    option (google.api.http) = {post: "/clusters/{cluster_id}:resumeCluster"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Resume a cluster"
      description: "Resumes a paused cluster. Only clusters in the `PAUSED` state can be resumed."
    };
  }

  rpc ResetRootPassword(ResetRootPasswordRequest) returns (ResetRootPasswordResponse) {
    option (google.api.http) = {
      post: "/clusters/{cluster_id}:resetRootPassword"
      body: "*"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Reset the root password of a cluster"
      description: "Sets a new password for the cluster's root user. The new password is not returned in the response for security reasons, so save your password in a secure location.\n\nThe cluster must be in the `READY` state. If the cluster is not `READY`, wait until it is before retrying."
    };
  }

  rpc ListNodeInstances(ListNodeInstancesRequest) returns (ListNodeInstancesResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/nodeInstances"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List node instances in a cluster"
      description: "Lists node instances in a cluster."
    };
  }

  rpc GetNodeInstance(GetNodeInstanceRequest) returns (NodeInstance) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/nodeInstances/{instance_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get a node instance"
      description: "Retrieves a node instance in a cluster."
    };
  }

  rpc ShowNodeQuota(ShowNodeQuotaRequest) returns (ShowNodeQuotaResponse) {
    option (google.api.http) = {get: "/clusters:showNodeQuota"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List node quotas for your organization"
      description: "Lists the node quotas for your organization, including the maximum number of nodes allowed for each component type."
    };
  }

  rpc GetLogRedactionPolicy(GetLogRedactionPolicyRequest) returns (LogRedactionPolicy) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/logRedactionPolicy"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get log redaction policy"
      description: "Gets the log redaction policy for a cluster."
    };
  }

  rpc UpdateLogRedactionPolicy(UpdateLogRedactionPolicyRequest) returns (LogRedactionPolicy) {
    option (google.api.http) = {
      patch: "/clusters/{log_redaction_policy.cluster_id}/logRedactionPolicy"
      body: "log_redaction_policy"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Update log redaction policy"
      description: "Updates the log redaction policy for the specified cluster. The log redaction policy determines whether sensitive information is redacted from logs. The default policy is to redact sensitive information from logs."
    };
  }
}

service RegionService {
  rpc ListRegions(ListRegionsRequest) returns (ListRegionsResponse) {
    option (google.api.http) = {get: "/regions"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List regions"
      description: "Lists the regions where you can create a cluster, across all supported cloud providers."
    };
  }

  rpc GetRegion(GetRegionRequest) returns (common.v1beta1.Region) {
    option (google.api.http) = {get: "/regions/{region_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get a region"
      description: "Retrieves details of a specific region by region ID."
    };
  }

  rpc ShowCloudProviders(ShowCloudProvidersRequest) returns (ShowCloudProvidersResponse) {
    option (google.api.http) = {get: "/regions:showCloudProviders"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List cloud providers"
      description: "Lists the cloud providers available for creating a cluster."
    };
  }

  rpc ListNodeSpecs(ListNodeSpecsRequest) returns (ListNodeSpecsResponse) {
    option (google.api.http) = {get: "/regions/{region_id}/nodeSpecs"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List node specs"
      description: "Retrieves a paginated list of node specifications (specs) available for creating or scaling a cluster in the specified region. You can filter the results using the `componentType`, `projectId`, or `clusterId` parameter."
    };
  }

  rpc GetNodeSpec(GetNodeSpecRequest) returns (NodeSpec) {
    option (google.api.http) = {get: "/regions/{region_id}/componentTypes/{component_type}/nodeSpecs/{node_spec_key}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get a node spec"
      description: "Retrieves details of a node spec used for creating or scaling a cluster."
    };
  }
}

service TidbNodeGroupService {
  rpc CreateTidbNodeGroup(CreateTidbNodeGroupRequest) returns (TidbNodeGroup) {
    option (google.api.http) = {
      post: "/clusters/{tidb_node_group.cluster_id}/tidbNodeGroups"
      body: "tidb_node_group"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Create a TiDB Node Group"
      description: "Creates a new TiDB Node Group in the specified cluster. The TiDB Node Group is used to scale the TiDB nodes in the cluster."
    };
  }

  rpc ListTidbNodeGroups(ListTidbNodeGroupsRequest) returns (ListTidbNodeGroupsResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List TiDB Node Groups"};
  }

  rpc GetTidbNodeGroup(GetTidbNodeGroupRequest) returns (TidbNodeGroup) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get a TiDB Node Group"};
  }

  rpc DeleteTidbNodeGroup(DeleteTidbNodeGroupRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Delete a TiDB Node Group"};
  }

  rpc UpdateTidbNodeGroup(UpdateTidbNodeGroupRequest) returns (TidbNodeGroup) {
    option (google.api.http) = {
      patch: "/clusters/{tidb_node_group.cluster_id}/tidbNodeGroups/{tidb_node_group.tidb_node_group_id}"
      body: "tidb_node_group"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Update a TiDB Node Group"};
  }

  rpc GetPublicEndpointSetting(GetPublicEndpointSettingRequest) returns (PublicEndpointSetting) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/publicEndpointSetting"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get the public endpoint setting of a TiDB Node Group"};
  }

  rpc UpdatePublicEndpointSetting(UpdatePublicEndpointSettingRequest) returns (PublicEndpointSetting) {
    option (google.api.http) = {
      patch: "/clusters/{cluster_id}/tidbNodeGroups/{public_endpoint_setting.tidb_node_group_id}/publicEndpointSetting"
      body: "public_endpoint_setting"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Update the public endpoint setting of a TiDB Node Group"};
  }

  rpc GetPrivateLinkIPAddressTypeSetting(GetPrivateLinkIPAddressTypeSettingRequest) returns (PrivateLinkIPAddressTypeSetting) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/privateLinkIpAddressTypeSetting"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get the PrivateLink IP address type setting of a TiDB Node Group"
      description: "Returns the desired, observed, and asynchronous state of the PrivateLink address capability for an AWS EKS TiDB Node Group."
      responses: {
        key: "404"
        value: {
          description: "The cluster or TiDB Node Group was not found."
          schema: {
            json_schema: {ref: ".google.rpc.Status"}
          }
        }
      }
      responses: {
        key: "412"
        value: {
          description: "The PrivateLink IP address type setting preconditions are not met."
          schema: {
            json_schema: {ref: ".google.rpc.Status"}
          }
        }
      }
    };
  }

  // (-- api-linter: core::0134::http-uri-name=disabled
  //     aip.dev/not-precedent: The public URI keeps the TiDB Node Group ID outside the body resource so GET and PATCH share one OpenAPI path template. --)
  rpc UpdatePrivateLinkIPAddressTypeSetting(UpdatePrivateLinkIPAddressTypeSettingRequest) returns (PrivateLinkIPAddressTypeSetting) {
    option (google.api.http) = {
      patch: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/privateLinkIpAddressTypeSetting"
      body: "private_link_ip_address_type_setting"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Update the PrivateLink IP address type setting of a TiDB Node Group"
      description: "Updates the desired IP address type and returns the asynchronous setting. A successful response means that the update was accepted, not that the underlying AWS resources are ready. Poll the Get method while state is CHANGING. Switching to IPv4 is allowed only after all IPv6 or dualstack PrivateLink endpoint connections for the TiDB Node Group have been deleted."
      responses: {
        key: "404"
        value: {
          description: "The cluster or TiDB Node Group was not found."
          schema: {
            json_schema: {ref: ".google.rpc.Status"}
          }
        }
      }
      responses: {
        key: "409"
        value: {
          description: "A conflicting PrivateLink IP address type change is already in progress. Retry the request after the current change completes."
          schema: {
            json_schema: {ref: ".google.rpc.Status"}
          }
        }
      }
      responses: {
        key: "412"
        value: {
          description: "The PrivateLink IP address type change preconditions are not met. When switching to IPv4, rejected, failed, expired, or otherwise non-deleted IPv6-capable PrivateLink endpoint connections remain blockers until AWS reports them as deleted."
          schema: {
            json_schema: {ref: ".google.rpc.Status"}
          }
        }
      }
    };
  }
}

service PrivateEndpointConnectionService {
  rpc GetPrivateLinkService(GetPrivateLinkServiceRequest) returns (PrivateLinkService) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/privateLinkService"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get private link service for a TiDB node group"
      description: "Retrieves the private link service configuration for a [TiDB node group](https://docs.pingcap.com/tidbcloud/tidb-node-group-overview/). You can the private link service to establish private endpoint connections to your TiDB cluster."
    };
  }

  rpc CreatePrivateEndpointConnection(CreatePrivateEndpointConnectionRequest) returns (PrivateEndpointConnection) {
    option (google.api.http) = {
      post: "/clusters/{cluster_id}/tidbNodeGroups/{private_endpoint_connection.tidb_node_group_id}/privateEndpointConnections"
      body: "private_endpoint_connection"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Create a private endpoint connection"
      description: "Creates a private endpoint connection for a TiDB node group. For more information, see the following documents:\n\n- [Connect via Private Endpoint with AWS](https://docs.pingcap.com/tidbcloud/set-up-private-endpoint-connections/) \n- [Connect via Private Endpoint with Azure](https://docs.pingcap.com/tidbcloud/set-up-private-endpoint-connections-on-azure/) \n- [Connect via Private Endpoint with Google Cloud](https://docs.pingcap.com/tidbcloud/set-up-private-endpoint-connections-on-google-cloud/)"
    };
  }

  rpc ListPrivateEndpointConnections(ListPrivateEndpointConnectionsRequest) returns (ListPrivateEndpointConnectionsResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id=*}/tidbNodeGroups/{tidb_node_group_id=*}/privateEndpointConnections"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "List private endpoint connections"
      description: "Lists private endpoint connections for a cluster and TiDB node group. To list private link connections across different clusters and TiDB groups, set cluster_id and tidb_node_group_id to \"-\". If you want to list private link connections under a project across different clusters and TiDB groups, set both cluster_id and tidb_node_group_id to \"-\", and pass the project_id in the query parameter."
    };
  }

  rpc GetPrivateEndpointConnection(GetPrivateEndpointConnectionRequest) returns (PrivateEndpointConnection) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/privateEndpointConnections/{private_endpoint_connection_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Get a private endpoint connection"
      description: "Retrieves a private endpoint connection for a TiDB node group."
    };
  }

  rpc DeletePrivateEndpointConnection(DeletePrivateEndpointConnectionRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/clusters/{cluster_id}/tidbNodeGroups/{tidb_node_group_id}/privateEndpointConnections/{private_endpoint_connection_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      summary: "Delete a private endpoint connection"
      description: "Deletes a private endpoint connection for a TiDB node group."
    };
  }
}

service NetworkContainerService {
  rpc CreateNetworkContainer(CreateNetworkContainerRequest) returns (NetworkContainer) {
    option (google.api.http) = {
      post: "/networkContainers"
      body: "network_container"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Create a network container"};
  }

  rpc GetNetworkContainer(GetNetworkContainerRequest) returns (NetworkContainer) {
    option (google.api.http) = {get: "/networkContainers/{network_container_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get a network container"};
  }

  rpc ListNetworkContainers(ListNetworkContainersRequest) returns (ListNetworkContainersResponse) {
    option (google.api.http) = {get: "/networkContainers"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List network containers"};
  }

  rpc DeleteNetworkContainer(DeleteNetworkContainerRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/networkContainers/{network_container_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Delete a network container"};
  }

  rpc CreateVpcPeering(CreateVpcPeeringRequest) returns (VpcPeering) {
    option (google.api.http) = {
      post: "/vpcPeerings"
      body: "vpc_peering"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Create a VPC peering"};
  }

  rpc DeleteVpcPeering(DeleteVpcPeeringRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/vpcPeerings/{vpc_peering_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Delete a VPC peering"};
  }

  rpc ListVpcPeerings(ListVpcPeeringsRequest) returns (ListVpcPeeringsResponse) {
    option (google.api.http) = {get: "/vpcPeerings"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List VPC peerings"};
  }

  rpc GetVpcPeering(GetVpcPeeringRequest) returns (VpcPeering) {
    option (google.api.http) = {get: "/vpcPeerings/{vpc_peering_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get a VPC peering"};
  }
}

// MaintenanceService provides APIs for managing the maintenance of TiDB Cloud Dedicated clusters.
service MaintenanceService {
  rpc GetMaintenanceWindow(GetMaintenanceWindowRequest) returns (MaintenanceWindow) {
    option (google.api.http) = {get: "/maintenanceWindows/{maintenance_window_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get a maintenance window"};
  }

  rpc ListMaintenanceWindows(ListMaintenanceWindowsRequest) returns (ListMaintenanceWindowsResponse) {
    option (google.api.http) = {get: "/maintenanceWindows"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List maintenance windows"};
  }

  rpc UpdateMaintenanceWindow(UpdateMaintenanceWindowRequest) returns (MaintenanceWindow) {
    option (google.api.http) = {
      patch: "/maintenanceWindows/{maintenance_window.maintenance_window_id}"
      body: "maintenance_window"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Update a maintenance window"};
  }

  rpc ListMaintenanceTasks(ListMaintenanceTasksRequest) returns (ListMaintenanceTasksResponse) {
    option (google.api.http) = {get: "/maintenanceTasks"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List maintenance tasks"};
  }

  rpc GetMaintenanceTask(GetMaintenanceTaskRequest) returns (MaintenanceTask) {
    option (google.api.http) = {get: "/maintenanceTasks/{maintenance_task_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get a maintenance task"};
  }

  rpc UpdateMaintenanceTask(UpdateMaintenanceTaskRequest) returns (MaintenanceTask) {
    option (google.api.http) = {
      patch: "/maintenanceTasks/{maintenance_task.maintenance_task_id}"
      body: "maintenance_task"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Update a maintenance task"};
  }
}

// DatabaseAuditLogService manages audit logs of SQL statements.
// Note: This feature is different from org-level audit log, which audits the cluster admin operations.
service DatabaseAuditLogService {
  rpc GetAuditLogConfig(GetAuditLogConfigRequest) returns (AuditLogConfig) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/auditLogConfig"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get the audit log config of a cluster"};
  }

  rpc UpdateAuditLogConfig(UpdateAuditLogConfigRequest) returns (AuditLogConfig) {
    option (google.api.http) = {
      patch: "/clusters/{audit_log_config.cluster_id}/auditLogConfig"
      body: "audit_log_config"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Update the audit log config of a cluster"};
  }

  rpc CreateAuditLogFilterRule(CreateAuditLogFilterRuleRequest) returns (AuditLogFilterRule) {
    option (google.api.http) = {
      post: "/clusters/{audit_log_filter_rule.cluster_id}/auditLogFilterRules"
      body: "audit_log_filter_rule"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Create an audit log filter rule"};
  }

  rpc ReplaceAuditLogFilterRule(ReplaceAuditLogFilterRuleRequest) returns (ReplaceAuditLogFilterRuleResponse) {
    option (google.api.http) = {
      post: "/clusters/{audit_log_filter_rule.cluster_id}/auditLogFilterRules/{audit_log_filter_rule.audit_log_filter_rule_id}:replaceAuditLogFilterRule"
      body: "audit_log_filter_rule"
    };
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Replace an audit log filter rule. All fields of the rule will be replaced with the provided values."};
  }

  rpc GetAuditLogFilterRule(GetAuditLogFilterRuleRequest) returns (AuditLogFilterRule) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/auditLogFilterRules/{audit_log_filter_rule_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Get an audit log filter rule"};
  }

  rpc ListAuditLogFilterRules(ListAuditLogFilterRulesRequest) returns (ListAuditLogFilterRulesResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/auditLogFilterRules"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "List audit log filter rules"};
  }

  rpc DeleteAuditLogFilterRule(DeleteAuditLogFilterRuleRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/clusters/{cluster_id}/auditLogFilterRules/{audit_log_filter_rule_id}"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Delete an audit log filter rule"};
  }

  rpc ShowObjectStorageAccessIamPrincipal(ShowObjectStorageAccessIamPrincipalRequest) returns (ShowObjectStorageAccessIamPrincipalResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/auditLogConfig:showObjectStorageAccessIamPrincipal"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Show IAM principal of TiDB Cloud for accessing customer's object storage"};
  }

  rpc QueryAuditLogFiles(QueryAuditLogFilesRequest) returns (QueryAuditLogFilesResponse) {
    option (google.api.http) = {get: "/clusters/{cluster_id}/auditLogConfig:queryAuditLogFiles"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Query audit log files"};
  }

  rpc GenerateAuditLogFileDownloadAddress(GenerateAuditLogFileDownloadAddressRequest) returns (GenerateAuditLogFileDownloadAddressResponse) {
    option (google.api.http) = {post: "/clusters/{cluster_id}/auditLogConfig:generateAuditLogFileDownloadAddress"};
    option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {summary: "Generate the download address for an audit log file, the address have an 15 minutes expiration time"};
  }
}

// CustomerManagedEncryptionKeyService manages customer-managed encryption keys.
service CustomerManagedEncryptionKeyService {
  rpc CreateCustomerManagedEncryptionKey(CreateCustomerManagedEncryptionKeyRequest) returns (CustomerManagedEncryptionKey) {
    option (google.api.http) = {
      post: "/cmeks"
      body: "customer_managed_encryption_key"
    };
  }

  rpc GetCustomerManagedEncryptionKey(GetCustomerManagedEncryptionKeyRequest) returns (CustomerManagedEncryptionKey) {
    option (google.api.http) = {get: "/cmeks/{customer_managed_encryption_key_id}"};
  }

  rpc ListCustomerManagedEncryptionKeys(ListCustomerManagedEncryptionKeysRequest) returns (ListCustomerManagedEncryptionKeysResponse) {
    option (google.api.http) = {get: "/cmeks"};
  }

  rpc DeleteCustomerManagedEncryptionKey(DeleteCustomerManagedEncryptionKeyRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {delete: "/cmeks/{customer_managed_encryption_key_id}"};
  }

  rpc ShowCmekAccessIamPrincipal(ShowCmekAccessIamPrincipalRequest) returns (ShowCmekAccessIamPrincipalResponse) {
    option (google.api.http) = {get: "/cmeks:showCmekAccessIamPrincipal"};
  }
}

message CreateClusterRequest {
  Cluster cluster = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The cluster to create."}
  ];

  // If set to `true`, the request is validated but not executed. Defaults to `false`.
  bool validate_only = 100 [(google.api.field_behavior) = OPTIONAL];
}

message ListClustersRequest {
  // The ID of the project. If not specified, the project ID of the default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  // A list of cluster IDs. If specified, only clusters matching these IDs are returned.
  repeated string cluster_ids = 2 [(google.api.field_behavior) = OPTIONAL];

  // A list of region IDs. If specified, only clusters in these regions are returned.
  repeated string region_ids = 3 [(google.api.field_behavior) = OPTIONAL];

  // (-- api-linter: core::0216::state-field-output-only=disabled
  //     aip.dev/not-precedent: Use state field as filter. --)
  repeated common.v1beta1.ClusterState cluster_states = 4 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "A list of cluster states. If specified, only clusters in these states are returned.\n\n`\"UPGRADING\"`, `\"IMPORTING\"`, `\"MODIFYING\"`, `\"PAUSING\"`, `\"PAUSED\"`, and `\"RESUMING\"` states are only available for [TiDB Cloud Dedicated](https://docs.pingcap.com/tidbcloud/select-cluster-tier/#tidb-cloud-dedicated) clusters."}
  ];

  repeated string tags = 5 [(google.api.field_behavior) = OPTIONAL];

  // When true, returns basic cluster metadata for each matching cluster.
  // When false or omitted, returns the full cluster information (default).
  optional bool global_meta_only = 6 [(google.api.field_behavior) = OPTIONAL];

  int32 page_size = 100 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      example: "10"
      default: "10"
      minimum: 1
      maximum: 100
      description: "The maximum number of clusters to return. If not specified, at most 10 clusters will be returned. The maximum value is `100`. Values greater than `100` are set to `100`."
    }
  ];

  string page_token = 101 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The pagination token received from a previous [List clusters](#tag/ClusterService/operation/ClusterService_ListClusters) request. Use this token to retrieve the next page of results.\n\n**Note**: When paginating, all other parameters must match the original request."
      example: "\"next_page_token\""
    }
  ];

  // The number of clusters to skip before returning results. If the value exceeds the total number of clusters, the response is `200` with an empty list and no `nextPageToken`.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListClustersResponse {
  // A list of clusters that match the query.
  repeated Cluster clusters = 1;

  int32 total_size = 100 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "The total number of clusters that match the query."
    example: "1"
  }];

  string next_page_token = 101 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "The token to retrieve the next page of results. Use this value as the `pageToken` parameter in the next request. This field is empty when there are no more pages."
    example: "\"next_page_token\""
  }];
}

message GetClusterRequest {
  // The ID of the cluster.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // When true, returns basic cluster metadata only.
  // When false or omitted, returns the full cluster information (default).
  optional bool global_meta_only = 2 [(google.api.field_behavior) = OPTIONAL];
}

message BindTagsRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  repeated string tags = 2 [(google.api.field_behavior) = REQUIRED];
}

message BindTagsResponse {
  repeated common.v1beta1.Tag tags = 1;
}

message UnbindTagsRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  repeated string tags = 2 [(google.api.field_behavior) = REQUIRED];
}

message UnbindTagsResponse {
  repeated common.v1beta1.Tag tags = 1;
}

message DeleteClusterRequest {
  // The ID of the cluster to delete.
  string cluster_id = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The ID of the cluster to delete."
      example: "\"cluster_id\""
    }
  ];

  bool validate_only = 100 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "If set to `true`, the request is validated but not executed. Defaults to `false`."
      example: "false"
    }
  ];
}

message UpdateClusterRequest {
  // The updated cluster configuration.
  Cluster cluster = 1 [(google.api.field_behavior) = REQUIRED];

  bool validate_only = 100 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "If set to `true`, the request is validated but not executed. Defaults to `false`."
      example: "false"
    }
  ];

  message Cluster {
    string cluster_id = 1 [
      (google.api.field_behavior) = REQUIRED,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The ID of the cluster to update."
        example: "\"1\""
      }
    ];

    TidbNodeSetting tidb_node_setting = 2 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The TiDB node setting for updating cluster. \n\nTo get the node spec available configurations of a specific region for a cloud provider, please refer to [List node specs](#tag/RegionService/operation/RegionService_ListNodeSpecs) API."}
    ];

    StorageNodeSetting tikv_node_setting = 3 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The TiKV node setting for updating cluster. \n\nTo get the node spec available configurations of a specific region for a cloud provider, please refer to [List node specs](#tag/RegionService/operation/RegionService_ListNodeSpecs) API."}
    ];

    StorageNodeSetting tiflash_node_setting = 4 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The TiFlash node setting for updating cluster. \n\nTo get the node spec available configurations of a specific region for a cloud provider, please refer to [List node specs](#tag/RegionService/operation/RegionService_ListNodeSpecs) API."
        example: "{\"nodeCount\":3,\"nodeSpecKey\":\"16C128G\",\"storageSizeGi\":500,\"storageType\":\"Basic\"}"
      }
    ];

    // The display name of the cluster.
    string display_name = 5 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The display name of the cluster."
        example: "\"My Cluster\""
      }
    ];

    map<string, string> labels = 6 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The labels of the cluster. \n\n**Note**: Currently, does not support `labels` updates."
        title: "map<string, string>"
        example: "{}"
      }
    ];
  }

  message TidbNodeSetting {
    // Update the homogeneous node spec key for all TiDB nodes in the cluster. If specified, all TiDB nodes across different groups in the cluster will update to the same node spec.
    string node_spec_key = 1 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "\"8C32G\""
        description: "The node spec key of the TiDB nodes in the cluster. For example, `8C32G`. \n\nTo get the node spec available configurations of a specific region for a cloud provider, please refer to [List node specs](#tag/Cluster/operation/RegionService_ListNodeSpecs) API."
      }
    ];

    repeated TidbNodeGroup tidb_node_groups = 2 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The TiDB node groups of the cluster."
        title: "TidbNodeGroup"
      }
    ];

    message TidbNodeGroup {
      string tidb_node_group_id = 1 [
        (google.api.field_behavior) = OPTIONAL,
        (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
          description: "The ID of the TiDB node group. If empty, it means to scale the default TiDB group."
          example: "\"1\""
        }
      ];

      optional int32 node_count = 2 [
        (google.api.field_behavior) = OPTIONAL,
        (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
          example: "2"
          description: "The number of TiDB nodes in the TiDB group. It must be greater than or equal to 1."
          minimum: 1
        }
      ];

      // The TiProxy setting for the TiDB group. If not specified, the default TiProxy setting will be used.
      v1beta1.TidbNodeGroup.TiProxySetting tiproxy_setting = 3 [(google.api.field_behavior) = OPTIONAL];

      // Update the heterogeneous node spec key for TiDB nodes in this TiDB group. If specified, only the TiDB nodes in this TiDB group will update to the specified node spec, while other TiDB groups in the cluster remain unchanged. Only takes effect when homogeneous `node_spec_key` in `tidb_node_setting` is not set.
      string node_spec_key = 4 [
        (google.api.field_behavior) = OPTIONAL,
        (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
          example: "\"8C32G\""
          description: "The node spec key of the TiDB group. For example, `8C32G`."
        }
      ];
    }
  }

  message StorageNodeSetting {
    string node_spec_key = 1 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "\"8C32G\""
        description: "The node spec key of the nodes in the cluster. For example, `8C32G`."
      }
    ];

    optional int32 node_count = 2 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "3"
        description: "The number of nodes in the cluster. \nWhen update TiFlash node setting: \n- If the node count is set to 0, the TiFlash node will be removed.\n- If the node count is null, the TiFlash node count won't change.\nFor other components, if the node count is set to 0, server will ignore the node count."
      }
    ];

    int32 storage_size_gi = 3 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "500"
        description: "The storage size of the node in GiB. To get the supported storage size range, please refer to the `NodeSpec` resource."
      }
    ];

    // Allow change storage type.
    v1beta1.Cluster.StorageNodeSetting.StorageType storage_type = 4 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The type of storage for the node. Default to `Basic`. For more information, see [TiKV node storage types](https://docs.pingcap.com/tidbcloud/size-your-cluster/#tikv-node-storage-types) and [TiFlash node storage types](https://docs.pingcap.com/tidbcloud/size-your-cluster/#tiflash-node-storage-types)."
        example: "\"Standard\""
      }
    ];

    optional int32 data_store_iops = 6 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "3000"
        description: "The IOPS of the data store for the node. Only supported for AWS clusters with `Plus` TiKV storage type."
      }
    ];

    // If raft_store_iops is not set, the default IOPS of raft store will be used.
    optional int32 raft_store_iops = 5 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        example: "3000"
        description: "The IOPS of the raft store for the node. If not set, the default IOPS of raft store will be used."
      }
    ];
  }
}

message PauseClusterRequest {
  string cluster_id = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The ID of the cluster to pause."
      example: "\"1\""
    }
  ];
}

message PauseClusterResponse {
  Cluster cluster = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The cluster details after it is paused."
      title: "Cluster"
    }
  ];
}

message ResumeClusterRequest {
  string cluster_id = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The ID of the cluster to resume."
      example: "\"1\""
    }
  ];
}

message ResumeClusterResponse {
  Cluster cluster = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The cluster details after it is resumed."
      title: "Cluster"
    }
  ];
}

message ResetRootPasswordRequest {
  string cluster_id = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The ID of the cluster for which to reset the root password."
      example: "\"1\""
    }
  ];

  // The new root password for the cluster.
  string root_password = 2 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The new root password for the cluster. The password must be between 8 and 64 characters long and can contain letters, numbers, and special characters."
      example: "\"new_password\""
    }
  ];
}

message ResetRootPasswordResponse {}

message ListNodeInstancesRequest {
  // The ID of the cluster for which to list node instances.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // If specified, only node instances of the specified component type will be returned.
  ComponentType component_type = 2 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The component type of the node instances. If specified, only node instances of the specified component type will be returned.\n- `TIDB`: TiDB node component.\n- `TIKV`: TiKV node instances.\n- `TIFLASH`: TiFlash node instances.\n- `PD`: PD node instances.\n- `PROXY`: Proxy node instances."}
  ];

  // The maximum number of node instances to return. The service may return fewer than this value.
  // If unspecified, at most X node instances will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListNodeInstances` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListNodeInstances` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListNodeInstancesResponse {
  repeated NodeInstance node_instances = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A list of node instances that matched the query."
    title: "NodeInstance"
  }];

  int32 total_size = 100 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "The total number of node instances that matched the query."
    example: "1"
  }];

  string next_page_token = 101 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A token, which can be sent as `pageToken` to retrieve the next page. If this field is omitted, there are no subsequent pages."
    example: "\"next_page_token\""
  }];
}

message GetNodeInstanceRequest {
  // The ID of the cluster for which to get the node instance.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the node instance to get.
  string instance_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message ShowNodeQuotaRequest {}

message ShowNodeQuotaResponse {
  repeated ComponentQuota component_quotas = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A list of quotas for each component type. Each quota specifies the maximum number of nodes allowed for that component."
    title: "ComponentQuota"
    example: "[{\"componentType\":\"TIDB\",\"quota\":200},{\"componentType\":\"TIKV\",\"quota\":200},{\"componentType\":\"TIFLASH\",\"quota\":50}]"
  }];

  message ComponentQuota {
    ComponentType component_type = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The component type. The value options are:\n\n- `TIDB`: [TiDB node](https://docs.pingcap.com/tidbcloud/tidb-cloud-glossary/#tidb-node).\n\n- `TIKV`: [TiKV node](https://docs.pingcap.com/tidbcloud/tidb-cloud-glossary/#tikv-node).\n\n- `TIFLASH`: [TiFlash node](https://docs.pingcap.com/tidbcloud/tidb-cloud-glossary/#tiflash-node)."}];

    // The maximum number of nodes allowed for this component type.
    int32 quota = 2;
  }
}

message CreateTidbNodeGroupRequest {
  TidbNodeGroup tidb_node_group = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The TiDB Node Group to create. The TiDB Node Group is used to scale the TiDB nodes in the cluster."
      title: "TidbNodeGroup"
    }
  ];

  // Indicates whether the request is a dry run. If set to true, the request will be validated but not executed.
  bool validate_only = 100 [(google.api.field_behavior) = OPTIONAL];
}

message ListTidbNodeGroupsRequest {
  // The ID of the cluster for which to list TiDB groups.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The maximum number of TiDB groups to return. The service may return fewer than this value.
  // If unspecified, at most X TiDB groups will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListTidbNodeGroups` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListTidbNodeGroups` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no `nextPageToken`.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListTidbNodeGroupsResponse {
  repeated TidbNodeGroup tidb_node_groups = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A list of TiDB Node Groups that matched the query."
    title: "TidbNodeGroup"
  }];

  // The total number of TiDB groups that matched the query.
  int32 total_size = 100;

  // A token, which can be sent as `pageToken` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 101;
}

message GetTidbNodeGroupRequest {
  // The ID of the cluster for which to get the TiDB Node Group.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB Node Group to get.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message DeleteTidbNodeGroupRequest {
  // The ID of the cluster from which to delete the TiDB Node Group.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB Node Group to delete.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Indicates whether the request is a dry run. If set to true, the request will be validated but not executed.
  bool validate_only = 100 [(google.api.field_behavior) = OPTIONAL];
}

message UpdateTidbNodeGroupRequest {
  TidbNodeGroup tidb_node_group = 1 [(google.api.field_behavior) = REQUIRED];

  // Indicates whether the request is a dry run. If set to true, the request will be validated but not executed.
  bool validate_only = 100 [(google.api.field_behavior) = OPTIONAL];

  message TidbNodeGroup {
    // The ID of the cluster to which the TiDB Node Group belongs.
    string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

    // The ID of the TiDB Node Group to update.
    string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];

    // The display name of the TiDB Node Group.
    string display_name = 3 [(google.api.field_behavior) = OPTIONAL];

    // The node count of the TiDB Node Group.
    optional int32 node_count = 4 [(google.api.field_behavior) = OPTIONAL];

    v1beta1.TidbNodeGroup.TiProxySetting tiproxy_setting = 5 [
      (google.api.field_behavior) = OPTIONAL,
      (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
        description: "The TiProxy setting of the TiDB Node Group. If not specified, the default TiProxy setting will be used."
        title: "TiProxySetting"
      }
    ];
  }
}

message GetPublicEndpointSettingRequest {
  // The ID of the cluster for which to get the public endpoint setting.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB Node Group for which to get the public endpoint setting.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message UpdatePublicEndpointSettingRequest {
  // The ID of the cluster for which to update the public endpoint setting.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  PublicEndpointSetting public_endpoint_setting = 2 [(google.api.field_behavior) = REQUIRED];

  // Indicates whether the request is a dry run. If set to true, the request will be validated but not executed.
  bool validate_only = 100 [(google.api.field_behavior) = OPTIONAL];
}

message ShowCloudProvidersRequest {
  // The ID of the project. If not specified, the project ID of the default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];
}

message ShowCloudProvidersResponse {
  // A list of cloud providers that are available for the project.
  repeated common.v1beta1.Region.CloudProvider cloud_providers = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "A list of cloud providers that are available for the project.\n\n- `\"aws\"`: Amazon Web Services\n\n- `\"gcp\"`: Google Cloud\n\n- `\"azure\"`: Microsoft Azure\n\n- `\"alicloud\"`: Alibaba Cloud"}];
}

message ListRegionsRequest {
  // The cloud provider where the region is located. If specified, only regions of the specified cloud provider are returned.
  string cloud_provider = 1 [(google.api.field_behavior) = OPTIONAL];

  // The ID of the project to list regions for. If not specified, the project ID of the default project is used.
  string project_id = 2 [(google.api.field_behavior) = OPTIONAL];

  int32 page_size = 100 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The maximum number of regions to return. If not specified, at most 20 regions will be returned. The maximum value is `100`. Values greater than `100` are set to `100`."
      example: "20"
      default: "20"
      minimum: 1
      maximum: 100
    }
  ];

  string page_token = 101 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The pagination token received from a previous [List regions](#tag/Region/operation/RegionService_ListRegions) request. Use this token to retrieve the next page of results.\n\n**Note**: When paginating, all other parameters must match the original request."
      example: "\"next_page_token\""
    }
  ];

  // The number of regions to skip before returning results. If the value exceeds the total number of clusters, the response is `200` with an empty list and no `nextPageToken`.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListRegionsResponse {
  repeated common.v1beta1.Region regions = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A list of regions that match the query."
    title: "Region"
  }];

  // The total number of regions that match the query.
  int32 total_size = 100;

  // The token to retrieve the next page of results. Use this value as the `pageToken` parameter in the next request. This field is empty when there are no more pages.
  string next_page_token = 101;
}

message GetRegionRequest {
  // The ID of the region to retrieve, in the format of `{cloud_provider}-{region_code}`. For example, `aws-us-east-1`.
  string region_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the project for which to retrieve the region. If not specified, the project ID of the default project is used.
  string project_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListNodeSpecsRequest {
  // The ID of the region, in the format of `{cloud_provider}-{region_code}`. For example, `aws-us-east-1`.
  string region_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Filters the results to include only node specs for the specified component type.
  ComponentType component_type = 2 [(google.api.field_behavior) = OPTIONAL];

  // Filters the results to include only node specs available to the specified project. If not specified, the project ID of the default project is used.
  string project_id = 3 [(google.api.field_behavior) = OPTIONAL];

  oneof condition {
    // The ID of the existing cluster. If specified, only node specs that are available to the cluster are returned. If not specified, all available node specs are returned.
    // Usage case: Scale up/down a cluster.
    string cluster_id = 4;

    // The TiDB version of the wanted cluster. If specified, only node specs that are available to the specified TiDB version are returned. If not specified, all available node specs are returned.
    // Usage case: Restore a cluster from a backup.
    string tidb_version = 5;
  }

  int32 page_size = 100 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The maximum number of node specs to return. If not specified, at most 10 node specs will be returned. The maximum value is `100`. Values greater than `100` are set to `100`."
      example: "10"
      default: "10"
      minimum: 1
      maximum: 100
    }
  ];

  string page_token = 101 [
    (google.api.field_behavior) = OPTIONAL,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The pagination token received from a previous [List node specs](#tag/Region/operation/RegionService_ListNodeSpecs) request. Use this token to retrieve the next page of results.\n\n**Note**: When paginating, all other parameters must match the original request."}
  ];

  // The number of node specs to skip before returning results. If the value exceeds the total number of clusters, the response is `200` with an empty list and no `nextPageToken`.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListNodeSpecsResponse {
  // A list of node specs that match the query.
  repeated NodeSpec node_specs = 1;

  // The total number of node specs that match the query.
  int32 total_size = 100;

  // The token to retrieve the next page of results. Use this value as the `pageToken` parameter in the next request. This field is empty when there are no more pages.
  string next_page_token = 101;
}

message GetNodeSpecRequest {
  // The region ID of the node spec to retrieve, in the format of `{cloud_provider}-{region_code}`. For example, `aws-us-east-1`. If not specified, the default region ID is used.
  string region_id = 1 [(google.api.field_behavior) = REQUIRED];

  // Filters the results to include only node specs for the specified component type.
  ComponentType component_type = 2 [(google.api.field_behavior) = REQUIRED];

  // The key of the node spec to retrieve. For example, `8C32G`.
  string node_spec_key = 3 [(google.api.field_behavior) = REQUIRED];

  // The ID of the project for which to retrieve the node spec. If not specified, the project ID of the default project is used.
  string project_id = 4 [(google.api.field_behavior) = OPTIONAL];

  oneof condition {
    // The ID of the existing cluster. If specified, only node specs that are available to the cluster are returned. If not specified, all available node specs are returned.
    // Usage case: Scale up/down a cluster.
    string cluster_id = 5;

    // The TiDB version of the wanted cluster. If specified, only node specs that are available to the specified TiDB version are returned. If not specified, all available node specs are returned.
    // Usage case: Restore a cluster from a backup.
    string tidb_version = 6;
  }
}

message CreateNetworkContainerRequest {
  NetworkContainer network_container = 1 [(google.api.field_behavior) = REQUIRED];

  bool validate_only = 2 [(google.api.field_behavior) = OPTIONAL];
}

message GetNetworkContainerRequest {
  string network_container_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message ListNetworkContainersRequest {
  // If unspecified, the project ID of default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  common.v1beta1.Region.CloudProvider cloud_provider = 2 [(google.api.field_behavior) = OPTIONAL];

  // The maximum number of network containers to return. The service may return fewer than this value.
  // If unspecified, at most X network containers will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListNetworkContainers` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListNetworkContainers` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListNetworkContainersResponse {
  repeated NetworkContainer network_containers = 1;

  // The total number of network containers that matched the query.
  int32 total_size = 100;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 101;
}

message DeleteNetworkContainerRequest {
  string network_container_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message CreateVpcPeeringRequest {
  VpcPeering vpc_peering = 1 [(google.api.field_behavior) = REQUIRED];
}

message DeleteVpcPeeringRequest {
  // Format {cloud_provider}-{random_suffix}
  string vpc_peering_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message ListVpcPeeringsRequest {
  // If unspecified, the project ID of default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  // If specified, only VPC peerings of the specified cloud provider will be returned.
  common.v1beta1.Region.CloudProvider cloud_provider = 2 [(google.api.field_behavior) = OPTIONAL];

  // The maximum number of VPC peerings to return. The service may return fewer than this value.
  // If unspecified, at most X VPC peerings will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListVpcPeerings` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListVpcPeerings` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];
}

message ListVpcPeeringsResponse {
  repeated VpcPeering vpc_peerings = 1;

  // The total number of VPC peerings that matched the query.
  int32 total_size = 100;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 101;
}

message GetVpcPeeringRequest {
  // Format {cloud_provider}-{random_suffix}
  string vpc_peering_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message GetPrivateLinkServiceRequest {
  // The ID of the cluster for which to get the private link service.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB node group for which to get the private link service.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message CreatePrivateEndpointConnectionRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  PrivateEndpointConnection private_endpoint_connection = 2 [(google.api.field_behavior) = REQUIRED];

  // (Azure only) If set to `true`, the request is validated but not executed. Defaults to `false`.
  bool validate_only = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ListPrivateEndpointConnectionsRequest {
  // The id of the cluster for which to list private link connections. To list private link connections across different clusters and TiDB groups, set as `-``.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // To list private link connections across different clusters and TiDB groups, set tidb_node_group_id to "-".
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Pass this project_id only when listing under a project across different clusters and TiDB groups.
  // Both cluster_id and tidb_node_group_id should be set to "-" in this case.
  // If unspecified, the project ID of the cluster is used.
  string project_id = 3 [(google.api.field_behavior) = OPTIONAL];

  // If specified, only private link connections of the specified cloud provider will be returned.
  common.v1beta1.Region.CloudProvider cloud_provider = 4 [(google.api.field_behavior) = OPTIONAL];

  // The maximum number of private link connections to return. The service may return fewer than this value.
  // If unspecified, at most 10 private link connections will be returned.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListPrivateLinkConnections` call.
  // Provide this to retrieve the subsequent page.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListPrivateEndpointConnectionsResponse {
  repeated PrivateEndpointConnection private_endpoint_connections = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "A list of private endpoint connections that match the query."
    title: "PrivateEndpointConnection"
  }];

  int32 total_size = 100 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "The total number of private link connections that match the query."
    example: "1"
  }];

  string next_page_token = 101 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
    description: "The token to retrieve the next page of results. Use this value as the `pageToken` parameter in the next request. This field is empty when there are no more pages."
    example: "\"next_page_token\""
  }];
}

message GetPrivateEndpointConnectionRequest {
  // The ID of the cluster for which to get the private endpoint connection.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB node group for which to get the private endpoint connection.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];

  // The ID of the private endpoint connection to get.
  string private_endpoint_connection_id = 3 [(google.api.field_behavior) = REQUIRED];
}

message DeletePrivateEndpointConnectionRequest {
  // The ID of the cluster from which to delete the private endpoint connection.
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // The ID of the TiDB node group from which to delete the private endpoint connection.
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];

  // The ID of the private endpoint connection to delete.
  string private_endpoint_connection_id = 3 [(google.api.field_behavior) = REQUIRED];
}

message GetMaintenanceWindowRequest {
  // Format: project-{project_id}
  string maintenance_window_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message ListMaintenanceWindowsRequest {
  // If unspecified, the project ID of default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  // The maximum number of maintenance windows to return. The service may return fewer than this value.
  // If unspecified, at most X maintenance windows will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListMaintenanceWindows` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListMaintenanceWindows` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListMaintenanceWindowsResponse {
  repeated MaintenanceWindow maintenance_windows = 1;

  // The total number of maintenance windows that matched the query.
  int32 total_size = 100;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 101;
}

message UpdateMaintenanceWindowRequest {
  MaintenanceWindow maintenance_window = 1 [(google.api.field_behavior) = REQUIRED];

  message MaintenanceWindow {
    // Format: project-{project_id}
    string maintenance_window_id = 1 [(google.api.field_behavior) = REQUIRED];

    int32 week_day = 2 [(google.api.field_behavior) = OPTIONAL];

    int32 day_hour = 3 [(google.api.field_behavior) = OPTIONAL];

    int32 hour_minute = 4 [(google.api.field_behavior) = OPTIONAL];
  }
}

message ListMaintenanceTasksRequest {
  // If unspecified, the project ID of default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  // The maximum number of maintenance tasks to return. The service may return fewer than this value.
  // If unspecified, at most X maintenance tasks will be returned.
  // The maximum value is X; values above X will be coerced to X.
  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  // A page token, received from a previous `ListMaintenanceTasks` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListMaintenanceTasks` must match
  // the call that provided the page token.
  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListMaintenanceTasksResponse {
  repeated MaintenanceTask maintenance_tasks = 1;

  // The total number of maintenance tasks that matched the query.
  int32 total_size = 100;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 101;
}

message GetMaintenanceTaskRequest {
  string maintenance_task_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message UpdateMaintenanceTaskRequest {
  MaintenanceTask maintenance_task = 1 [(google.api.field_behavior) = REQUIRED];

  message MaintenanceTask {
    string maintenance_task_id = 1 [(google.api.field_behavior) = REQUIRED];

    // Timestamp when the task run.
    google.protobuf.Timestamp scheduled_apply_time = 2 [(google.api.field_behavior) = REQUIRED];
  }
}

message GetAuditLogConfigRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message UpdateAuditLogConfigRequest {
  AuditLogConfig audit_log_config = 1 [(google.api.field_behavior) = REQUIRED];

  bool validate_only = 2 [(google.api.field_behavior) = OPTIONAL];

  message AuditLogConfig {
    string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

    optional bool enabled = 2 [(google.api.field_behavior) = OPTIONAL];

    string bucket_uri = 3 [(google.api.field_behavior) = OPTIONAL];

    // Format: {cloud_provider}-{region_code}
    string bucket_region_id = 4 [(google.api.field_behavior) = OPTIONAL];

    string aws_role_arn = 5 [(google.api.field_behavior) = OPTIONAL];

    string azure_sas_token = 6 [(google.api.field_behavior) = OPTIONAL];

    v1beta1.AuditLogConfig.BucketManager bucket_manager = 7 [(google.api.field_behavior) = OPTIONAL];

    optional bool unredacted = 8 [(google.api.field_behavior) = OPTIONAL];

    v1beta1.AuditLogConfig.Format format = 10 [(google.api.field_behavior) = OPTIONAL];

    v1beta1.AuditLogConfig.RotationPolicy rotation_policy = 11 [(google.api.field_behavior) = OPTIONAL];
  }
}

message CreateAuditLogFilterRuleRequest {
  AuditLogFilterRule audit_log_filter_rule = 1 [(google.api.field_behavior) = REQUIRED];
}

message ReplaceAuditLogFilterRuleRequest {
  // Required. The audit log filter rule to update.
  AuditLogFilterRule audit_log_filter_rule = 1 [(google.api.field_behavior) = REQUIRED];

  message AuditLogFilterRule {
    string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

    string audit_log_filter_rule_id = 2 [(google.api.field_behavior) = REQUIRED];

    string user_expr = 3 [(google.api.field_behavior) = OPTIONAL];

    bool disabled = 4 [(google.api.field_behavior) = OPTIONAL];

    repeated v1beta1.AuditLogFilterRule.EventType event_types = 5 [(google.api.field_behavior) = OPTIONAL];

    string display_name = 6 [(google.api.field_behavior) = OPTIONAL];
  }
}

message ReplaceAuditLogFilterRuleResponse {
  AuditLogFilterRule audit_log_filter_rule = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message GetAuditLogFilterRuleRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  string audit_log_filter_rule_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message ListAuditLogFilterRulesRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListAuditLogFilterRulesResponse {
  repeated AuditLogFilterRule audit_log_filter_rules = 1;

  int32 total_size = 100;

  string next_page_token = 101;
}

message DeleteAuditLogFilterRuleRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  string audit_log_filter_rule_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message ShowObjectStorageAccessIamPrincipalRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message QueryAuditLogFilesRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  // the start time of the audit logs created time range.
  google.protobuf.Timestamp start_time = 2 [(google.api.field_behavior) = OPTIONAL];

  // the end time of the audit logs created time range.
  google.protobuf.Timestamp end_time = 3 [(google.api.field_behavior) = OPTIONAL];

  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message QueryAuditLogFilesResponse {
  repeated AuditLogFile audit_log_files = 1;

  int32 total_size = 100;

  string next_page_token = 101;

  message AuditLogFile {
    string filename = 1;

    string uri = 2;

    string bucket = 3;

    int32 size_byte = 4;

    google.protobuf.Timestamp create_time = 5;
  }
}

message GenerateAuditLogFileDownloadAddressRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];

  string audit_log_file_uri = 2 [(google.api.field_behavior) = REQUIRED];
}

message GenerateAuditLogFileDownloadAddressResponse {
  string download_address = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message ShowObjectStorageAccessIamPrincipalResponse {
  string iam_principal = 1;
}

message CreateCustomerManagedEncryptionKeyRequest {
  CustomerManagedEncryptionKey customer_managed_encryption_key = 1 [(google.api.field_behavior) = REQUIRED];

  bool validate_only = 2 [(google.api.field_behavior) = OPTIONAL];
}

message GetCustomerManagedEncryptionKeyRequest {
  string customer_managed_encryption_key_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message ListCustomerManagedEncryptionKeysRequest {
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  int32 page_size = 100 [(google.api.field_behavior) = OPTIONAL];

  string page_token = 101 [(google.api.field_behavior) = OPTIONAL];

  // The number of individual resources to skip before starting to return results.
  // If the skip value causes the cursor to move past the end of the collection,
  // the response will be 200 OK with an empty result set and no next_page_token.
  int32 skip = 102 [(.google.api.field_behavior) = OPTIONAL];
}

message ListCustomerManagedEncryptionKeysResponse {
  repeated CustomerManagedEncryptionKey cmeks = 1;

  int32 total_size = 100;

  string next_page_token = 101;
}

message DeleteCustomerManagedEncryptionKeyRequest {
  string customer_managed_encryption_key_id = 1 [(google.api.field_behavior) = REQUIRED];
}

message ShowCmekAccessIamPrincipalRequest {
  // If unspecified, the project ID of default project is used.
  string project_id = 1 [(google.api.field_behavior) = OPTIONAL];

  common.v1beta1.Region.CloudProvider cloud_provider = 2 [(google.api.field_behavior) = OPTIONAL];

  string region_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

message ShowCmekAccessIamPrincipalResponse {
  string project_id = 1;

  common.v1beta1.Region.CloudProvider cloud_provider = 2;

  string region_id = 3;

  oneof principal {
    AwsCmekPrincipal aws_cmek_principal = 4;
    AzureCmekPrincipal azure_cmek_principal = 5;
  }

  message AwsCmekPrincipal {
    string account_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message AzureCmekPrincipal {
    AzureAppRegistration app_registration = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    string key_vault_key_suffix = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }
}

message GetLogRedactionPolicyRequest {
  // The ID of the cluster for which to get the log redaction policy.
  string cluster_id = 1 [
    (google.api.field_behavior) = REQUIRED,
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      description: "The ID of the cluster for which to get the log redaction policy."
      example: "cluster_id"
    }
  ];
}

message UpdateLogRedactionPolicyRequest {
  LogRedactionPolicy log_redaction_policy = 1 [(google.api.field_behavior) = REQUIRED];
}

message GetPrivateLinkIPAddressTypeSettingRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];
}

message UpdatePrivateLinkIPAddressTypeSettingRequest {
  string cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  string tidb_node_group_id = 2 [(google.api.field_behavior) = REQUIRED];
  PrivateLinkIPAddressTypeSetting private_link_ip_address_type_setting = 3 [(google.api.field_behavior) = REQUIRED];
}
