Skip to content
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
109 changes: 109 additions & 0 deletions src/main/java/com/google/firebase/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2020 Google Inc.
*
* 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.google.firebase;

/**
* Platform-wide error codes that can be raised by Admin SDK APIs.
*/
public enum ErrorCode {

/**
* Client specified an invalid argument.
*/
INVALID_ARGUMENT,

/**
* Request can not be executed in the current system state, such as deleting a non-empty
* directory.
*/
FAILED_PRECONDITION,

/**
* Client specified an invalid range.
*/
OUT_OF_RANGE,

/**
* Request not authenticated due to missing, invalid, or expired OAuth token.
*/
UNAUTHENTICATED,

/**
* Client does not have sufficient permission. This can happen because the OAuth token does
* not have the right scopes, the client doesn't have permission, or the API has not been
* enabled for the client project.
*/
PERMISSION_DENIED,

/**
* A specified resource is not found, or the request is rejected by undisclosed reasons,
* such as whitelisting.
*/
NOT_FOUND,

/**
* Concurrency conflict, such as read-modify-write conflict.
*/
CONFLICT,

/**
* Concurrency conflict, such as read-modify-write conflict.
*/
ABORTED,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure, concurrency conflict and aborted are caused by the same reasons?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONFLICT is really an error code we made up. This is because HTTP 409 can be mapped to both ABORTED and ALREADY_EXISTS: https://cloud.google.com/apis/design/errors#handling_errors

To mitigate this ambiguity we introduced an error code CONFLICT, so that if we only have the HTTP status code to identify an error condition, we will map it to CONFLICT instead of one of the other two.


/**
* The resource that a client tried to create already exists.
*/
ALREADY_EXISTS,

/**
* Either out of resource quota or reaching rate limiting.
*/
RESOURCE_EXHAUSTED,

/**
* Request cancelled by the client.
*/
CANCELLED,

/**
* Unrecoverable data loss or data corruption. The client should report the error to the user.
*/
DATA_LOSS,

/**
* Unknown server error. Typically a server bug.
*/
UNKNOWN,

/**
* Internal server error. Typically a server bug.
*/
INTERNAL,

/**
* Service unavailable. Typically the server is down.
*/
UNAVAILABLE,

/**
* Request deadline exceeded. This will happen only if the caller sets a deadline that is
* shorter than the method's default deadline (i.e. requested deadline is not enough for the
* server to process the request) and the request did not finish within the deadline.
*/
DEADLINE_EXCEEDED,
}
51 changes: 43 additions & 8 deletions src/main/java/com/google/firebase/FirebaseException.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,59 @@
package com.google.firebase;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Strings;
import com.google.firebase.internal.NonNull;
import com.google.firebase.internal.Nullable;

/** Base class for all Firebase exceptions. */
/**
* Base class for all Firebase exceptions.
*/
public class FirebaseException extends Exception {

// TODO(b/27677218): Exceptions should have non-empty messages.
@Deprecated
protected FirebaseException() {}
private final ErrorCode errorCode;
private final IncomingHttpResponse httpResponse;

@Deprecated
public FirebaseException(@NonNull String detailMessage) {
super(detailMessage);
checkArgument(!Strings.isNullOrEmpty(detailMessage), "Detail message must not be empty");
this(detailMessage, null);
}

@Deprecated
public FirebaseException(@NonNull String detailMessage, Throwable cause) {
super(detailMessage, cause);
checkArgument(!Strings.isNullOrEmpty(detailMessage), "Detail message must not be empty");
this(ErrorCode.UNKNOWN, detailMessage, null, cause);
}

public FirebaseException(
@NonNull ErrorCode errorCode,
@NonNull String message,
@Nullable IncomingHttpResponse httpResponse,
@Nullable Throwable cause) {
super(message, cause);
checkArgument(!Strings.isNullOrEmpty(message), "Message must not be null or empty");
this.errorCode = checkNotNull(errorCode, "ErrorCode must not be null");
this.httpResponse = httpResponse;
}

/**
* Returns the platform-wide error code associated with this exception.
*
* @return A Firebase error code.
*/
// TODO: Expose this method publicly
ErrorCode getErrorCode() {
return errorCode;
}

/**
* Returns the HTTP response that resulted in this exception. If the exception was not caused by
* an HTTP error response, returns null.
*
* @return An HTTP response or null.
*/
@Nullable
public IncomingHttpResponse getHttpResponse() {
return httpResponse;
}
}
94 changes: 94 additions & 0 deletions src/main/java/com/google/firebase/IncomingHttpResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2020 Google Inc.
*
* 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.google.firebase;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.database.annotations.Nullable;
import java.util.Map;

/**
* Contains the information that describe an HTTP response received by the SDK.
*/
public final class IncomingHttpResponse {

private final int statusCode;
private final String content;
private final Map<String, Object> headers;
private final OutgoingHttpRequest request;

IncomingHttpResponse(HttpResponse response, @Nullable String content) {
checkNotNull(response, "response must not be null");
this.statusCode = response.getStatusCode();
this.content = content;
this.headers = ImmutableMap.copyOf(response.getHeaders());
this.request = new OutgoingHttpRequest(response.getRequest());
}

IncomingHttpResponse(HttpResponseException e, HttpRequest request) {
this(e, new OutgoingHttpRequest(request));
}

IncomingHttpResponse(HttpResponseException e, OutgoingHttpRequest request) {
checkNotNull(e, "exception must not be null");
this.statusCode = e.getStatusCode();
this.content = e.getContent();
this.headers = ImmutableMap.copyOf(e.getHeaders());
this.request = checkNotNull(request, "request must not be null");
}

/**
* Returns the status code of the response.
*
* @return An HTTP status code (e.g. 500).
*/
public int getStatusCode() {
return this.statusCode;
}

/**
* Returns the content of the response as a string.
*
* @return HTTP content or null.
*/
@Nullable
public String getContent() {
return this.content;
}

/**
* Returns the headers set on the response.
*
* @return An immutable map of headers (possibly empty).
*/
public Map<String, Object> getHeaders() {
return this.headers;
}

/**
* Returns the request that resulted in this response.
*
* @return An HTTP request.
*/
public OutgoingHttpRequest getRequest() {
return request;
}
}
92 changes: 92 additions & 0 deletions src/main/java/com/google/firebase/OutgoingHttpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2020 Google Inc.
*
* 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.google.firebase;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpRequest;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.internal.Nullable;
import java.util.Map;

/**
* Contains the information that describe an HTTP request made by the SDK.
*/
public final class OutgoingHttpRequest {

private final String method;
private final String url;
private final HttpContent content;
private final Map<String, Object> headers;

OutgoingHttpRequest(String method, String url) {
checkArgument(!Strings.isNullOrEmpty(method), "method must not be null or empty");
checkArgument(!Strings.isNullOrEmpty(url), "url must not be empty");
this.method = method;
this.url = url;
this.content = null;
this.headers = ImmutableMap.of();
}

OutgoingHttpRequest(HttpRequest request) {
checkNotNull(request, "request must not be null");
this.method = request.getRequestMethod();
this.url = request.getUrl().toString();
this.content = request.getContent();
this.headers = ImmutableMap.copyOf(request.getHeaders());
}

/**
* Returns the HTTP method of the request.
*
* @return An HTTP method string (e.g. GET).
*/
public String getMethod() {
return method;
}

/**
* Returns the URL of the request.
*
* @return An absolute HTTP URL.
*/
public String getUrl() {
return url;
}

/**
* Returns any content that was sent with the request.
*
* @return HTTP content or null.
*/
@Nullable
public HttpContent getContent() {
return content;
}

/**
* Returns the headers set on the request.
*
* @return An immutable map of headers (possibly empty).
*/
public Map<String, Object> getHeaders() {
return headers;
}
}
10 changes: 0 additions & 10 deletions src/test/java/com/google/firebase/FirebaseAppTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,16 +579,6 @@ public void testFirebaseConfigStringIgnoresInvalidKey() {
assertEquals("hipster-chat-mock", firebaseApp.getOptions().getProjectId());
}

@Test(expected = IllegalArgumentException.class)
public void testFirebaseExceptionNullDetail() {
new FirebaseException(null);
}

@Test(expected = IllegalArgumentException.class)
public void testFirebaseExceptionEmptyDetail() {
new FirebaseException("");
}

@Test
public void testFirebaseAppCreationWithEmptySupplier() {
FirebaseApp.initializeApp(FirebaseOptions.builder()
Expand Down
Loading