Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bigtable: add CRUD for AppProfiles #3619

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions google-cloud-clients/google-cloud-bigtable-admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.api</groupId>
<artifactId>gax</artifactId>
<classifier>testlib</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,31 @@
*/
package com.google.cloud.bigtable.admin.v2;

import com.google.api.core.ApiAsyncFunction;
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting;
import com.google.bigtable.admin.v2.AppProfileName;
import com.google.bigtable.admin.v2.DeleteAppProfileRequest;
import com.google.bigtable.admin.v2.GetAppProfileRequest;
import com.google.bigtable.admin.v2.InstanceName;
import com.google.bigtable.admin.v2.ListAppProfilesRequest;
import com.google.bigtable.admin.v2.ProjectName;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPage;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPagedResponse;
import com.google.cloud.bigtable.admin.v2.models.AppProfile;
import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy;
import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest;
import com.google.cloud.bigtable.admin.v2.models.UpdateAppProfileRequest;
import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.protobuf.Empty;
import java.io.IOException;
import java.util.List;
import javax.annotation.Nonnull;

/**
Expand Down Expand Up @@ -105,4 +127,332 @@ public ProjectName getProjectName() {
public void close() {
stub.close();
}

/**
* Creates a new app profile.
*
* <p>Sample code:
*
* <pre>{@code
* AppProfile appProfile = client.createAppProfile(
* CreateAppProfileRequest.of("my-instance", "my-new-app-profile")
* .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
* );
* }</pre>
*
* @see CreateAppProfileRequest
*/
@SuppressWarnings("WeakerAccess")
public AppProfile createAppProfile(CreateAppProfileRequest request) {
return awaitFuture(createAppProfileAsync(request));
}

/**
* Asynchronously creates a new app profile.
*
* <p>Sample code:
*
* <pre>{@code
* ApiFuture<AppProfile> appProfileFuture = client.createAppProfileAsync(
* CreateAppProfileRequest.of("my-instance", "my-new-app-profile")
* .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
* );
*
* AppProfile appProfile = appProfileFuture.get();
* }</pre>
*
* @see CreateAppProfileRequest
*/
@SuppressWarnings("WeakerAccess")
public ApiFuture<AppProfile> createAppProfileAsync(CreateAppProfileRequest request) {
return ApiFutures.transform(
stub.createAppProfileCallable().futureCall(request.toProto(projectName)),
new ApiFunction<com.google.bigtable.admin.v2.AppProfile, AppProfile>() {
@Override
public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) {
return AppProfile.fromProto(proto);
}
},
MoreExecutors.directExecutor()
);
}

/**
* Get the app profile by id.
*
* <p>Sample code:
*
* <pre>{@code
* AppProfile appProfile = client.getAppProfile("my-instance", "my-app-profile");
* }</pre>
*
* @see AppProfile
*/
@SuppressWarnings("WeakerAccess")
public AppProfile getAppProfile(String instanceId, String appProfileId) {
return awaitFuture(getAppProfileAsync(instanceId, appProfileId));
}

/**
* Asynchronously get the app profile by id.
*
* <p>Sample code:
*
* <pre>{@code
* ApiFuture<AppProfile> appProfileFuture = client.getAppProfileAsync("my-instance", "my-app-profile");
*
* AppProfile appProfile = appProfileFuture.get();
* }</pre>
*
* @see AppProfile
*/
@SuppressWarnings("WeakerAccess")
public ApiFuture<AppProfile> getAppProfileAsync(String instanceId, String appProfileId) {
AppProfileName name = AppProfileName.of(projectName.getProject(), instanceId, appProfileId);

GetAppProfileRequest request = GetAppProfileRequest.newBuilder()
.setName(name.toString())
.build();

return ApiFutures.transform(
stub.getAppProfileCallable().futureCall(request),
new ApiFunction<com.google.bigtable.admin.v2.AppProfile, AppProfile>() {
@Override
public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) {
return AppProfile.fromProto(proto);
}
},
MoreExecutors.directExecutor()
);
}

/**
* Lists all app profiles of the specified instance.
*
* <p>Sample code:
*
* <pre>{@code
* List<AppProfile> appProfiles = client.listAppProfiles("my-instance");
* }</pre>
*
* @see AppProfile
*/
@SuppressWarnings("WeakerAccess")
public List<AppProfile> listAppProfiles(String instanceId) {
return awaitFuture(listAppProfilesAsync(instanceId));
}

/**
* Asynchronously lists all app profiles of the specified instance.
*
* <p>Sample code:
*
* <pre>{@code
* ApiFuture<List<AppProfile>> appProfilesFuture = client.listAppProfilesAsync("my-instance");
*
* List<AppProfile> appProfiles = appProfileFuture.get();
* }</pre>
*
* @see AppProfile
*/
@SuppressWarnings("WeakerAccess")
public ApiFuture<List<AppProfile>> listAppProfilesAsync(String instanceId) {
InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId);

ListAppProfilesRequest request = ListAppProfilesRequest.newBuilder()
.setParent(instanceName.toString())
.build();

// TODO(igorbernstein2): try to upstream pagination spooling or figure out a way to expose the
// paginated responses while maintaining the wrapper facade.

// Fetch the first page.
ApiFuture<ListAppProfilesPage> firstPageFuture = ApiFutures.transform(
stub.listAppProfilesPagedCallable().futureCall(request),
new ApiFunction<ListAppProfilesPagedResponse, ListAppProfilesPage>() {
@Override
public ListAppProfilesPage apply(ListAppProfilesPagedResponse response) {
return response.getPage();
}
},
MoreExecutors.directExecutor()
);

// Fetch the rest of the pages by chaining the futures.
ApiFuture<List<com.google.bigtable.admin.v2.AppProfile>> allProtos = ApiFutures
.transformAsync(
firstPageFuture,
new ApiAsyncFunction<ListAppProfilesPage, List<com.google.bigtable.admin.v2.AppProfile>>() {
List<com.google.bigtable.admin.v2.AppProfile> responseAccumulator = Lists
.newArrayList();

@Override
public ApiFuture<List<com.google.bigtable.admin.v2.AppProfile>> apply(
ListAppProfilesPage page) {
// Add all entries from the page
responseAccumulator.addAll(Lists.newArrayList(page.getValues()));

// If this is the last page, just return the accumulated responses.
if (!page.hasNextPage()) {
return ApiFutures.immediateFuture(responseAccumulator);
}

// Otherwise fetch the next page.
return ApiFutures.transformAsync(
page.getNextPageAsync(),
this,
MoreExecutors.directExecutor()
);
}
},
MoreExecutors.directExecutor()
);

// Wrap all of the accumulated protos.
return ApiFutures.transform(allProtos,
new ApiFunction<List<com.google.bigtable.admin.v2.AppProfile>, List<AppProfile>>() {
@Override
public List<AppProfile> apply(List<com.google.bigtable.admin.v2.AppProfile> input) {
List<AppProfile> results = Lists.newArrayListWithCapacity(input.size());
for (com.google.bigtable.admin.v2.AppProfile appProfile : input) {
results.add(AppProfile.fromProto(appProfile));
}
return results;
}
},
MoreExecutors.directExecutor()
);
}

/**
* Updates an existing app profile.
*
* <p>Sample code:
*
* <pre>{@code
* AppProfile existingAppProfile = client.getAppProfile("my-instance", "my-app-profile");
*
* AppProfile updatedAppProfile = client.updateAppProfile(
* UpdateAppProfileRequest.of(existingAppProfile)
* .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
* );
* }</pre>
*
* @see UpdateAppProfileRequest
*/
@SuppressWarnings("WeakerAccess")
public AppProfile updateAppProfile(UpdateAppProfileRequest request) {
return awaitFuture(updateAppProfileAsync(request));
}

/**
* Asynchronously updates an existing app profile.
*
* <p>Sample code:
*
* <pre>{@code
*
* ApiFuture<AppProfile> existingAppProfileFuture = client.getAppProfileAsync("my-instance", "my-app-profile");
*
* ApiFuture<AppProfile> updatedAppProfileFuture = ApiFutures.transformAsync(
* existingAppProfileFuture,
* new ApiAsyncFunction<AppProfile, AppProfile>() {
* @Override
* public ApiFuture<AppProfile> apply(AppProfile existingAppProfile) {

This comment was marked as spam.

* return client.updateAppProfileAsync(
* UpdateAppProfileRequest.of(existingAppProfile)
* .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-other-cluster"))
* );
* }
* },
* MoreExecutors.directExecutor()
* );
*
* ApiFuture<AppProfile> appProfile = updatedAppProfileFuture.get();
* }</pre>
*
* @see UpdateAppProfileRequest
*/
@SuppressWarnings("WeakerAccess")
public ApiFuture<AppProfile> updateAppProfileAsync(UpdateAppProfileRequest request) {
return ApiFutures.transform(
stub.updateAppProfileOperationCallable().futureCall(request.toProto(projectName)),
new ApiFunction<com.google.bigtable.admin.v2.AppProfile, AppProfile>() {
@Override
public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) {
return AppProfile.fromProto(proto);
}
},
MoreExecutors.directExecutor()
);
}

/**
* Deletes the specified app profile.
*
* <p>Sample code:
*
* <pre>{@code
* client.deleteAppProfile("my-instance", "my-app-profile");
* }</pre>
*/
@SuppressWarnings("WeakerAccess")
public void deleteAppProfile(String instanceId, String appProfileId) {
awaitFuture(deleteAppProfileAsync(instanceId, appProfileId));
}

/**
* Asynchronously deletes the specified app profile.
*
* <p>Sample code:
*
* <pre>{@code
* ApiFuture<Void> deleteFuture = client.deleteAppProfileAsync("my-instance", "my-app-profile");
*
* deleteFuture.get();
* }</pre>
*/
@SuppressWarnings("WeakerAccess")
public ApiFuture<Void> deleteAppProfileAsync(String instanceId, String appProfileId) {
AppProfileName name = AppProfileName.of(projectName.getProject(), instanceId, appProfileId);
DeleteAppProfileRequest request = DeleteAppProfileRequest.newBuilder()
.setName(name.toString())
.build();

return ApiFutures.transform(
stub.deleteAppProfileCallable().futureCall(request),
new ApiFunction<Empty, Void>() {
@Override
public Void apply(Empty input) {
return null;
}
},
MoreExecutors.directExecutor()
);
}

/**
* Awaits the result of a future, taking care to propagate errors while maintaining the call site
* in a suppressed exception. This allows semantic errors to be caught across threads, while
* preserving the call site in the error. The caller's stacktrace will be made available as a
* suppressed exception.
*/
// TODO(igorbernstein2): try to move this into gax
private <T> T awaitFuture(ApiFuture<T> future) {
RuntimeException error;
try {
return Futures.getUnchecked(future);
} catch (UncheckedExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
error = (RuntimeException) e.getCause();
} else {
error = e;
}
} catch (RuntimeException e) {
error = e;
}
// Add the caller's stack as a suppressed exception
error.addSuppressed(new RuntimeException("Encountered error while awaiting future"));
throw error;
}
}
Loading