// Copyright 2025 Google LLC
//
// 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.

syntax = "proto3";

package google.bigtable.admin.v2;

import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/bigtable/admin/v2/types.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/rpc/status.proto";

option csharp_namespace = "Google.Cloud.Bigtable.Admin.V2";
option go_package = "cloud.google.com/go/bigtable/admin/apiv2/adminpb;adminpb";
option java_multiple_files = true;
option java_outer_classname = "TableProto";
option java_package = "com.google.bigtable.admin.v2";
option php_namespace = "Google\\Cloud\\Bigtable\\Admin\\V2";
option ruby_package = "Google::Cloud::Bigtable::Admin::V2";
option (google.api.resource_definition) = {
  type: "cloudkms.googleapis.com/CryptoKeyVersion"
  pattern: "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_version}"
};

// Information about a table restore.
message RestoreInfo {
  // The type of the restore source.
  RestoreSourceType source_type = 1;

  // Information about the source used to restore the table.
  oneof source_info {
    // Information about the backup used to restore the table. The backup
    // may no longer exist.
    BackupInfo backup_info = 2;
  }
}

// Change stream configuration.
message ChangeStreamConfig {
  // How long the change stream should be retained. Change stream data older
  // than the retention period will not be returned when reading the change
  // stream from the table.
  // Values must be at least 1 day and at most 7 days, and will be truncated to
  // microsecond granularity.
  google.protobuf.Duration retention_period = 1;
}

// A collection of user data indexed by row, column, and timestamp.
// Each table is served using the resources of its parent cluster.
message Table {
  option (google.api.resource) = {
    type: "bigtableadmin.googleapis.com/Table"
    pattern: "projects/{project}/instances/{instance}/tables/{table}"
  };

  // The state of a table's data in a particular cluster.
  message ClusterState {
    // Table replication states.
    enum ReplicationState {
      // The replication state of the table is unknown in this cluster.
      STATE_NOT_KNOWN = 0;

      // The cluster was recently created, and the table must finish copying
      // over pre-existing data from other clusters before it can begin
      // receiving live replication updates and serving Data API requests.
      INITIALIZING = 1;

      // The table is temporarily unable to serve Data API requests from this
      // cluster due to planned internal maintenance.
      PLANNED_MAINTENANCE = 2;

      // The table is temporarily unable to serve Data API requests from this
      // cluster due to unplanned or emergency maintenance.
      UNPLANNED_MAINTENANCE = 3;

      // The table can serve Data API requests from this cluster. Depending on
      // replication delay, reads may not immediately reflect the state of the
      // table in other clusters.
      READY = 4;

      // The table is fully created and ready for use after a restore, and is
      // being optimized for performance. When optimizations are complete, the
      // table will transition to `READY` state.
      READY_OPTIMIZING = 5;
    }

    // Output only. The state of replication for the table in this cluster.
    ReplicationState replication_state = 1
        [(google.api.field_behavior) = OUTPUT_ONLY];

    // Output only. The encryption information for the table in this cluster.
    // If the encryption key protecting this resource is customer managed, then
    // its version can be rotated in Cloud Key Management Service (Cloud KMS).
    // The primary version of the key and its status will be reflected here when
    // changes propagate from Cloud KMS.
    repeated EncryptionInfo encryption_info = 2
        [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  // Possible timestamp granularities to use when keeping multiple versions
  // of data in a table.
  enum TimestampGranularity {
    // The user did not specify a granularity. Should not be returned.
    // When specified during table creation, MILLIS will be used.
    TIMESTAMP_GRANULARITY_UNSPECIFIED = 0;

    // The table keeps data versioned at a granularity of 1ms.
    MILLIS = 1;
  }

  // Defines a view over a table's fields.
  enum View {
    // Uses the default view for each method as documented in its request.
    VIEW_UNSPECIFIED = 0;

    // Only populates `name`.
    NAME_ONLY = 1;

    // Only populates `name` and fields related to the table's schema.
    SCHEMA_VIEW = 2;

    // Only populates `name` and fields related to the table's replication
    // state.
    REPLICATION_VIEW = 3;

    // Only populates `name` and fields related to the table's encryption state.
    ENCRYPTION_VIEW = 5;

    // Populates all fields.
    FULL = 4;
  }

  // Defines an automated backup policy for a table
  message AutomatedBackupPolicy {
    // Required. How long the automated backups should be retained. The only
    // supported value at this time is 3 days.
    google.protobuf.Duration retention_period = 1
        [(google.api.field_behavior) = REQUIRED];

    // Required. How frequently automated backups should occur. The only
    // supported value at this time is 24 hours.
    google.protobuf.Duration frequency = 2
        [(google.api.field_behavior) = REQUIRED];
  }

  // The unique name of the table. Values are of the form
  // `projects/{project}/instances/{instance}/tables/[_a-zA-Z0-9][-_.a-zA-Z0-9]*`.
  // Views: `NAME_ONLY`, `SCHEMA_VIEW`, `REPLICATION_VIEW`, `FULL`
  string name = 1;

  // Output only. Map from cluster ID to per-cluster table state.
  // If it could not be determined whether or not the table has data in a
  // particular cluster (for example, if its zone is unavailable), then
  // there will be an entry for the cluster with UNKNOWN `replication_status`.
  // Views: `REPLICATION_VIEW`, `ENCRYPTION_VIEW`, `FULL`
  map<string, ClusterState> cluster_states = 2
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // The column families configured for this table, mapped by column family ID.
  // Views: `SCHEMA_VIEW`, `STATS_VIEW`, `FULL`
  map<string, ColumnFamily> column_families = 3;

  // Immutable. The granularity (i.e. `MILLIS`) at which timestamps are stored
  // in this table. Timestamps not matching the granularity will be rejected. If
  // unspecified at creation time, the value will be set to `MILLIS`. Views:
  // `SCHEMA_VIEW`, `FULL`.
  TimestampGranularity granularity = 4
      [(google.api.field_behavior) = IMMUTABLE];

  // Output only. If this table was restored from another data source (e.g. a
  // backup), this field will be populated with information about the restore.
  RestoreInfo restore_info = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  // If specified, enable the change stream on this table.
  // Otherwise, the change stream is disabled and the change stream is not
  // retained.
  ChangeStreamConfig change_stream_config = 8;

  // Set to true to make the table protected against data loss. i.e. deleting
  // the following resources through Admin APIs are prohibited:
  //
  // * The table.
  // * The column families in the table.
  // * The instance containing the table.
  //
  // Note one can still delete the data stored in the table through Data APIs.
  bool deletion_protection = 9;

  oneof automated_backup_config {
    // If specified, automated backups are enabled for this table.
    // Otherwise, automated backups are disabled.
    AutomatedBackupPolicy automated_backup_policy = 13;
  }

  // The row key schema for this table. The schema is used to decode the raw row
  // key bytes into a structured format. The order of field declarations in this
  // schema is important, as it reflects how the raw row key bytes are
  // structured. Currently, this only affects how the key is read via a
  // GoogleSQL query from the ExecuteQuery API.
  //
  // For a SQL query, the _key column is still read as raw bytes. But queries
  // can reference the key fields by name, which will be decoded from _key using
  // provided type and encoding. Queries that reference key fields will fail if
  // they encounter an invalid row key.
  //
  // For example, if _key = "some_id#2024-04-30#\x00\x13\x00\xf3" with the
  // following schema:
  // {
  //   fields {
  //     field_name: "id"
  //     type { string { encoding: utf8_bytes {} } }
  //   }
  //   fields {
  //     field_name: "date"
  //     type { string { encoding: utf8_bytes {} } }
  //   }
  //   fields {
  //     field_name: "product_code"
  //     type { int64 { encoding: big_endian_bytes {} } }
  //   }
  //   encoding { delimited_bytes { delimiter: "#" } }
  // }
  //
  // The decoded key parts would be:
  //   id = "some_id", date = "2024-04-30", product_code = 1245427
  // The query "SELECT _key, product_code FROM table" will return two columns:
  // /------------------------------------------------------\
  // |              _key                     | product_code |
  // | --------------------------------------|--------------|
  // | "some_id#2024-04-30#\x00\x13\x00\xf3" |   1245427    |
  // \------------------------------------------------------/
  //
  // The schema has the following invariants:
  // (1) The decoded field values are order-preserved. For read, the field
  // values will be decoded in sorted mode from the raw bytes.
  // (2) Every field in the schema must specify a non-empty name.
  // (3) Every field must specify a type with an associated encoding. The type
  // is limited to scalar types only: Array, Map, Aggregate, and Struct are not
  // allowed.
  // (4) The field names must not collide with existing column family
  // names and reserved keywords "_key" and "_timestamp".
  //
  // The following update operations are allowed for row_key_schema:
  // - Update from an empty schema to a new schema.
  // - Remove the existing schema. This operation requires setting the
  //   `ignore_warnings` flag to `true`, since it might be a backward
  //   incompatible change. Without the flag, the update request will fail with
  //   an INVALID_ARGUMENT error.
  // Any other row key schema update operation (e.g. update existing schema
  // columns names or types) is currently unsupported.
  Type.Struct row_key_schema = 15;
}

// AuthorizedViews represent subsets of a particular Cloud Bigtable table. Users
// can configure access to each Authorized View independently from the table and
// use the existing Data APIs to access the subset of data.
message AuthorizedView {
  option (google.api.resource) = {
    type: "bigtableadmin.googleapis.com/AuthorizedView"
    pattern: "projects/{project}/instances/{instance}/tables/{table}/authorizedViews/{authorized_view}"
    plural: "authorizedViews"
    singular: "authorizedView"
  };

  // Subsets of a column family that are included in this AuthorizedView.
  message FamilySubsets {
    // Individual exact column qualifiers to be included in the AuthorizedView.
    repeated bytes qualifiers = 1;

    // Prefixes for qualifiers to be included in the AuthorizedView. Every
    // qualifier starting with one of these prefixes is included in the
    // AuthorizedView. To provide access to all qualifiers, include the empty
    // string as a prefix
    // ("").
    repeated bytes qualifier_prefixes = 2;
  }

  // Defines a simple AuthorizedView that is a subset of the underlying Table.
  message SubsetView {
    // Row prefixes to be included in the AuthorizedView.
    // To provide access to all rows, include the empty string as a prefix ("").
    repeated bytes row_prefixes = 1;

    // Map from column family name to the columns in this family to be included
    // in the AuthorizedView.
    map<string, FamilySubsets> family_subsets = 2;
  }

  // Defines a subset of an AuthorizedView's fields.
  enum ResponseView {
    // Uses the default view for each method as documented in the request.
    RESPONSE_VIEW_UNSPECIFIED = 0;

    // Only populates `name`.
    NAME_ONLY = 1;

    // Only populates the AuthorizedView's basic metadata. This includes:
    // name, deletion_protection, etag.
    BASIC = 2;

    // Populates every fields.
    FULL = 3;
  }

  // Identifier. The name of this AuthorizedView.
  // Values are of the form
  // `projects/{project}/instances/{instance}/tables/{table}/authorizedViews/{authorized_view}`
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // The type of this AuthorizedView.
  oneof authorized_view {
    // An AuthorizedView permitting access to an explicit subset of a Table.
    SubsetView subset_view = 2;
  }

  // The etag for this AuthorizedView.
  // If this is provided on update, it must match the server's etag. The server
  // returns ABORTED error on a mismatched etag.
  string etag = 3;

  // Set to true to make the AuthorizedView protected against deletion.
  // The parent Table and containing Instance cannot be deleted if an
  // AuthorizedView has this bit set.
  bool deletion_protection = 4;
}

// A set of columns within a table which share a common configuration.
message ColumnFamily {
  // Garbage collection rule specified as a protobuf.
  // Must serialize to at most 500 bytes.
  //
  // NOTE: Garbage collection executes opportunistically in the background, and
  // so it's possible for reads to return a cell even if it matches the active
  // GC expression for its family.
  GcRule gc_rule = 1;

  // The type of data stored in each of this family's cell values, including its
  // full encoding. If omitted, the family only serves raw untyped bytes.
  //
  // For now, only the `Aggregate` type is supported.
  //
  // `Aggregate` can only be set at family creation and is immutable afterwards.
  //
  //
  // If `value_type` is `Aggregate`, written data must be compatible with:
  //  * `value_type.input_type` for `AddInput` mutations
  Type value_type = 3;
}

// Rule for determining which cells to delete during garbage collection.
message GcRule {
  // A GcRule which deletes cells matching all of the given rules.
  message Intersection {
    // Only delete cells which would be deleted by every element of `rules`.
    repeated GcRule rules = 1;
  }

  // A GcRule which deletes cells matching any of the given rules.
  message Union {
    // Delete cells which would be deleted by any element of `rules`.
    repeated GcRule rules = 1;
  }

  // Garbage collection rules.
  oneof rule {
    // Delete all cells in a column except the most recent N.
    int32 max_num_versions = 1;

    // Delete cells in a column older than the given age.
    // Values must be at least one millisecond, and will be truncated to
    // microsecond granularity.
    google.protobuf.Duration max_age = 2;

    // Delete cells that would be deleted by every nested rule.
    Intersection intersection = 3;

    // Delete cells that would be deleted by any nested rule.
    Union union = 4;
  }
}

// Encryption information for a given resource.
// If this resource is protected with customer managed encryption, the in-use
// Cloud Key Management Service (Cloud KMS) key version is specified along with
// its status.
message EncryptionInfo {
  // Possible encryption types for a resource.
  enum EncryptionType {
    // Encryption type was not specified, though data at rest remains encrypted.
    ENCRYPTION_TYPE_UNSPECIFIED = 0;

    // The data backing this resource is encrypted at rest with a key that is
    // fully managed by Google. No key version or status will be populated.
    // This is the default state.
    GOOGLE_DEFAULT_ENCRYPTION = 1;

    // The data backing this resource is encrypted at rest with a key that is
    // managed by the customer.
    // The in-use version of the key and its status are populated for
    // CMEK-protected tables.
    // CMEK-protected backups are pinned to the key version that was in use at
    // the time the backup was taken. This key version is populated but its
    // status is not tracked and is reported as `UNKNOWN`.
    CUSTOMER_MANAGED_ENCRYPTION = 2;
  }

  // Output only. The type of encryption used to protect this resource.
  EncryptionType encryption_type = 3
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The status of encrypt/decrypt calls on underlying data for
  // this resource. Regardless of status, the existing data is always encrypted
  // at rest.
  google.rpc.Status encryption_status = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The version of the Cloud KMS key specified in the parent
  // cluster that is in use for the data underlying this table.
  string kms_key_version = 2 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.resource_reference) = {
      type: "cloudkms.googleapis.com/CryptoKeyVersion"
    }
  ];
}

// A snapshot of a table at a particular time. A snapshot can be used as a
// checkpoint for data restoration or a data source for a new table.
//
// Note: This is a private alpha release of Cloud Bigtable snapshots. This
// feature is not currently available to most Cloud Bigtable customers. This
// feature might be changed in backward-incompatible ways and is not recommended
// for production use. It is not subject to any SLA or deprecation policy.
message Snapshot {
  option (google.api.resource) = {
    type: "bigtableadmin.googleapis.com/Snapshot"
    pattern: "projects/{project}/instances/{instance}/clusters/{cluster}/snapshots/{snapshot}"
  };

  // Possible states of a snapshot.
  enum State {
    // The state of the snapshot could not be determined.
    STATE_NOT_KNOWN = 0;

    // The snapshot has been successfully created and can serve all requests.
    READY = 1;

    // The snapshot is currently being created, and may be destroyed if the
    // creation process encounters an error. A snapshot may not be restored to a
    // table while it is being created.
    CREATING = 2;
  }

  // The unique name of the snapshot.
  // Values are of the form
  // `projects/{project}/instances/{instance}/clusters/{cluster}/snapshots/{snapshot}`.
  string name = 1;

  // Output only. The source table at the time the snapshot was taken.
  Table source_table = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The size of the data in the source table at the time the
  // snapshot was taken. In some cases, this value may be computed
  // asynchronously via a background process and a placeholder of 0 will be used
  // in the meantime.
  int64 data_size_bytes = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The time when the snapshot is created.
  google.protobuf.Timestamp create_time = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // The time when the snapshot will be deleted. The maximum amount of time a
  // snapshot can stay active is 365 days. If 'ttl' is not specified,
  // the default maximum of 365 days will be used.
  google.protobuf.Timestamp delete_time = 5;

  // Output only. The current state of the snapshot.
  State state = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Description of the snapshot.
  string description = 7;
}

// A backup of a Cloud Bigtable table.
message Backup {
  option (google.api.resource) = {
    type: "bigtableadmin.googleapis.com/Backup"
    pattern: "projects/{project}/instances/{instance}/clusters/{cluster}/backups/{backup}"
  };

  // Indicates the current state of the backup.
  enum State {
    // Not specified.
    STATE_UNSPECIFIED = 0;

    // The pending backup is still being created. Operations on the
    // backup may fail with `FAILED_PRECONDITION` in this state.
    CREATING = 1;

    // The backup is complete and ready for use.
    READY = 2;
  }

  // The type of the backup.
  enum BackupType {
    // Not specified.
    BACKUP_TYPE_UNSPECIFIED = 0;

    // The default type for Cloud Bigtable managed backups. Supported for
    // backups created in both HDD and SSD instances. Requires optimization when
    // restored to a table in an SSD instance.
    STANDARD = 1;

    // A backup type with faster restore to SSD performance. Only supported for
    // backups created in SSD instances. A new SSD table restored from a hot
    // backup reaches production performance more quickly than a standard
    // backup.
    HOT = 2;
  }

  // A globally unique identifier for the backup which cannot be
  // changed. Values are of the form
  // `projects/{project}/instances/{instance}/clusters/{cluster}/
  //    backups/[_a-zA-Z0-9][-_.a-zA-Z0-9]*`
  // The final segment of the name must be between 1 and 50 characters
  // in length.
  //
  // The backup is stored in the cluster identified by the prefix of the backup
  // name of the form
  // `projects/{project}/instances/{instance}/clusters/{cluster}`.
  string name = 1;

  // Required. Immutable. Name of the table from which this backup was created.
  // This needs to be in the same instance as the backup. Values are of the form
  // `projects/{project}/instances/{instance}/tables/{source_table}`.
  string source_table = 2 [
    (google.api.field_behavior) = IMMUTABLE,
    (google.api.field_behavior) = REQUIRED
  ];

  // Output only. Name of the backup from which this backup was copied. If a
  // backup is not created by copying a backup, this field will be empty. Values
  // are of the form:
  // projects/<project>/instances/<instance>/clusters/<cluster>/backups/<backup>
  string source_backup = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Required. The expiration time of the backup.
  // When creating a backup or updating its `expire_time`, the value must be
  // greater than the backup creation time by:
  // - At least 6 hours
  // - At most 90 days
  //
  // Once the `expire_time` has passed, Cloud Bigtable will delete the backup.
  google.protobuf.Timestamp expire_time = 3
      [(google.api.field_behavior) = REQUIRED];

  // Output only. `start_time` is the time that the backup was started
  // (i.e. approximately the time the
  // [CreateBackup][google.bigtable.admin.v2.BigtableTableAdmin.CreateBackup]
  // request is received).  The row data in this backup will be no older than
  // this timestamp.
  google.protobuf.Timestamp start_time = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. `end_time` is the time that the backup was finished. The row
  // data in the backup will be no newer than this timestamp.
  google.protobuf.Timestamp end_time = 5
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Size of the backup in bytes.
  int64 size_bytes = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The current state of the backup.
  State state = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The encryption information for the backup.
  EncryptionInfo encryption_info = 9
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Indicates the backup type of the backup.
  BackupType backup_type = 11;

  // The time at which the hot backup will be converted to a standard backup.
  // Once the `hot_to_standard_time` has passed, Cloud Bigtable will convert the
  // hot backup to a standard backup. This value must be greater than the backup
  // creation time by:
  // - At least 24 hours
  //
  // This field only applies for hot backups. When creating or updating a
  // standard backup, attempting to set this field will fail the request.
  google.protobuf.Timestamp hot_to_standard_time = 12;
}

// Information about a backup.
message BackupInfo {
  // Output only. Name of the backup.
  string backup = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The time that the backup was started. Row data in the backup
  // will be no older than this timestamp.
  google.protobuf.Timestamp start_time = 2
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. This time that the backup was finished. Row data in the
  // backup will be no newer than this timestamp.
  google.protobuf.Timestamp end_time = 3
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Name of the table the backup was created from.
  string source_table = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. Name of the backup from which this backup was copied. If a
  // backup is not created by copying a backup, this field will be empty. Values
  // are of the form:
  // projects/<project>/instances/<instance>/clusters/<cluster>/backups/<backup>
  string source_backup = 10 [(google.api.field_behavior) = OUTPUT_ONLY];
}

// Indicates the type of the restore source.
enum RestoreSourceType {
  // No restore associated.
  RESTORE_SOURCE_TYPE_UNSPECIFIED = 0;

  // A backup was used as the source of the restore.
  BACKUP = 1;
}

// Represents a protobuf schema.
message ProtoSchema {
  // Required. Contains a protobuf-serialized
  // [google.protobuf.FileDescriptorSet](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto),
  // which could include multiple proto files.
  // To generate it, [install](https://grpc.io/docs/protoc-installation/) and
  // run `protoc` with
  // `--include_imports` and `--descriptor_set_out`. For example, to generate
  // for moon/shot/app.proto, run
  // ```
  // $protoc  --proto_path=/app_path --proto_path=/lib_path \
  //          --include_imports \
  //          --descriptor_set_out=descriptors.pb \
  //          moon/shot/app.proto
  // ```
  // For more details, see protobuffer [self
  // description](https://developers.google.com/protocol-buffers/docs/techniques#self-description).
  bytes proto_descriptors = 2 [(google.api.field_behavior) = REQUIRED];
}

// A named collection of related schemas.
message SchemaBundle {
  option (google.api.resource) = {
    type: "bigtableadmin.googleapis.com/SchemaBundle"
    pattern: "projects/{project}/instances/{instance}/tables/{table}/schemaBundles/{schema_bundle}"
    plural: "schemaBundles"
    singular: "schemaBundle"
  };

  // Identifier. The unique name identifying this schema bundle.
  // Values are of the form
  // `projects/{project}/instances/{instance}/tables/{table}/schemaBundles/{schema_bundle}`
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // The type of this schema bundle. The oneof case cannot change after
  // creation.
  oneof type {
    // Schema for Protobufs.
    ProtoSchema proto_schema = 2;
  }

  // Optional. The etag for this schema bundle.
  // This may be sent on update and delete requests to ensure the
  // client has an up-to-date value before proceeding. The server
  // returns an ABORTED error on a mismatched etag.
  string etag = 3 [(google.api.field_behavior) = OPTIONAL];
}
