diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequest.java index 0547ebae01..b801b0db3b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequest.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequest.java @@ -29,37 +29,57 @@ public final class RestoreTableRequest { private final String sourceBackupId; private final String sourceClusterId; private final String sourceInstanceId; + private final String sourceProjectId; /** * Create a {@link RestoreTableRequest} object. It assumes the source backup locates in the same - * instance as the destination table. To restore a table from a backup in another instance, use - * {@link #of(String, String, String) of} method. + * instance and project as the destination table. To restore a table from a backup in another + * instance, use {@link #of(String, String, String) of} method. To restore a table from a backup + * in another project, use {@link #of(String, String, String, String) of} method. */ public static RestoreTableRequest of(String sourceClusterId, String sourceBackupId) { - RestoreTableRequest request = new RestoreTableRequest(null, sourceClusterId, sourceBackupId); + RestoreTableRequest request = + new RestoreTableRequest(null, sourceClusterId, sourceBackupId, null); return request; } /** - * Create a {@link RestoreTableRequest} object. The source backup could locate in a the same or a - * different instance. + * Create a {@link RestoreTableRequest} object. The source backup could locate in the same or a + * different instance but the same project as the destination table. To restore a table from a + * backup in another project, use {@link #of(String, String, String, String) of} method. */ public static RestoreTableRequest of( String sourceInstanceId, String sourceClusterId, String sourceBackupId) { RestoreTableRequest request = - new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId); + new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId, null); + return request; + } + + /** + * Create a {@link RestoreTableRequest} object. The source backup could locate in the same or a + * different instance and/or project. + */ + public static RestoreTableRequest of( + String sourceInstanceId, + String sourceClusterId, + String sourceBackupId, + String sourceProjectId) { + RestoreTableRequest request = + new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId, sourceProjectId); return request; } private RestoreTableRequest( @Nullable String sourceInstanceId, @Nonnull String sourceClusterId, - @Nonnull String sourceBackupId) { + @Nonnull String sourceBackupId, + @Nullable String sourceProjectId) { Preconditions.checkNotNull(sourceClusterId); Preconditions.checkNotNull(sourceBackupId); this.sourceBackupId = sourceBackupId; this.sourceInstanceId = sourceInstanceId; this.sourceClusterId = sourceClusterId; + this.sourceProjectId = sourceProjectId; } public RestoreTableRequest setTableId(String tableId) { @@ -80,13 +100,18 @@ public boolean equals(Object o) { return Objects.equal(requestBuilder.getTableId(), that.requestBuilder.getTableId()) && Objects.equal(sourceInstanceId, that.sourceInstanceId) && Objects.equal(sourceClusterId, that.sourceClusterId) - && Objects.equal(sourceBackupId, that.sourceBackupId); + && Objects.equal(sourceBackupId, that.sourceBackupId) + && Objects.equal(sourceProjectId, that.sourceProjectId); } @Override public int hashCode() { return Objects.hashCode( - requestBuilder.getTableId(), sourceInstanceId, sourceClusterId, sourceBackupId); + requestBuilder.getTableId(), + sourceInstanceId, + sourceClusterId, + sourceBackupId, + sourceProjectId); } @InternalApi @@ -99,7 +124,7 @@ public com.google.bigtable.admin.v2.RestoreTableRequest toProto( .setParent(NameUtil.formatInstanceName(projectId, instanceId)) .setBackup( NameUtil.formatBackupName( - projectId, + sourceProjectId == null ? projectId : sourceProjectId, sourceInstanceId == null ? instanceId : sourceInstanceId, sourceClusterId, sourceBackupId)) diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java index f3d2261869..a5ada34be5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientTests.java @@ -613,6 +613,54 @@ public void testRestoreTable() throws ExecutionException, InterruptedException { assertThat(actualResult.getTable().getId()).isEqualTo(TABLE_ID); } + @Test + public void testRestoreTableCrossProject() throws ExecutionException, InterruptedException { + // Setup + Mockito.when(mockStub.restoreTableOperationCallable()) + .thenReturn(mockRestoreTableOperationCallable); + + Timestamp startTime = Timestamp.newBuilder().setSeconds(1234).build(); + Timestamp endTime = Timestamp.newBuilder().setSeconds(5678).build(); + String operationName = "my-operation"; + + // Use existing adminClient as destination project: + String dstProjectId = PROJECT_ID; + String dstInstanceId = INSTANCE_ID; + String dstTableName = TABLE_NAME; + + // Create RestoreTableRequest from different source project: + String srcProjectId = "src-project"; + String srcInstanceId = "src-instance"; + String srcClusterId = "src-cluster"; + + RestoreTableRequest req = + RestoreTableRequest.of(srcInstanceId, srcClusterId, BACKUP_ID, srcProjectId) + .setTableId(TABLE_ID); + mockOperationResult( + mockRestoreTableOperationCallable, + req.toProto(dstProjectId, dstInstanceId), + com.google.bigtable.admin.v2.Table.newBuilder().setName(dstTableName).build(), + RestoreTableMetadata.newBuilder() + .setName(dstTableName) + .setOptimizeTableOperationName(operationName) + .setSourceType(RestoreSourceType.BACKUP) + .setBackupInfo( + BackupInfo.newBuilder() + .setBackup(BACKUP_ID) + .setSourceTable(NameUtil.formatTableName(srcProjectId, srcInstanceId, TABLE_ID)) + .setStartTime(startTime) + .setEndTime(endTime) + .build()) + .build()); + + // Execute + RestoredTableResult actualResult = adminClient.restoreTable(req); + + // Verify + assertThat(actualResult.getTable().getId()).isEqualTo(TABLE_ID); + assertThat(actualResult.getTable().getInstanceId()).isEqualTo(dstInstanceId); + } + @Test public void testDeleteBackup() { // Setup diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequestTest.java index 232902f585..30a2274c7e 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequestTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/RestoreTableRequestTest.java @@ -31,6 +31,7 @@ public class RestoreTableRequestTest { private static final String INSTANCE_ID = "my-instance"; private static final String CLUSTER_ID = "my-cluster"; private static final String SOURCE_INSTANCE_ID = "source-instance-id"; + private static final String SOURCE_PROJECT_ID = "source-project-id"; @Test public void testToProto() { @@ -61,6 +62,23 @@ public void testToProtoCrossInstance() { assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); } + @Test + public void testToProtoCrossProject() { + RestoreTableRequest request = + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId(TABLE_ID); + + com.google.bigtable.admin.v2.RestoreTableRequest requestProto = + com.google.bigtable.admin.v2.RestoreTableRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setBackup( + NameUtil.formatBackupName( + SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID)) + .setTableId(TABLE_ID) + .build(); + assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto); + } + @Test public void testEquality() { RestoreTableRequest request = @@ -88,6 +106,24 @@ public void testEqualityCrossInstance() { .setTableId("another-table")); } + @Test + public void testEqualityCrossProject() { + RestoreTableRequest request = + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId(TABLE_ID); + + assertThat(request) + .isEqualTo( + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId(TABLE_ID)); + assertThat(request) + .isNotEqualTo(RestoreTableRequest.of(CLUSTER_ID, BACKUP_ID).setTableId(TABLE_ID)); + assertThat(request) + .isNotEqualTo( + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId("another-table")); + } + @Test public void testHashCode() { RestoreTableRequest request = @@ -117,4 +153,24 @@ public void testHashCodeCrossInstance() { .setTableId("another-table") .hashCode()); } + + @Test + public void testHashCodeCrossProject() { + RestoreTableRequest request = + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId(TABLE_ID); + assertThat(request.hashCode()) + .isEqualTo( + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId(TABLE_ID) + .hashCode()); + assertThat(request.hashCode()) + .isNotEqualTo( + RestoreTableRequest.of(CLUSTER_ID, BACKUP_ID).setTableId(TABLE_ID).hashCode()); + assertThat(request.hashCode()) + .isNotEqualTo( + RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID) + .setTableId("another-table") + .hashCode()); + } }