diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index a4e204df0f..a1637b3c6b 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -29,7 +29,7 @@ com.google.cloud google-cloud-bigtable - 2.50.0 + 2.67.0 @@ -49,6 +49,11 @@ + + + ../snippets/src/main/resources + + org.codehaus.mojo diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 0f1dcdbf4b..21b929024f 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -48,6 +48,11 @@ + + + ../snippets/src/main/resources + + org.codehaus.mojo diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index acad499184..83938c31f3 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 com.google.cloud google-cloud-bigtable-snippets @@ -29,7 +31,7 @@ com.google.cloud libraries-bom - 26.50.0 + 26.69.0 pom import @@ -56,4 +58,18 @@ test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + + **/SingerProto.java + + + + diff --git a/samples/snippets/src/main/java/com/example/bigtable/SchemaBundleExample.java b/samples/snippets/src/main/java/com/example/bigtable/SchemaBundleExample.java new file mode 100644 index 0000000000..7ac0af3fcb --- /dev/null +++ b/samples/snippets/src/main/java/com/example/bigtable/SchemaBundleExample.java @@ -0,0 +1,232 @@ +/* + * 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. + */ + +package com.example.bigtable; + +import com.google.api.gax.rpc.NotFoundException; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings; +import com.google.cloud.bigtable.admin.v2.models.CreateSchemaBundleRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; +import com.google.cloud.bigtable.admin.v2.models.SchemaBundle; +import com.google.cloud.bigtable.admin.v2.models.Table; +import com.google.cloud.bigtable.admin.v2.models.UpdateSchemaBundleRequest; +import com.google.protobuf.ByteString; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.InvalidProtocolBufferException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * This example demonstrates the usage of BigtableTableAdminClient to create, configure and delete a + * Cloud Bigtable schema bundle. + * + *

The example follows these steps: + * + *

    + *
  1. Creates a Bigtable table. + *
  2. Creates a schema bundle. + *
  3. Updates a schema bundle. + *
  4. Gets the schema bundle. + *
  5. Lists all schema bundles for the table. + *
  6. Deletes the schema bundle. + *
  7. Deletes the table. + *
+ */ +public class SchemaBundleExample { + + private static final String COLUMN_FAMILY = "cf"; + private static final String PROTO_FILE_PATH = "com/example/bigtable/descriptors.pb"; + private final String tableId; + private final String schemaBundleId; + private final BigtableTableAdminClient adminClient; + + public static void main(String[] args) throws IOException { + if (args.length != 2) { + System.out.println("Missing required project id or instance id"); + return; + } + String projectId = args[0]; + String instanceId = args[1]; + + SchemaBundleExample example = + new SchemaBundleExample(projectId, instanceId, "test-table", "test-schema-bundle"); + example.run(); + } + + public SchemaBundleExample( + String projectId, String instanceId, String tableId, String schemaBundleId) + throws IOException { + this.tableId = tableId; + this.schemaBundleId = schemaBundleId; + + // Creates the settings to configure a bigtable table admin client. + BigtableTableAdminSettings adminSettings = + BigtableTableAdminSettings.newBuilder() + .setProjectId(projectId) + .setInstanceId(instanceId) + .build(); + + // Creates a bigtable table admin client. + adminClient = BigtableTableAdminClient.create(adminSettings); + } + + public void close() { + adminClient.close(); + } + + public void run() { + createTable(); + createSchemaBundle(); + updateSchemaBundle(); + getSchemaBundle(); + listAllSchemaBundles(); + deleteSchemaBundle(); + deleteTable(); + close(); + } + + public void createTable() { + // Checks if table exists, creates table if it does not exist. + if (!adminClient.exists(tableId)) { + System.out.println("Table does not exist, creating table: " + tableId); + CreateTableRequest createTableRequest = + CreateTableRequest.of(tableId).addFamily(COLUMN_FAMILY); + Table table = adminClient.createTable(createTableRequest); + System.out.printf("Table: %s created successfully%n", table.getId()); + } + } + + public void deleteTable() { + // Deletes the entire table. + System.out.println("\nDelete table: " + tableId); + try { + adminClient.deleteTable(tableId); + System.out.printf("Table: %s deleted successfully%n", tableId); + } catch (NotFoundException e) { + System.err.println("Failed to delete a non-existent table: " + e.getMessage()); + } + } + + /** Demonstrates how to create a schema bundle under a table with the specified schema. */ + public void createSchemaBundle() { + // Checks if the schema bundle exists, creates it if it does not exist. + try { + adminClient.getSchemaBundle(tableId, schemaBundleId); + } catch (NotFoundException exception) { + System.out.printf("%nCreating schema bundle %s in table %s%n", schemaBundleId, tableId); + // [START bigtable_create_schema_bundle] + try { + InputStream in = getClass().getClassLoader().getResourceAsStream(PROTO_FILE_PATH); + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(tableId, schemaBundleId) + .setProtoSchema(ByteString.readFrom(in)); + SchemaBundle schemaBundle = adminClient.createSchemaBundle(request); + System.out.printf("Schema bundle: %s created successfully%n", schemaBundle.getId()); + } catch (NotFoundException e) { + System.err.println( + "Failed to create a schema bundle from a non-existent table: " + e.getMessage()); + } catch (IOException e) { + throw new RuntimeException(e); + } + // [END bigtable_create_schema_bundle] + } + } + + /** Demonstrates how to modify a schema bundle. */ + public void updateSchemaBundle() { + System.out.printf("%nUpdating schema bundle %s in table %s%n", schemaBundleId, tableId); + // [START bigtable_update_schema_bundle] + try { + InputStream in = getClass().getClassLoader().getResourceAsStream(PROTO_FILE_PATH); + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(tableId, schemaBundleId) + .setProtoSchema(ByteString.readFrom(in)); + SchemaBundle schemaBundle = adminClient.updateSchemaBundle(request); + System.out.printf("Schema bundle: %s updated successfully%n", schemaBundle.getId()); + } catch (NotFoundException e) { + System.err.println("Failed to modify a non-existent schema bundle: " + e.getMessage()); + } catch (IOException e) { + throw new RuntimeException(e); + } + // [END bigtable_update_schema_bundle] + } + + /** Demonstrates how to get a schema bundle's definition. */ + public SchemaBundle getSchemaBundle() { + System.out.printf("%nGetting schema bundle %s in table %s%n", schemaBundleId, tableId); + // [START bigtable_get_schema_bundle] + SchemaBundle schemaBundle = null; + try { + schemaBundle = adminClient.getSchemaBundle(tableId, schemaBundleId); + // Deserialize and print the FileDescriptorSet + DescriptorProtos.FileDescriptorSet fileDescriptorSet = + DescriptorProtos.FileDescriptorSet.parseFrom(schemaBundle.getProtoSchema()); + + System.out.println("--------- Deserialized FileDescriptorSet ---------"); + for (DescriptorProtos.FileDescriptorProto fileDescriptorProto : + fileDescriptorSet.getFileList()) { + System.out.println("File: " + fileDescriptorProto.getName()); + System.out.println(" Package: " + fileDescriptorProto.getPackage()); + for (DescriptorProtos.DescriptorProto messageType : + fileDescriptorProto.getMessageTypeList()) { + System.out.println(" Message: " + messageType.getName()); + } + } + System.out.println("--------------------------------------------------"); + } catch (InvalidProtocolBufferException e) { + System.err.println("Failed to parse FileDescriptorSet: " + e.getMessage()); + } catch (NotFoundException e) { + System.err.println( + "Failed to retrieve metadata from a non-existent schema bundle: " + e.getMessage()); + } + // [END bigtable_get_schema_bundle] + return schemaBundle; + } + + /** Demonstrates how to list all schema bundles within a table. */ + public List listAllSchemaBundles() { + System.out.printf("%nListing schema bundles in table %s%n", tableId); + // [START bigtable_list_schema_bundles] + List schemaBundleIds = new ArrayList<>(); + try { + schemaBundleIds = adminClient.listSchemaBundles(tableId); + for (String schemaBundleId : schemaBundleIds) { + System.out.println(schemaBundleId); + } + } catch (NotFoundException e) { + System.err.println( + "Failed to list schema bundles from a non-existent table: " + e.getMessage()); + } + // [END bigtable_list_schema_bundles] + return schemaBundleIds; + } + + /** Demonstrates how to delete a schema bundle. */ + public void deleteSchemaBundle() { + System.out.printf("%nDeleting schema bundle %s in table %s%n", schemaBundleId, tableId); + // [START bigtable_delete_schema_bundle] + try { + adminClient.deleteSchemaBundle(tableId, schemaBundleId); + System.out.printf("SchemaBundle: %s deleted successfully%n", schemaBundleId); + } catch (NotFoundException e) { + System.err.println("Failed to delete a non-existent schema bundle: " + e.getMessage()); + } + // [END bigtable_delete_schema_bundle] + } +} diff --git a/samples/snippets/src/main/resources/com/example/bigtable/README.md b/samples/snippets/src/main/resources/com/example/bigtable/README.md new file mode 100644 index 0000000000..da219f2e66 --- /dev/null +++ b/samples/snippets/src/main/resources/com/example/bigtable/README.md @@ -0,0 +1,6 @@ +#### To generate SingerProto.java and descriptors.pb file from singer.proto using `protoc` +```shell +cd samples/snippets/src/main/resources/ +protoc --proto_path=com/example/bigtable/ --include_imports --descriptor_set_out=com/example/bigtable/descriptors.pb \ +--java_out=. com/example/bigtable/singer.proto +``` diff --git a/samples/snippets/src/main/resources/com/example/bigtable/SingerProto.java b/samples/snippets/src/main/resources/com/example/bigtable/SingerProto.java new file mode 100644 index 0000000000..4c5714ebd3 --- /dev/null +++ b/samples/snippets/src/main/resources/com/example/bigtable/SingerProto.java @@ -0,0 +1,1159 @@ +/* + * 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. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: singer.proto +// Protobuf Java Version: 4.32.0 + +package com.example.bigtable; + +@com.google.protobuf.Generated +public final class SingerProto { + private SingerProto() {} + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 32, + /* patch= */ 0, + /* suffix= */ "", + SingerProto.class.getName()); + } + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + /** + * Protobuf enum {@code examples.bigtable.music.Genre} + */ + public enum Genre + implements com.google.protobuf.ProtocolMessageEnum { + /** + * POP = 0; + */ + POP(0), + /** + * JAZZ = 1; + */ + JAZZ(1), + /** + * FOLK = 2; + */ + FOLK(2), + /** + * ROCK = 3; + */ + ROCK(3), + UNRECOGNIZED(-1), + ; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 32, + /* patch= */ 0, + /* suffix= */ "", + Genre.class.getName()); + } + /** + * POP = 0; + */ + public static final int POP_VALUE = 0; + /** + * JAZZ = 1; + */ + public static final int JAZZ_VALUE = 1; + /** + * FOLK = 2; + */ + public static final int FOLK_VALUE = 2; + /** + * ROCK = 3; + */ + public static final int ROCK_VALUE = 3; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Genre valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Genre forNumber(int value) { + switch (value) { + case 0: return POP; + case 1: return JAZZ; + case 2: return FOLK; + case 3: return ROCK; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Genre> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Genre findValueByNumber(int number) { + return Genre.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return com.example.bigtable.SingerProto.getDescriptor().getEnumTypes().get(0); + } + + private static final Genre[] VALUES = values(); + + public static Genre valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Genre(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:examples.bigtable.music.Genre) + } + + public interface SingerInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:examples.bigtable.music.SingerInfo) + com.google.protobuf.MessageOrBuilder { + + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + boolean hasSingerId(); + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + long getSingerId(); + + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + boolean hasBirthDate(); + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + java.lang.String getBirthDate(); + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + com.google.protobuf.ByteString + getBirthDateBytes(); + + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + boolean hasNationality(); + /** + * optional string nationality = 3; + * @return The nationality. + */ + java.lang.String getNationality(); + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + com.google.protobuf.ByteString + getNationalityBytes(); + + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + boolean hasGenre(); + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + int getGenreValue(); + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The genre. + */ + com.example.bigtable.SingerProto.Genre getGenre(); + } + /** + * Protobuf type {@code examples.bigtable.music.SingerInfo} + */ + public static final class SingerInfo extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:examples.bigtable.music.SingerInfo) + SingerInfoOrBuilder { + private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 32, + /* patch= */ 0, + /* suffix= */ "", + SingerInfo.class.getName()); + } + // Use SingerInfo.newBuilder() to construct. + private SingerInfo(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private SingerInfo() { + birthDate_ = ""; + nationality_ = ""; + genre_ = 0; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.example.bigtable.SingerProto.internal_static_examples_bigtable_music_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.example.bigtable.SingerProto.internal_static_examples_bigtable_music_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.example.bigtable.SingerProto.SingerInfo.class, com.example.bigtable.SingerProto.SingerInfo.Builder.class); + } + + private int bitField0_; + public static final int SINGER_ID_FIELD_NUMBER = 1; + private long singerId_ = 0L; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + + public static final int BIRTH_DATE_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object birthDate_ = ""; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + @java.lang.Override + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + @java.lang.Override + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + birthDate_ = s; + return s; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NATIONALITY_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object nationality_ = ""; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + @java.lang.Override + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + @java.lang.Override + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nationality_ = s; + return s; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int GENRE_FIELD_NUMBER = 4; + private int genre_ = 0; + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + @java.lang.Override public int getGenreValue() { + return genre_; + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override public com.example.bigtable.SingerProto.Genre getGenre() { + com.example.bigtable.SingerProto.Genre result = com.example.bigtable.SingerProto.Genre.forNumber(genre_); + return result == null ? com.example.bigtable.SingerProto.Genre.UNRECOGNIZED : result; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeInt64(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeEnum(4, genre_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(1, singerId_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, birthDate_); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, nationality_); + } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(4, genre_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.example.bigtable.SingerProto.SingerInfo)) { + return super.equals(obj); + } + com.example.bigtable.SingerProto.SingerInfo other = (com.example.bigtable.SingerProto.SingerInfo) obj; + + if (hasSingerId() != other.hasSingerId()) return false; + if (hasSingerId()) { + if (getSingerId() + != other.getSingerId()) return false; + } + if (hasBirthDate() != other.hasBirthDate()) return false; + if (hasBirthDate()) { + if (!getBirthDate() + .equals(other.getBirthDate())) return false; + } + if (hasNationality() != other.hasNationality()) return false; + if (hasNationality()) { + if (!getNationality() + .equals(other.getNationality())) return false; + } + if (hasGenre() != other.hasGenre()) return false; + if (hasGenre()) { + if (genre_ != other.genre_) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasSingerId()) { + hash = (37 * hash) + SINGER_ID_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getSingerId()); + } + if (hasBirthDate()) { + hash = (37 * hash) + BIRTH_DATE_FIELD_NUMBER; + hash = (53 * hash) + getBirthDate().hashCode(); + } + if (hasNationality()) { + hash = (37 * hash) + NATIONALITY_FIELD_NUMBER; + hash = (53 * hash) + getNationality().hashCode(); + } + if (hasGenre()) { + hash = (37 * hash) + GENRE_FIELD_NUMBER; + hash = (53 * hash) + genre_; + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static com.example.bigtable.SingerProto.SingerInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static com.example.bigtable.SingerProto.SingerInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static com.example.bigtable.SingerProto.SingerInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(com.example.bigtable.SingerProto.SingerInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code examples.bigtable.music.SingerInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:examples.bigtable.music.SingerInfo) + com.example.bigtable.SingerProto.SingerInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.example.bigtable.SingerProto.internal_static_examples_bigtable_music_SingerInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.example.bigtable.SingerProto.internal_static_examples_bigtable_music_SingerInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.example.bigtable.SingerProto.SingerInfo.class, com.example.bigtable.SingerProto.SingerInfo.Builder.class); + } + + // Construct using com.example.bigtable.SingerProto.SingerInfo.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + singerId_ = 0L; + birthDate_ = ""; + nationality_ = ""; + genre_ = 0; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.example.bigtable.SingerProto.internal_static_examples_bigtable_music_SingerInfo_descriptor; + } + + @java.lang.Override + public com.example.bigtable.SingerProto.SingerInfo getDefaultInstanceForType() { + return com.example.bigtable.SingerProto.SingerInfo.getDefaultInstance(); + } + + @java.lang.Override + public com.example.bigtable.SingerProto.SingerInfo build() { + com.example.bigtable.SingerProto.SingerInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.example.bigtable.SingerProto.SingerInfo buildPartial() { + com.example.bigtable.SingerProto.SingerInfo result = new com.example.bigtable.SingerProto.SingerInfo(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(com.example.bigtable.SingerProto.SingerInfo result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.singerId_ = singerId_; + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.birthDate_ = birthDate_; + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.nationality_ = nationality_; + to_bitField0_ |= 0x00000004; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.genre_ = genre_; + to_bitField0_ |= 0x00000008; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.example.bigtable.SingerProto.SingerInfo) { + return mergeFrom((com.example.bigtable.SingerProto.SingerInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.example.bigtable.SingerProto.SingerInfo other) { + if (other == com.example.bigtable.SingerProto.SingerInfo.getDefaultInstance()) return this; + if (other.hasSingerId()) { + setSingerId(other.getSingerId()); + } + if (other.hasBirthDate()) { + birthDate_ = other.birthDate_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasNationality()) { + nationality_ = other.nationality_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (other.hasGenre()) { + setGenreValue(other.getGenreValue()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + singerId_ = input.readInt64(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 18: { + birthDate_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + nationality_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 32: { + genre_ = input.readEnum(); + bitField0_ |= 0x00000008; + break; + } // case 32 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private long singerId_ ; + /** + * optional int64 singer_id = 1; + * @return Whether the singerId field is set. + */ + @java.lang.Override + public boolean hasSingerId() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * optional int64 singer_id = 1; + * @return The singerId. + */ + @java.lang.Override + public long getSingerId() { + return singerId_; + } + /** + * optional int64 singer_id = 1; + * @param value The singerId to set. + * @return This builder for chaining. + */ + public Builder setSingerId(long value) { + + singerId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * optional int64 singer_id = 1; + * @return This builder for chaining. + */ + public Builder clearSingerId() { + bitField0_ = (bitField0_ & ~0x00000001); + singerId_ = 0L; + onChanged(); + return this; + } + + private java.lang.Object birthDate_ = ""; + /** + * optional string birth_date = 2; + * @return Whether the birthDate field is set. + */ + public boolean hasBirthDate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * optional string birth_date = 2; + * @return The birthDate. + */ + public java.lang.String getBirthDate() { + java.lang.Object ref = birthDate_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + birthDate_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string birth_date = 2; + * @return The bytes for birthDate. + */ + public com.google.protobuf.ByteString + getBirthDateBytes() { + java.lang.Object ref = birthDate_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + birthDate_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string birth_date = 2; + * @param value The birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDate( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + birthDate_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @return This builder for chaining. + */ + public Builder clearBirthDate() { + birthDate_ = getDefaultInstance().getBirthDate(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * optional string birth_date = 2; + * @param value The bytes for birthDate to set. + * @return This builder for chaining. + */ + public Builder setBirthDateBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + birthDate_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object nationality_ = ""; + /** + * optional string nationality = 3; + * @return Whether the nationality field is set. + */ + public boolean hasNationality() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * optional string nationality = 3; + * @return The nationality. + */ + public java.lang.String getNationality() { + java.lang.Object ref = nationality_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nationality_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string nationality = 3; + * @return The bytes for nationality. + */ + public com.google.protobuf.ByteString + getNationalityBytes() { + java.lang.Object ref = nationality_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nationality_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string nationality = 3; + * @param value The nationality to set. + * @return This builder for chaining. + */ + public Builder setNationality( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + nationality_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @return This builder for chaining. + */ + public Builder clearNationality() { + nationality_ = getDefaultInstance().getNationality(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * optional string nationality = 3; + * @param value The bytes for nationality to set. + * @return This builder for chaining. + */ + public Builder setNationalityBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + nationality_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private int genre_ = 0; + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return Whether the genre field is set. + */ + @java.lang.Override public boolean hasGenre() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The enum numeric value on the wire for genre. + */ + @java.lang.Override public int getGenreValue() { + return genre_; + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @param value The enum numeric value on the wire for genre to set. + * @return This builder for chaining. + */ + public Builder setGenreValue(int value) { + genre_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return The genre. + */ + @java.lang.Override + public com.example.bigtable.SingerProto.Genre getGenre() { + com.example.bigtable.SingerProto.Genre result = com.example.bigtable.SingerProto.Genre.forNumber(genre_); + return result == null ? com.example.bigtable.SingerProto.Genre.UNRECOGNIZED : result; + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @param value The genre to set. + * @return This builder for chaining. + */ + public Builder setGenre(com.example.bigtable.SingerProto.Genre value) { + if (value == null) { throw new NullPointerException(); } + bitField0_ |= 0x00000008; + genre_ = value.getNumber(); + onChanged(); + return this; + } + /** + * optional .examples.bigtable.music.Genre genre = 4; + * @return This builder for chaining. + */ + public Builder clearGenre() { + bitField0_ = (bitField0_ & ~0x00000008); + genre_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:examples.bigtable.music.SingerInfo) + } + + // @@protoc_insertion_point(class_scope:examples.bigtable.music.SingerInfo) + private static final com.example.bigtable.SingerProto.SingerInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new com.example.bigtable.SingerProto.SingerInfo(); + } + + public static com.example.bigtable.SingerProto.SingerInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SingerInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.example.bigtable.SingerProto.SingerInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_examples_bigtable_music_SingerInfo_descriptor; + private static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_examples_bigtable_music_SingerInfo_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\014singer.proto\022\027examples.bigtable.music\"" + + "\302\001\n\nSingerInfo\022\026\n\tsinger_id\030\001 \001(\003H\000\210\001\001\022\027" + + "\n\nbirth_date\030\002 \001(\tH\001\210\001\001\022\030\n\013nationality\030\003" + + " \001(\tH\002\210\001\001\0222\n\005genre\030\004 \001(\0162\036.examples.bigt" + + "able.music.GenreH\003\210\001\001B\014\n\n_singer_idB\r\n\013_" + + "birth_dateB\016\n\014_nationalityB\010\n\006_genre*.\n\005" + + "Genre\022\007\n\003POP\020\000\022\010\n\004JAZZ\020\001\022\010\n\004FOLK\020\002\022\010\n\004RO" + + "CK\020\003B%\n\024com.example.bigtableB\013SingerProt" + + "oP\000b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_examples_bigtable_music_SingerInfo_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_examples_bigtable_music_SingerInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_examples_bigtable_music_SingerInfo_descriptor, + new java.lang.String[] { "SingerId", "BirthDate", "Nationality", "Genre", }); + descriptor.resolveAllFeaturesImmutable(); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/samples/snippets/src/main/resources/com/example/bigtable/descriptors.pb b/samples/snippets/src/main/resources/com/example/bigtable/descriptors.pb new file mode 100644 index 0000000000..c8726c6084 Binary files /dev/null and b/samples/snippets/src/main/resources/com/example/bigtable/descriptors.pb differ diff --git a/samples/snippets/src/main/resources/com/example/bigtable/singer.proto b/samples/snippets/src/main/resources/com/example/bigtable/singer.proto new file mode 100644 index 0000000000..8d697fdfd9 --- /dev/null +++ b/samples/snippets/src/main/resources/com/example/bigtable/singer.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package examples.bigtable.music; + +option java_package = "com.example.bigtable"; +option java_outer_classname = "SingerProto"; +option java_multiple_files = false; + +message SingerInfo { + optional int64 singer_id = 1; + optional string birth_date = 2; + optional string nationality = 3; + optional Genre genre = 4; +} + +enum Genre { + POP = 0; + JAZZ = 1; + FOLK = 2; + ROCK = 3; +} \ No newline at end of file diff --git a/samples/snippets/src/test/java/com/example/bigtable/SchemaBundleExampleTest.java b/samples/snippets/src/test/java/com/example/bigtable/SchemaBundleExampleTest.java new file mode 100644 index 0000000000..c76d1288eb --- /dev/null +++ b/samples/snippets/src/test/java/com/example/bigtable/SchemaBundleExampleTest.java @@ -0,0 +1,138 @@ +/* + * 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. + */ + +package com.example.bigtable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; + +import com.google.api.gax.rpc.NotFoundException; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings; +import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; +import com.google.cloud.bigtable.admin.v2.models.SchemaBundle; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SchemaBundleExampleTest extends BigtableBaseTest { + + private static final String TABLE_PREFIX = "table"; + private static final String SCHEMA_BUNDLE_PREFIX = "schema-bundle"; + private static final String COLUMN_FAMILY = "cf"; + private String tableId; + private String schemaBundleId; + private static BigtableTableAdminClient adminClient; + private SchemaBundleExample schemaBundleExample; + + @BeforeClass + public static void beforeClass() throws IOException { + initializeVariables(); + BigtableTableAdminSettings adminSettings = + BigtableTableAdminSettings.newBuilder() + .setProjectId(projectId) + .setInstanceId(instanceId) + .build(); + adminClient = BigtableTableAdminClient.create(adminSettings); + } + + @AfterClass + public static void afterClass() { + garbageCollect(); + adminClient.close(); + } + + @Before + public void setup() throws IOException { + tableId = generateResourceId(TABLE_PREFIX); + schemaBundleId = generateResourceId(SCHEMA_BUNDLE_PREFIX); + schemaBundleExample = new SchemaBundleExample(projectId, instanceId, tableId, schemaBundleId); + adminClient.createTable(CreateTableRequest.of(tableId).addFamily(COLUMN_FAMILY)); + } + + @After + public void after() { + if (adminClient.exists(tableId)) { + adminClient.deleteTable(tableId); + } + schemaBundleExample.close(); + } + + @Test + public void testRunDoesNotFail() { + schemaBundleExample.run(); + } + + @Test + public void testSchemaBundleCreateUpdateDelete() throws IOException { + // Creates a schema bundle. + schemaBundleExample.createSchemaBundle(); + SchemaBundle schemaBundle = adminClient.getSchemaBundle(tableId, schemaBundleId); + assertEquals(schemaBundle.getId(), schemaBundleId); + + // Updates the schema bundle. + schemaBundleExample.updateSchemaBundle(); + SchemaBundle updatedSchemaBundle = adminClient.getSchemaBundle(tableId, schemaBundleId); + assertNotEquals(schemaBundle, updatedSchemaBundle); + + // Deletes the schema bundle. + schemaBundleExample.deleteSchemaBundle(); + assertThrows( + NotFoundException.class, () -> adminClient.getSchemaBundle(tableId, schemaBundleId)); + } + + @Test + public void testGetSchemaBundle() { + schemaBundleExample.createSchemaBundle(); + SchemaBundle schemaBundle = schemaBundleExample.getSchemaBundle(); + assertNotNull(schemaBundle); + assertEquals(schemaBundle.getId(), schemaBundleId); + } + + @Test + public void testListSchemaBundles() { + schemaBundleExample.createSchemaBundle(); + List schemaBundleIds = schemaBundleExample.listAllSchemaBundles(); + assertEquals(schemaBundleIds.size(), 1); + assertEquals(schemaBundleIds.get(0), schemaBundleId); + } + + private static void garbageCollect() { + Pattern timestampPattern = Pattern.compile(TABLE_PREFIX + "-([0-9a-f]+)-([0-9a-f]+)"); + for (String tableId : adminClient.listTables()) { + Matcher matcher = timestampPattern.matcher(tableId); + if (!matcher.matches()) { + continue; + } + String timestampStr = matcher.group(1); + long timestamp = Long.parseLong(timestampStr, 16); + if (System.currentTimeMillis() - timestamp < TimeUnit.MINUTES.toMillis(10)) { + continue; + } + System.out.println("\nGarbage collecting orphaned table: " + tableId); + adminClient.deleteTable(tableId); + } + } +}