From 7d342fbb3262d2b0b4e3f26d12ec3e2ce38bec6b Mon Sep 17 00:00:00 2001 From: lixiachen Date: Thu, 10 Sep 2026 16:59:52 -0400 Subject: [PATCH 1/3] feat: add support for Avro schema to SchemaBundle model classes --- .../v2/models/CreateSchemaBundleRequest.java | 24 +++- .../admin/v2/models/SchemaBundle.java | 12 +- .../v2/models/UpdateSchemaBundleRequest.java | 25 +++- .../v2/BigtableTableAdminClientTests.java | 123 ++++++++++++++++++ .../models/CreateSchemaBundleRequestTest.java | 80 ++++++++++++ .../admin/v2/models/SchemaBundleTest.java | 106 +++++++++++++++ .../models/UpdateSchemaBundleRequestTest.java | 113 ++++++++++++++++ 7 files changed, 476 insertions(+), 7 deletions(-) diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java index b6d88e2b53ff..a70ef6a15386 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigtable.admin.v2.models; import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.ProtoSchema; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.common.base.Objects; @@ -25,6 +26,8 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; import javax.annotation.Nonnull; /** @@ -70,9 +73,24 @@ public CreateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF /** Sets the proto schema for this schema bundle. */ public CreateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); - requestBuilder.setSchemaBundle( - com.google.bigtable.admin.v2.SchemaBundle.newBuilder() - .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema))); + requestBuilder + .getSchemaBundleBuilder() + .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); + return this; + } + + /** Sets the avro schema for this schema bundle. */ + public CreateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { + Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + return setAvroSchema(Collections.singletonList(avroSchema)); + } + + /** Sets the avro schema for this schema bundle. */ + public CreateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { + Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + requestBuilder + .getSchemaBundleBuilder() + .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); return this; } diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java index 7782c335a2ef..d47299f226be 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java @@ -20,6 +20,7 @@ import com.google.bigtable.admin.v2.SchemaBundleName; import com.google.common.base.Objects; import com.google.common.base.Preconditions; +import java.util.List; import javax.annotation.Nonnull; /** @@ -42,7 +43,8 @@ private SchemaBundle(@Nonnull com.google.bigtable.admin.v2.SchemaBundle proto) { Preconditions.checkNotNull(proto); Preconditions.checkArgument(!proto.getName().isEmpty(), "SchemaBundle must have a name"); Preconditions.checkArgument( - proto.hasProtoSchema(), "Schemabundle must have a proto_schema field"); + proto.hasProtoSchema() || proto.hasAvroSchema(), + "Schemabundle must have a proto_schema or avro_schema field"); this.proto = proto; this.schemaBundleName = SchemaBundleName.parse(proto.getName()); } @@ -67,6 +69,14 @@ public com.google.protobuf.ByteString getProtoSchema() { throw new IllegalStateException("This SchemaBundle doesn't have a valid type specified"); } + /** Gets the avro schema of this schema bundle. */ + public List getAvroSchema() { + if (proto.hasAvroSchema()) { + return proto.getAvroSchema().getJsonSchemasList(); + } + throw new IllegalStateException("This SchemaBundle does not contain an Avro schema"); + } + /** * Creates the request protobuf. This method is considered an internal implementation detail and * not meant to be used by applications. diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java index 904c4d0097f4..0e4f03d2f6e6 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigtable.admin.v2.models; import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.ProtoSchema; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.common.base.Objects; @@ -27,6 +28,8 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; import javax.annotation.Nonnull; /** @@ -90,13 +93,29 @@ public UpdateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF public UpdateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) throws IOException { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); - requestBuilder.setSchemaBundle( - com.google.bigtable.admin.v2.SchemaBundle.newBuilder() - .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema))); + requestBuilder + .getSchemaBundleBuilder() + .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); updateFieldMask(com.google.bigtable.admin.v2.SchemaBundle.PROTO_SCHEMA_FIELD_NUMBER); return this; } + /** Sets the avro schema for this schema bundle. */ + public UpdateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { + Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + return setAvroSchema(Collections.singletonList(avroSchema)); + } + + /** Sets the avro schema for this schema bundle. */ + public UpdateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { + Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + requestBuilder + .getSchemaBundleBuilder() + .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); + updateFieldMask(com.google.bigtable.admin.v2.SchemaBundle.AVRO_SCHEMA_FIELD_NUMBER); + return this; + } + /** * Configures if safety warnings should be disabled. If set, then non backwards compatible changes * are allowed. diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java index cecf509db1a7..d5da42b3c4a7 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java @@ -28,6 +28,7 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.api.gax.rpc.testing.FakeOperationSnapshot; import com.google.bigtable.admin.v2.AuthorizedViewName; +import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.Backup.State; import com.google.bigtable.admin.v2.BackupInfo; import com.google.bigtable.admin.v2.ChangeStreamConfig; @@ -142,6 +143,9 @@ public class BigtableTableAdminClientTests { private static final String TEST_PROTO_SCHEMA_BUNDLE = "proto_schema_bundle.pb"; // Location: `google-cloud-bigtable/src/test/resources/updated_proto_schema_bundle.pb` private static final String TEST_UPDATED_PROTO_SCHEMA_BUNDLE = "updated_proto_schema_bundle.pb"; + private static final String TEST_AVRO_SCHEMA = "{\"type\": \"record\", \"name\": \"User\"}"; + private static final String TEST_UPDATED_AVRO_SCHEMA = + "{\"type\": \"record\", \"name\": \"UpdatedUser\"}"; private static final String INSTANCE_NAME = NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID); private static final String TABLE_NAME = @@ -1594,6 +1598,125 @@ public void testDeleteSchemaBundle() { assertThat(wasCalled.get()).isTrue(); } + @Test + public void testCreateSchemaBundleWithAvroSchema() { + // Setup + Mockito.when(mockStub.createSchemaBundleOperationCallable()) + .thenReturn(mockCreateSchemaBundleOperationCallable); + + com.google.bigtable.admin.v2.CreateSchemaBundleRequest expectedRequest = + com.google.bigtable.admin.v2.CreateSchemaBundleRequest.newBuilder() + .setParent(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID)) + .setSchemaBundleId(SCHEMA_BUNDLE_ID) + .setSchemaBundle( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA))) + .build(); + + com.google.bigtable.admin.v2.SchemaBundle expectedResponse = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA)) + .build(); + + mockOperationResult( + mockCreateSchemaBundleOperationCallable, + expectedRequest, + expectedResponse, + CreateSchemaBundleMetadata.newBuilder() + .setName(expectedRequest.getSchemaBundle().getName()) + .build()); + + CreateSchemaBundleRequest req = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + // Execute + SchemaBundle actualResult = adminClient.createSchemaBundle(req); + + // Verify + assertThat(actualResult).isEqualTo(SchemaBundle.fromProto(expectedResponse)); + assertThat(actualResult.getAvroSchema()).containsExactly(TEST_AVRO_SCHEMA); + } + + @Test + public void testUpdateSchemaBundleWithAvroSchema() { + // Setup + Mockito.when(mockStub.updateSchemaBundleOperationCallable()) + .thenReturn(mockUpdateSchemaBundleOperationCallable); + + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest expectedRequest = + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest.newBuilder() + .setSchemaBundle( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema( + AvroSchema.newBuilder().addJsonSchemas(TEST_UPDATED_AVRO_SCHEMA))) + .setUpdateMask(FieldMask.newBuilder().addPaths("avro_schema")) + .build(); + + com.google.bigtable.admin.v2.SchemaBundle expectedResponse = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_UPDATED_AVRO_SCHEMA)) + .build(); + + mockOperationResult( + mockUpdateSchemaBundleOperationCallable, + expectedRequest, + expectedResponse, + UpdateSchemaBundleMetadata.newBuilder() + .setName(expectedRequest.getSchemaBundle().getName()) + .build()); + + UpdateSchemaBundleRequest req = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA); + + // Execute + SchemaBundle actualResult = adminClient.updateSchemaBundle(req); + + // Verify + assertThat(actualResult).isEqualTo(SchemaBundle.fromProto(expectedResponse)); + assertThat(actualResult.getAvroSchema()).containsExactly(TEST_UPDATED_AVRO_SCHEMA); + } + + @Test + public void testGetSchemaBundleWithAvroSchema() { + // Setup + Mockito.when(mockStub.getSchemaBundleCallable()).thenReturn(mockGetSchemaBundleCallable); + + com.google.bigtable.admin.v2.GetSchemaBundleRequest expectedRequest = + com.google.bigtable.admin.v2.GetSchemaBundleRequest.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .build(); + + com.google.bigtable.admin.v2.SchemaBundle expectedResponse = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA)) + .build(); + + Mockito.when(mockGetSchemaBundleCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + SchemaBundle actualResult = adminClient.getSchemaBundle(TABLE_ID, SCHEMA_BUNDLE_ID); + + // Verify + assertThat(actualResult).isEqualTo(SchemaBundle.fromProto(expectedResponse)); + assertThat(actualResult.getAvroSchema()).containsExactly(TEST_AVRO_SCHEMA); + } + @Test public void testGetBackupIamPolicy() { // Setup diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java index 2d37eccff5b0..8bd24b8d12fe 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; import java.io.IOException; import java.net.URISyntaxException; @@ -39,6 +40,9 @@ public class CreateSchemaBundleRequestTest { private static final String TEST_PROTO_SCHEMA_BUNDLE = "proto_schema_bundle.pb"; // Location: `google-cloud-bigtable/src/test/resources/updated_proto_schema_bundle.pb` private static final String TEST_UPDATED_PROTO_SCHEMA_BUNDLE = "updated_proto_schema_bundle.pb"; + private static final String TEST_AVRO_SCHEMA = "{\"type\": \"record\", \"name\": \"User\"}"; + private static final String TEST_UPDATED_AVRO_SCHEMA = + "{\"type\": \"record\", \"name\": \"UpdatedUser\"}"; @Test public void testToProto() throws IOException, URISyntaxException { @@ -99,6 +103,82 @@ public void testHashCode() throws IOException, URISyntaxException { .hashCode()); } + @Test + public void testToProtoWithAvroSchema() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + com.google.bigtable.admin.v2.CreateSchemaBundleRequest requestProto = + com.google.bigtable.admin.v2.CreateSchemaBundleRequest.newBuilder() + .setParent(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID)) + .setSchemaBundleId(SCHEMA_BUNDLE_ID) + .setSchemaBundle( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setAvroSchema( + com.google.bigtable.admin.v2.AvroSchema.newBuilder() + .addJsonSchemas(TEST_AVRO_SCHEMA) + .build()) + .build()) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testToProtoWithAvroSchemaList() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(ImmutableList.of(TEST_AVRO_SCHEMA, TEST_UPDATED_AVRO_SCHEMA)); + + com.google.bigtable.admin.v2.CreateSchemaBundleRequest requestProto = + com.google.bigtable.admin.v2.CreateSchemaBundleRequest.newBuilder() + .setParent(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID)) + .setSchemaBundleId(SCHEMA_BUNDLE_ID) + .setSchemaBundle( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setAvroSchema( + com.google.bigtable.admin.v2.AvroSchema.newBuilder() + .addJsonSchemas(TEST_AVRO_SCHEMA) + .addJsonSchemas(TEST_UPDATED_AVRO_SCHEMA) + .build()) + .build()) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testEqualityWithAvroSchema() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + assertThat(request) + .isEqualTo( + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA)); + + assertThat(request) + .isNotEqualTo( + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA)); + } + + @Test + public void testHashCodeWithAvroSchema() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + assertThat(request.hashCode()) + .isEqualTo( + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA) + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA) + .hashCode()); + } + private String getResourceFilePath(String filePath) throws URISyntaxException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL protoSchema = cl.getResource(filePath); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundleTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundleTest.java index f83bc7dcc449..6baff720f0f7 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundleTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundleTest.java @@ -17,7 +17,9 @@ package com.google.cloud.bigtable.admin.v2.models; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.SchemaBundleName; import com.google.protobuf.ByteString; import org.junit.Test; @@ -146,4 +148,108 @@ public void testHashCode() { .build() .hashCode()); } + + @Test + public void testFromProtoWithAvroSchema() { + SchemaBundleName schemaBundleName = + SchemaBundleName.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID); + + com.google.bigtable.admin.v2.SchemaBundle schemaBundleProto = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema( + AvroSchema.newBuilder() + .addJsonSchemas("{\"type\": \"record\", \"name\": \"User\"}") + .build()) + .build(); + + SchemaBundle result = SchemaBundle.fromProto(schemaBundleProto); + + assertThat(result.getId()).isEqualTo(SCHEMA_BUNDLE_ID); + assertThat(result.getTableId()).isEqualTo(TABLE_ID); + assertThat(result.getAvroSchema()) + .containsExactly("{\"type\": \"record\", \"name\": \"User\"}"); + } + + @Test + public void testGetProtoSchemaThrowsOnAvroSchemaBundle() { + SchemaBundleName schemaBundleName = + SchemaBundleName.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID); + + com.google.bigtable.admin.v2.SchemaBundle schemaBundleProto = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema( + AvroSchema.newBuilder() + .addJsonSchemas("{\"type\": \"record\", \"name\": \"User\"}") + .build()) + .build(); + + SchemaBundle result = SchemaBundle.fromProto(schemaBundleProto); + + assertThrows(IllegalStateException.class, result::getProtoSchema); + } + + @Test + public void testGetAvroSchemaThrowsOnProtoSchemaBundle() { + SchemaBundleName schemaBundleName = + SchemaBundleName.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID); + + com.google.bigtable.admin.v2.SchemaBundle schemaBundleProto = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setProtoSchema( + com.google.bigtable.admin.v2.ProtoSchema.newBuilder() + .setProtoDescriptors(ByteString.copyFromUtf8("schema")) + .build()) + .build(); + + SchemaBundle result = SchemaBundle.fromProto(schemaBundleProto); + + assertThrows(IllegalStateException.class, result::getAvroSchema); + } + + @Test + public void testEqualityWithAvroSchema() { + SchemaBundleName schemaBundleName = + SchemaBundleName.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID); + com.google.bigtable.admin.v2.SchemaBundle proto = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas("schema").build()) + .build(); + SchemaBundle schemaBundle = SchemaBundle.fromProto(proto); + + assertThat(schemaBundle).isEqualTo(SchemaBundle.fromProto(proto)); + + assertThat(schemaBundle) + .isNotEqualTo( + SchemaBundle.fromProto( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas("schema2").build()) + .build())); + } + + @Test + public void testHashCodeWithAvroSchema() { + SchemaBundleName schemaBundleName = + SchemaBundleName.of(PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID); + com.google.bigtable.admin.v2.SchemaBundle proto = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas("schema").build()) + .build(); + SchemaBundle schemaBundle = SchemaBundle.fromProto(proto); + + assertThat(schemaBundle.hashCode()).isEqualTo(SchemaBundle.fromProto(proto).hashCode()); + + assertThat(schemaBundle.hashCode()) + .isNotEqualTo( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName(schemaBundleName.toString()) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas("schema").build()) + .build() + .hashCode()); + } } diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java index 994d56068aad..da8ee762d5b2 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java @@ -18,8 +18,10 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.ProtoSchema; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; import com.google.protobuf.FieldMask; import java.io.IOException; @@ -41,6 +43,9 @@ public class UpdateSchemaBundleRequestTest { private static final String TEST_PROTO_SCHEMA_BUNDLE = "proto_schema_bundle.pb"; // Location: `google-cloud-bigtable/src/test/resources/updated_proto_schema_bundle.pb` private static final String TEST_UPDATED_PROTO_SCHEMA_BUNDLE = "updated_proto_schema_bundle.pb"; + private static final String TEST_AVRO_SCHEMA = "{\"type\": \"record\", \"name\": \"User\"}"; + private static final String TEST_UPDATED_AVRO_SCHEMA = + "{\"type\": \"record\", \"name\": \"UpdatedUser\"}"; @Test public void testToProto() throws IOException, URISyntaxException { @@ -137,6 +142,114 @@ public void testHashCode() throws IOException, URISyntaxException { .hashCode()); } + @Test + public void testToProtoWithAvroSchema() { + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA) + .setIgnoreWarnings(true); + + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest requestProto = + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest.newBuilder() + .setSchemaBundle( + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA).build()) + .build()) + .setUpdateMask(FieldMask.newBuilder().addPaths("avro_schema")) + .setIgnoreWarnings(true) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testUpdateAvroSchema() { + com.google.bigtable.admin.v2.SchemaBundle existingSchemaBundle = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA).build()) + .build(); + + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(SchemaBundle.fromProto(existingSchemaBundle)) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA); + + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest requestProto = + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest.newBuilder() + .setSchemaBundle( + existingSchemaBundle.toBuilder() + .setAvroSchema( + AvroSchema.newBuilder().addJsonSchemas(TEST_UPDATED_AVRO_SCHEMA))) + .setUpdateMask(FieldMask.newBuilder().addPaths("avro_schema")) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testUpdateAvroSchemaList() { + com.google.bigtable.admin.v2.SchemaBundle existingSchemaBundle = + com.google.bigtable.admin.v2.SchemaBundle.newBuilder() + .setName( + NameUtil.formatSchemaBundleName( + PROJECT_ID, INSTANCE_ID, TABLE_ID, SCHEMA_BUNDLE_ID)) + .setAvroSchema(AvroSchema.newBuilder().addJsonSchemas(TEST_AVRO_SCHEMA).build()) + .build(); + + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(SchemaBundle.fromProto(existingSchemaBundle)) + .setAvroSchema(ImmutableList.of(TEST_AVRO_SCHEMA, TEST_UPDATED_AVRO_SCHEMA)); + + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest requestProto = + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest.newBuilder() + .setSchemaBundle( + existingSchemaBundle.toBuilder() + .setAvroSchema( + AvroSchema.newBuilder() + .addAllJsonSchemas( + ImmutableList.of(TEST_AVRO_SCHEMA, TEST_UPDATED_AVRO_SCHEMA)))) + .setUpdateMask(FieldMask.newBuilder().addPaths("avro_schema")) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + + @Test + public void testEqualityWithAvroSchema() { + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + assertThat(request) + .isEqualTo( + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA)); + + assertThat(request) + .isNotEqualTo( + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA)); + } + + @Test + public void testHashCodeWithAvroSchema() { + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + + assertThat(request.hashCode()) + .isEqualTo( + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA) + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_UPDATED_AVRO_SCHEMA) + .hashCode()); + } + private String getResourceFilePath(String filePath) throws URISyntaxException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL protoSchema = cl.getResource(filePath); From cf778b7416c5d961bde13046c6b5f1153bebb765 Mon Sep 17 00:00:00 2001 From: lixiachen Date: Tue, 15 Sep 2026 17:06:08 -0400 Subject: [PATCH 2/3] Address review comments --- .../v2/models/CreateSchemaBundleRequest.java | 8 +++++- .../admin/v2/models/SchemaBundle.java | 2 ++ .../v2/models/UpdateSchemaBundleRequest.java | 8 +++++- .../models/CreateSchemaBundleRequestTest.java | 27 +++++++++++++++++++ .../models/UpdateSchemaBundleRequestTest.java | 27 +++++++++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java index a70ef6a15386..e076896054d8 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java @@ -73,6 +73,9 @@ public CreateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF /** Sets the proto schema for this schema bundle. */ public CreateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); + Preconditions.checkState( + !requestBuilder.getSchemaBundleBuilder().hasAvroSchema(), + "Cannot set proto_schema when avro_schema is already set"); requestBuilder .getSchemaBundleBuilder() .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); @@ -85,9 +88,12 @@ public CreateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { return setAvroSchema(Collections.singletonList(avroSchema)); } - /** Sets the avro schema for this schema bundle. */ + /** Sets a list of avro schemas for this schema bundle. */ public CreateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + Preconditions.checkState( + !requestBuilder.getSchemaBundleBuilder().hasProtoSchema(), + "Cannot set avro_schema when proto_schema is already set"); requestBuilder .getSchemaBundleBuilder() .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java index d47299f226be..be17e6a1f56e 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SchemaBundle.java @@ -42,6 +42,8 @@ public static SchemaBundle fromProto(@Nonnull com.google.bigtable.admin.v2.Schem private SchemaBundle(@Nonnull com.google.bigtable.admin.v2.SchemaBundle proto) { Preconditions.checkNotNull(proto); Preconditions.checkArgument(!proto.getName().isEmpty(), "SchemaBundle must have a name"); + // proto_schema and avro_schema are defined in a protobuf oneof, so at most one + // can be set. Preconditions.checkArgument( proto.hasProtoSchema() || proto.hasAvroSchema(), "Schemabundle must have a proto_schema or avro_schema field"); diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java index 0e4f03d2f6e6..cf5118a76753 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java @@ -93,6 +93,9 @@ public UpdateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF public UpdateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) throws IOException { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); + Preconditions.checkState( + !requestBuilder.getSchemaBundleBuilder().hasAvroSchema(), + "Cannot set proto_schema when avro_schema is already set"); requestBuilder .getSchemaBundleBuilder() .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); @@ -106,9 +109,12 @@ public UpdateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { return setAvroSchema(Collections.singletonList(avroSchema)); } - /** Sets the avro schema for this schema bundle. */ + /** Sets a list of avro schemas for this schema bundle. */ public UpdateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); + Preconditions.checkState( + !requestBuilder.getSchemaBundleBuilder().hasProtoSchema(), + "Cannot set avro_schema when proto_schema is already set"); requestBuilder .getSchemaBundleBuilder() .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java index 8bd24b8d12fe..599763c031dd 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigtable.admin.v2.models; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.common.collect.ImmutableList; @@ -179,6 +180,32 @@ public void testHashCodeWithAvroSchema() { .hashCode()); } + @Test + public void testSetProtoSchemaWhenAvroSchemaAlreadySetThrowsException() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + ByteString protoSchema = ByteString.copyFromUtf8("proto"); + + IllegalStateException exception = + assertThrows(IllegalStateException.class, () -> request.setProtoSchema(protoSchema)); + assertThat(exception) + .hasMessageThat() + .contains("Cannot set proto_schema when avro_schema is already set"); + } + + @Test + public void testSetAvroSchemaWhenProtoSchemaAlreadySetThrowsException() { + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setProtoSchema(ByteString.copyFromUtf8("proto")); + + IllegalStateException exception = + assertThrows(IllegalStateException.class, () -> request.setAvroSchema(TEST_AVRO_SCHEMA)); + assertThat(exception) + .hasMessageThat() + .contains("Cannot set avro_schema when proto_schema is already set"); + } + private String getResourceFilePath(String filePath) throws URISyntaxException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL protoSchema = cl.getResource(filePath); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java index da8ee762d5b2..177ea821af9c 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigtable.admin.v2.models; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.ProtoSchema; @@ -250,6 +251,32 @@ public void testHashCodeWithAvroSchema() { .hashCode()); } + @Test + public void testSetProtoSchemaWhenAvroSchemaAlreadySetThrowsException() { + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + ByteString protoSchema = ByteString.copyFromUtf8("proto"); + + IllegalStateException exception = + assertThrows(IllegalStateException.class, () -> request.setProtoSchema(protoSchema)); + assertThat(exception) + .hasMessageThat() + .contains("Cannot set proto_schema when avro_schema is already set"); + } + + @Test + public void testSetAvroSchemaWhenProtoSchemaAlreadySetThrowsException() throws IOException { + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setProtoSchema(ByteString.copyFromUtf8("proto")); + + IllegalStateException exception = + assertThrows(IllegalStateException.class, () -> request.setAvroSchema(TEST_AVRO_SCHEMA)); + assertThat(exception) + .hasMessageThat() + .contains("Cannot set avro_schema when proto_schema is already set"); + } + private String getResourceFilePath(String filePath) throws URISyntaxException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL protoSchema = cl.getResource(filePath); From 5d08ab4bb1268c8fede07d3c756617af6caca318 Mon Sep 17 00:00:00 2001 From: lixiachen Date: Wed, 16 Sep 2026 12:45:32 -0400 Subject: [PATCH 3/3] Allow overwriting schema types in CreateSchemaBundleRequest and UpdateSchemaBundleRequest instead of throwing exceptions --- .../v2/models/CreateSchemaBundleRequest.java | 19 +++++++---- .../v2/models/UpdateSchemaBundleRequest.java | 27 ++++++++++------ .../models/CreateSchemaBundleRequestTest.java | 32 +++++++++---------- .../models/UpdateSchemaBundleRequestTest.java | 32 +++++++++---------- 4 files changed, 62 insertions(+), 48 deletions(-) diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java index e076896054d8..0893d30bd642 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequest.java @@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.logging.Logger; import javax.annotation.Nonnull; /** @@ -45,6 +46,8 @@ * @see SchemaBundle for more details. */ public final class CreateSchemaBundleRequest { + private static final Logger LOGGER = Logger.getLogger(CreateSchemaBundleRequest.class.getName()); + private final String tableId; private final com.google.bigtable.admin.v2.CreateSchemaBundleRequest.Builder requestBuilder = com.google.bigtable.admin.v2.CreateSchemaBundleRequest.newBuilder(); @@ -73,9 +76,11 @@ public CreateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF /** Sets the proto schema for this schema bundle. */ public CreateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); - Preconditions.checkState( - !requestBuilder.getSchemaBundleBuilder().hasAvroSchema(), - "Cannot set proto_schema when avro_schema is already set"); + if (requestBuilder.getSchemaBundleBuilder().hasAvroSchema()) { + LOGGER.warning( + "This schema bundle already has an Avro schema set. Setting the proto schema will" + + " unset the Avro schema."); + } requestBuilder .getSchemaBundleBuilder() .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); @@ -91,9 +96,11 @@ public CreateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { /** Sets a list of avro schemas for this schema bundle. */ public CreateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); - Preconditions.checkState( - !requestBuilder.getSchemaBundleBuilder().hasProtoSchema(), - "Cannot set avro_schema when proto_schema is already set"); + if (requestBuilder.getSchemaBundleBuilder().hasProtoSchema()) { + LOGGER.warning( + "This schema bundle already has a proto schema set. Setting the Avro schema will" + + " unset the proto schema."); + } requestBuilder .getSchemaBundleBuilder() .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); diff --git a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java index cf5118a76753..aae4d21a0381 100644 --- a/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java +++ b/java-bigtable/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequest.java @@ -30,6 +30,7 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.logging.Logger; import javax.annotation.Nonnull; /** @@ -46,6 +47,8 @@ * @see SchemaBundle for more details. */ public final class UpdateSchemaBundleRequest { + private static final Logger LOGGER = Logger.getLogger(UpdateSchemaBundleRequest.class.getName()); + private final com.google.bigtable.admin.v2.UpdateSchemaBundleRequest.Builder requestBuilder; private final String tableId; private final String schemaBundleId; @@ -81,7 +84,7 @@ private UpdateSchemaBundleRequest( this.requestBuilder = requestBuilder; } - /** Sets the proto schema for this schema bundle. */ + /** Updates the proto schema for this schema bundle. */ public UpdateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaFile) throws IOException { Preconditions.checkNotNull(protoSchemaFile, "protoSchemaFile must be set"); @@ -89,13 +92,15 @@ public UpdateSchemaBundleRequest setProtoSchemaFile(@Nonnull String protoSchemaF return setProtoSchema(ByteString.copyFrom(content)); } - /** Sets the proto schema for this schema bundle. */ + /** Updates the proto schema for this schema bundle. */ public UpdateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) throws IOException { Preconditions.checkNotNull(protoSchema, "protoSchema must be set"); - Preconditions.checkState( - !requestBuilder.getSchemaBundleBuilder().hasAvroSchema(), - "Cannot set proto_schema when avro_schema is already set"); + if (requestBuilder.getSchemaBundleBuilder().hasAvroSchema()) { + LOGGER.warning( + "This schema bundle already has an Avro schema set. Setting the proto schema will" + + " unset the Avro schema."); + } requestBuilder .getSchemaBundleBuilder() .setProtoSchema(ProtoSchema.newBuilder().setProtoDescriptors(protoSchema)); @@ -103,18 +108,20 @@ public UpdateSchemaBundleRequest setProtoSchema(@Nonnull ByteString protoSchema) return this; } - /** Sets the avro schema for this schema bundle. */ + /** Updates the avro schema for this schema bundle. */ public UpdateSchemaBundleRequest setAvroSchema(@Nonnull String avroSchema) { Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); return setAvroSchema(Collections.singletonList(avroSchema)); } - /** Sets a list of avro schemas for this schema bundle. */ + /** Updates the list of avro schemas for this schema bundle. */ public UpdateSchemaBundleRequest setAvroSchema(@Nonnull List avroSchema) { Preconditions.checkNotNull(avroSchema, "avroSchema must be set"); - Preconditions.checkState( - !requestBuilder.getSchemaBundleBuilder().hasProtoSchema(), - "Cannot set avro_schema when proto_schema is already set"); + if (requestBuilder.getSchemaBundleBuilder().hasProtoSchema()) { + LOGGER.warning( + "This schema bundle already has a proto schema set. Setting the Avro schema will" + + " unset the proto schema."); + } requestBuilder .getSchemaBundleBuilder() .setAvroSchema(AvroSchema.newBuilder().addAllJsonSchemas(avroSchema)); diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java index 599763c031dd..91fb65ebbc09 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateSchemaBundleRequestTest.java @@ -17,7 +17,6 @@ package com.google.cloud.bigtable.admin.v2.models; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.common.collect.ImmutableList; @@ -181,29 +180,30 @@ public void testHashCodeWithAvroSchema() { } @Test - public void testSetProtoSchemaWhenAvroSchemaAlreadySetThrowsException() { - CreateSchemaBundleRequest request = - CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + public void testSetProtoSchemaWhenAvroSchemaAlreadySetOverwrites() { ByteString protoSchema = ByteString.copyFromUtf8("proto"); + CreateSchemaBundleRequest request = + CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA) + .setProtoSchema(protoSchema); - IllegalStateException exception = - assertThrows(IllegalStateException.class, () -> request.setProtoSchema(protoSchema)); - assertThat(exception) - .hasMessageThat() - .contains("Cannot set proto_schema when avro_schema is already set"); + com.google.bigtable.admin.v2.CreateSchemaBundleRequest proto = + request.toProto(PROJECT_ID, INSTANCE_ID); + assertThat(proto.getSchemaBundle().hasProtoSchema()).isTrue(); + assertThat(proto.getSchemaBundle().hasAvroSchema()).isFalse(); } @Test - public void testSetAvroSchemaWhenProtoSchemaAlreadySetThrowsException() { + public void testSetAvroSchemaWhenProtoSchemaAlreadySetOverwrites() { CreateSchemaBundleRequest request = CreateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) - .setProtoSchema(ByteString.copyFromUtf8("proto")); + .setProtoSchema(ByteString.copyFromUtf8("proto")) + .setAvroSchema(TEST_AVRO_SCHEMA); - IllegalStateException exception = - assertThrows(IllegalStateException.class, () -> request.setAvroSchema(TEST_AVRO_SCHEMA)); - assertThat(exception) - .hasMessageThat() - .contains("Cannot set avro_schema when proto_schema is already set"); + com.google.bigtable.admin.v2.CreateSchemaBundleRequest proto = + request.toProto(PROJECT_ID, INSTANCE_ID); + assertThat(proto.getSchemaBundle().hasAvroSchema()).isTrue(); + assertThat(proto.getSchemaBundle().hasProtoSchema()).isFalse(); } private String getResourceFilePath(String filePath) throws URISyntaxException { diff --git a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java index 177ea821af9c..495c0e647d0f 100644 --- a/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java +++ b/java-bigtable/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateSchemaBundleRequestTest.java @@ -17,7 +17,6 @@ package com.google.cloud.bigtable.admin.v2.models; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; import com.google.bigtable.admin.v2.AvroSchema; import com.google.bigtable.admin.v2.ProtoSchema; @@ -252,29 +251,30 @@ public void testHashCodeWithAvroSchema() { } @Test - public void testSetProtoSchemaWhenAvroSchemaAlreadySetThrowsException() { - UpdateSchemaBundleRequest request = - UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID).setAvroSchema(TEST_AVRO_SCHEMA); + public void testSetProtoSchemaWhenAvroSchemaAlreadySetOverwrites() throws IOException { ByteString protoSchema = ByteString.copyFromUtf8("proto"); + UpdateSchemaBundleRequest request = + UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) + .setAvroSchema(TEST_AVRO_SCHEMA) + .setProtoSchema(protoSchema); - IllegalStateException exception = - assertThrows(IllegalStateException.class, () -> request.setProtoSchema(protoSchema)); - assertThat(exception) - .hasMessageThat() - .contains("Cannot set proto_schema when avro_schema is already set"); + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest proto = + request.toProto(PROJECT_ID, INSTANCE_ID); + assertThat(proto.getSchemaBundle().hasProtoSchema()).isTrue(); + assertThat(proto.getSchemaBundle().hasAvroSchema()).isFalse(); } @Test - public void testSetAvroSchemaWhenProtoSchemaAlreadySetThrowsException() throws IOException { + public void testSetAvroSchemaWhenProtoSchemaAlreadySetOverwrites() throws IOException { UpdateSchemaBundleRequest request = UpdateSchemaBundleRequest.of(TABLE_ID, SCHEMA_BUNDLE_ID) - .setProtoSchema(ByteString.copyFromUtf8("proto")); + .setProtoSchema(ByteString.copyFromUtf8("proto")) + .setAvroSchema(TEST_AVRO_SCHEMA); - IllegalStateException exception = - assertThrows(IllegalStateException.class, () -> request.setAvroSchema(TEST_AVRO_SCHEMA)); - assertThat(exception) - .hasMessageThat() - .contains("Cannot set avro_schema when proto_schema is already set"); + com.google.bigtable.admin.v2.UpdateSchemaBundleRequest proto = + request.toProto(PROJECT_ID, INSTANCE_ID); + assertThat(proto.getSchemaBundle().hasAvroSchema()).isTrue(); + assertThat(proto.getSchemaBundle().hasProtoSchema()).isFalse(); } private String getResourceFilePath(String filePath) throws URISyntaxException {