Skip to content

Error handling revamp for the project management API #367

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

Merged
merged 3 commits into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public <V> V parse(IncomingHttpResponse response, Class<V> responseType) throws
}
}

private void parse(IncomingHttpResponse response, Object destination) throws T {
public void parse(IncomingHttpResponse response, Object destination) throws T {
try {
JsonParser parser = jsonFactory.createJsonParser(response.getContent());
parser.parse(destination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,27 @@

package com.google.firebase.projectmanagement;

import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseException;
import com.google.firebase.internal.Nullable;
import com.google.firebase.IncomingHttpResponse;
import com.google.firebase.database.annotations.Nullable;
import com.google.firebase.internal.NonNull;

/**
* An exception encountered while interacting with the Firebase Project Management Service.
*/
public class FirebaseProjectManagementException extends FirebaseException {
FirebaseProjectManagementException(String detailMessage) {
this(detailMessage, null);
public final class FirebaseProjectManagementException extends FirebaseException {

FirebaseProjectManagementException(@NonNull FirebaseException base) {
this(base, base.getMessage());
}

FirebaseProjectManagementException(@NonNull FirebaseException base, @NonNull String message) {
super(base.getErrorCodeNew(), message, base.getCause(), base.getHttpResponse());
}

FirebaseProjectManagementException(String detailMessage, @Nullable Throwable cause) {
super(detailMessage, cause);
FirebaseProjectManagementException(
@NonNull ErrorCode code, @NonNull String message, @Nullable IncomingHttpResponse response) {
super(code, message, null, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.IncomingHttpResponse;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.CallableOperation;
import java.nio.charset.StandardCharsets;
Expand All @@ -56,7 +58,6 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
private final FirebaseApp app;
private final Sleeper sleeper;
private final Scheduler scheduler;
private final HttpRequestFactory requestFactory;
private final HttpHelper httpHelper;

private final CreateAndroidAppFromAppIdFunction createAndroidAppFromAppIdFunction =
Expand All @@ -78,15 +79,9 @@ class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppS
this.app = checkNotNull(app);
this.sleeper = checkNotNull(sleeper);
this.scheduler = checkNotNull(scheduler);
this.requestFactory = checkNotNull(requestFactory);
this.httpHelper = new HttpHelper(app.getOptions().getJsonFactory(), requestFactory);
}

@VisibleForTesting
HttpRequestFactory getRequestFactory() {
return requestFactory;
}

@VisibleForTesting
void setInterceptor(HttpResponseInterceptor interceptor) {
httpHelper.setInterceptor(interceptor);
Expand Down Expand Up @@ -318,14 +313,14 @@ protected String execute() throws FirebaseProjectManagementException {
payloadBuilder.put("display_name", displayName);
}
OperationResponse operationResponseInstance = new OperationResponse();
httpHelper.makePostRequest(
IncomingHttpResponse response = httpHelper.makePostRequest(
url, payloadBuilder.build(), operationResponseInstance, projectId, "Project ID");
if (Strings.isNullOrEmpty(operationResponseInstance.name)) {
throw HttpHelper.createFirebaseProjectManagementException(
String message = buildMessage(
namespace,
"Bundle ID",
"Unable to create App: server returned null operation name.",
/* cause= */ null);
"Unable to create App: server returned null operation name.");
throw new FirebaseProjectManagementException(ErrorCode.INTERNAL, message, response);
}
return operationResponseInstance.name;
}
Expand All @@ -341,27 +336,29 @@ private String pollOperation(String projectId, String operationName)
* Math.pow(POLL_EXPONENTIAL_BACKOFF_FACTOR, currentAttempt));
sleepOrThrow(projectId, delayMillis);
OperationResponse operationResponseInstance = new OperationResponse();
httpHelper.makeGetRequest(url, operationResponseInstance, projectId, "Project ID");
IncomingHttpResponse response = httpHelper.makeGetRequest(
url, operationResponseInstance, projectId, "Project ID");
if (!operationResponseInstance.done) {
continue;
}
// The Long Running Operation API guarantees that when done == true, exactly one of 'response'
// or 'error' is set.
if (operationResponseInstance.response == null
|| Strings.isNullOrEmpty(operationResponseInstance.response.appId)) {
throw HttpHelper.createFirebaseProjectManagementException(
String message = buildMessage(
projectId,
"Project ID",
"Unable to create App: internal server error.",
/* cause= */ null);
"Unable to create App: internal server error.");
throw new FirebaseProjectManagementException(ErrorCode.INTERNAL, message, response);
}
return operationResponseInstance.response.appId;
}
throw HttpHelper.createFirebaseProjectManagementException(

String message = buildMessage(
projectId,
"Project ID",
"Unable to create App: deadline exceeded.",
/* cause= */ null);
"Unable to create App: deadline exceeded.");
throw new FirebaseProjectManagementException(ErrorCode.DEADLINE_EXCEEDED, message, null);
}

/**
Expand Down Expand Up @@ -420,19 +417,22 @@ private WaitOperationRunnable(
public void run() {
String url = String.format("%s/v1/%s", FIREBASE_PROJECT_MANAGEMENT_URL, operationName);
OperationResponse operationResponseInstance = new OperationResponse();
IncomingHttpResponse httpResponse;
try {
httpHelper.makeGetRequest(url, operationResponseInstance, projectId, "Project ID");
httpResponse = httpHelper.makeGetRequest(
url, operationResponseInstance, projectId, "Project ID");
} catch (FirebaseProjectManagementException e) {
settableFuture.setException(e);
return;
}
if (!operationResponseInstance.done) {
if (numberOfPreviousPolls + 1 >= MAXIMUM_POLLING_ATTEMPTS) {
settableFuture.setException(HttpHelper.createFirebaseProjectManagementException(
projectId,
String message = buildMessage(projectId,
"Project ID",
"Unable to create App: deadline exceeded.",
/* cause= */ null));
"Unable to create App: deadline exceeded.");
FirebaseProjectManagementException exception = new FirebaseProjectManagementException(
ErrorCode.DEADLINE_EXCEEDED, message, httpResponse);
settableFuture.setException(exception);
} else {
long delayMillis = (long) (
POLL_BASE_WAIT_TIME_MILLIS
Expand All @@ -451,11 +451,12 @@ public void run() {
// or 'error' is set.
if (operationResponseInstance.response == null
|| Strings.isNullOrEmpty(operationResponseInstance.response.appId)) {
settableFuture.setException(HttpHelper.createFirebaseProjectManagementException(
projectId,
String message = buildMessage(projectId,
"Project ID",
"Unable to create App: internal server error.",
/* cause= */ null));
"Unable to create App: internal server error.");
FirebaseProjectManagementException exception = new FirebaseProjectManagementException(
ErrorCode.INTERNAL, message, httpResponse);
settableFuture.setException(exception);
} else {
settableFuture.set(operationResponseInstance.response.appId);
}
Expand Down Expand Up @@ -765,14 +766,17 @@ private void sleepOrThrow(String projectId, long delayMillis)
try {
sleeper.sleep(delayMillis);
} catch (InterruptedException e) {
throw HttpHelper.createFirebaseProjectManagementException(
projectId,
String message = buildMessage(projectId,
"Project ID",
"Unable to create App: exponential backoff interrupted.",
/* cause= */ null);
"Unable to create App: exponential backoff interrupted.");
throw new FirebaseProjectManagementException(ErrorCode.ABORTED, message, null);
}
}

private String buildMessage(String resourceId, String resourceIdName, String description) {
return String.format("%s \"%s\": %s", resourceIdName, resourceId, description);
}

/* Helper types. */

private interface CreateAppFromAppIdFunction<T> extends ApiFunction<String, T> {}
Expand Down
Loading