Skip to content
Closed
31 changes: 31 additions & 0 deletions src/main/java/com/google/firebase/FirebaseOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public GoogleCredentials get() {
};

private final String databaseUrl;
private final String fcmRootUrl;
private final String storageBucket;
private final Supplier<GoogleCredentials> credentialsSupplier;
private final Map<String, Object> databaseAuthVariableOverride;
Expand Down Expand Up @@ -101,6 +102,11 @@ private FirebaseOptions(@NonNull final FirebaseOptions.Builder builder) {
} else {
this.serviceAccountId = null;
}
if (!Strings.isNullOrEmpty(builder.fcmRootUrl)) {
this.fcmRootUrl = builder.fcmRootUrl;
} else {
this.fcmRootUrl = null;
}
this.storageBucket = builder.storageBucket;
this.httpTransport = builder.httpTransport != null ? builder.httpTransport
: ApiClientUtils.getDefaultTransport();
Expand All @@ -124,6 +130,15 @@ public String getDatabaseUrl() {
return databaseUrl;
}

/**
* Returns the Fcm Root URL that is used for Fcm Messaging.
*
* @return The Fcm Messaging URL supplied via {@link Builder#setFcmRootUrl}.
*/
public String getFcmRootUrl() {
return fcmRootUrl;
}

/**
* Returns the name of the Google Cloud Storage bucket used for storing application data.
*
Expand Down Expand Up @@ -260,6 +275,7 @@ public static final class Builder {
private ThreadManager threadManager;
private int connectTimeout;
private int readTimeout;
private String fcmRootUrl;

/**
* Constructs an empty builder.
Expand Down Expand Up @@ -290,6 +306,7 @@ public Builder(FirebaseOptions options) {
connectTimeout = options.connectTimeout;
readTimeout = options.readTimeout;
firestoreOptions = options.firestoreOptions;
fcmRootUrl = options.fcmRootUrl;
}

/**
Expand All @@ -306,6 +323,20 @@ public Builder setDatabaseUrl(@Nullable String databaseUrl) {
return this;
}

/**
* Sets the Fcm Root URL to use for Fcm Messaging.
*
* <p>See <a href="https://firebase.google.com/docs/admin/setup#initialize_the_sdk">
* Initialize the SDK</a> for code samples and detailed documentation.
*
* @param fcmRootUrl The Fcm Root URL to use for Fcm Messaging.
* @return This <code>Builder</code> instance is returned so subsequent calls can be chained.
*/
public Builder setFcmRootUrl(@Nullable String fcmRootUrl) {
this.fcmRootUrl = fcmRootUrl;
return this;
}

/**
* Sets the name of the Google Cloud Storage bucket for reading and writing application data.
* This should be the full name of the bucket as listed in the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@
* A helper class for interacting with Firebase Cloud Messaging service.
*/
final class FirebaseMessagingClientImpl implements FirebaseMessagingClient {

private static final String FCM_URL = "https://fcm.googleapis.com/v1/projects/%s/messages:send";
private static final String FCM_ROOT_URL = "https://fcm.googleapis.com";
private static final String FCM_SEND_PATH = "v1/projects/%s/messages:send";
private static final String FCM_BATCH_PATH = "batch";

private static final Map<String, String> COMMON_HEADERS =
ImmutableMap.of(
"X-GOOG-API-FORMAT-VERSION", "2",
"X-Firebase-Client", "fire-admin-java/" + SdkUtils.getVersion());

private final String fcmSendUrl;
private final String fcmBatchUrl;
private final HttpRequestFactory requestFactory;
private final HttpRequestFactory childRequestFactory;
private final JsonFactory jsonFactory;
Expand All @@ -78,22 +80,33 @@ final class FirebaseMessagingClientImpl implements FirebaseMessagingClient {

private FirebaseMessagingClientImpl(Builder builder) {
checkArgument(!Strings.isNullOrEmpty(builder.projectId));
this.fcmSendUrl = String.format(FCM_URL, builder.projectId);
String fcmRootUrl = Strings.isNullOrEmpty(builder.fcmRootUrl) ? FCM_ROOT_URL :
builder.fcmRootUrl;
this.fcmSendUrl = String.format(String.format(
"%s/%s", fcmRootUrl, FCM_SEND_PATH),
builder.projectId);
this.fcmBatchUrl = String.format("%s/%s", fcmRootUrl, FCM_BATCH_PATH);
this.requestFactory = checkNotNull(builder.requestFactory);
this.childRequestFactory = checkNotNull(builder.childRequestFactory);
this.jsonFactory = checkNotNull(builder.jsonFactory);
this.responseInterceptor = builder.responseInterceptor;
this.errorHandler = new MessagingErrorHandler(this.jsonFactory);
this.httpClient = new ErrorHandlingHttpClient<>(requestFactory, jsonFactory, errorHandler)
.setInterceptor(responseInterceptor);
this.batchClient = new MessagingBatchClient(requestFactory.getTransport(), jsonFactory);
this.batchClient = new MessagingBatchClient(requestFactory.getTransport(),
jsonFactory, fcmRootUrl);
}

@VisibleForTesting
String getFcmSendUrl() {
return fcmSendUrl;
}

@VisibleForTesting
String getFcmBatchUrl() {
return fcmBatchUrl;
}

@VisibleForTesting
HttpRequestFactory getRequestFactory() {
return requestFactory;
Expand Down Expand Up @@ -139,7 +152,7 @@ private BatchResponse sendBatchRequest(
return new BatchResponseImpl(callback.getResponses());
} catch (HttpResponseException e) {
OutgoingHttpRequest req = new OutgoingHttpRequest(
HttpMethods.POST, MessagingBatchClient.FCM_BATCH_URL);
HttpMethods.POST, fcmBatchUrl);
IncomingHttpResponse resp = new IncomingHttpResponse(e, req);
throw errorHandler.handleHttpResponseException(e, resp);
} catch (IOException e) {
Expand Down Expand Up @@ -193,6 +206,7 @@ static FirebaseMessagingClientImpl fromApp(FirebaseApp app) {
.setRequestFactory(ApiClientUtils.newAuthorizedRequestFactory(app))
.setChildRequestFactory(ApiClientUtils.newUnauthorizedRequestFactory(app))
.setJsonFactory(app.getOptions().getJsonFactory())
.setFcmRootUrl(app.getOptions().getFcmRootUrl())
.build();
}

Expand All @@ -207,9 +221,15 @@ static final class Builder {
private HttpRequestFactory childRequestFactory;
private JsonFactory jsonFactory;
private HttpResponseInterceptor responseInterceptor;
private String fcmRootUrl;

private Builder() { }

Builder setFcmRootUrl(String fcmRootUrl) {
this.fcmRootUrl = fcmRootUrl;
return this;
}

Builder setProjectId(String projectId) {
this.projectId = projectId;
return this;
Expand Down Expand Up @@ -319,22 +339,17 @@ private MessagingServiceErrorResponse safeParse(String response) {

private static class MessagingBatchClient extends AbstractGoogleJsonClient {

private static final String FCM_ROOT_URL = "https://fcm.googleapis.com";
private static final String FCM_BATCH_PATH = "batch";
private static final String FCM_BATCH_URL = String.format(
"%s/%s", FCM_ROOT_URL, FCM_BATCH_PATH);

MessagingBatchClient(HttpTransport transport, JsonFactory jsonFactory) {
super(new Builder(transport, jsonFactory));
MessagingBatchClient(HttpTransport transport, JsonFactory jsonFactory, String fcmRootUrl) {
super(new Builder(transport, jsonFactory, fcmRootUrl));
}

private MessagingBatchClient(Builder builder) {
super(builder);
}

private static class Builder extends AbstractGoogleJsonClient.Builder {
Builder(HttpTransport transport, JsonFactory jsonFactory) {
super(transport, jsonFactory, FCM_ROOT_URL, "", null, false);
Builder(HttpTransport transport, JsonFactory jsonFactory, String fcmRootUrl) {
super(transport, jsonFactory, fcmRootUrl, "", null, false);
setBatchPath(FCM_BATCH_PATH);
setApplicationName("fire-admin-java");
}
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/com/google/firebase/FirebaseOptionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
public class FirebaseOptionsTest {

private static final String FIREBASE_DB_URL = "https://mock-project.firebaseio.com";
private static final String FIREBASE_FCM_URL = "https://localhost";
private static final String FIREBASE_STORAGE_BUCKET = "mock-storage-bucket";
private static final String FIREBASE_PROJECT_ID = "explicit-project-id";

Expand All @@ -53,6 +54,7 @@ public class FirebaseOptionsTest {
.setStorageBucket(FIREBASE_STORAGE_BUCKET)
.setProjectId(FIREBASE_PROJECT_ID)
.setCredentials(TestUtils.getCertCredential(ServiceAccount.EDITOR.asStream()))
.setFcmRootUrl(FIREBASE_FCM_URL)
.build();

private static final ThreadManager MOCK_THREAD_MANAGER = new ThreadManager() {
Expand Down Expand Up @@ -88,8 +90,10 @@ public void createOptionsWithAllValuesSet() throws IOException {
.setConnectTimeout(30000)
.setReadTimeout(60000)
.setFirestoreOptions(firestoreOptions)
.setFcmRootUrl(FIREBASE_FCM_URL)
.build();
assertEquals(FIREBASE_DB_URL, firebaseOptions.getDatabaseUrl());
assertEquals(FIREBASE_FCM_URL, firebaseOptions.getFcmRootUrl());
assertEquals(FIREBASE_STORAGE_BUCKET, firebaseOptions.getStorageBucket());
assertEquals(FIREBASE_PROJECT_ID, firebaseOptions.getProjectId());
assertSame(jsonFactory, firebaseOptions.getJsonFactory());
Expand Down Expand Up @@ -183,6 +187,7 @@ public void checkToBuilderCreatesNewEquivalentInstance() {
assertNotSame(ALL_VALUES_OPTIONS, allValuesOptionsCopy);
assertEquals(ALL_VALUES_OPTIONS.getCredentials(), allValuesOptionsCopy.getCredentials());
assertEquals(ALL_VALUES_OPTIONS.getDatabaseUrl(), allValuesOptionsCopy.getDatabaseUrl());
assertEquals(ALL_VALUES_OPTIONS.getFcmRootUrl(), allValuesOptionsCopy.getFcmRootUrl());
assertEquals(ALL_VALUES_OPTIONS.getProjectId(), allValuesOptionsCopy.getProjectId());
assertEquals(ALL_VALUES_OPTIONS.getJsonFactory(), allValuesOptionsCopy.getJsonFactory());
assertEquals(ALL_VALUES_OPTIONS.getHttpTransport(), allValuesOptionsCopy.getHttpTransport());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
public class FirebaseMessagingClientImplTest {

private static final String TEST_FCM_URL =
"https://fcm.googleapis.com/v1/projects/test-project/messages:send";
"https://fcm.googleapis.com/v1/projects/test-project/messages:send";
private static final String TEST_FCM_BATCH_URL = "https://fcm.googleapis.com/batch";

private static final List<Integer> HTTP_ERRORS = ImmutableList.of(401, 404, 500);

Expand Down Expand Up @@ -534,6 +535,7 @@ public void testFromApp() throws IOException {
FirebaseMessagingClientImpl client = FirebaseMessagingClientImpl.fromApp(app);

assertEquals(TEST_FCM_URL, client.getFcmSendUrl());
assertEquals(TEST_FCM_BATCH_URL, client.getFcmBatchUrl());
assertSame(options.getJsonFactory(), client.getJsonFactory());

HttpRequest request = client.getRequestFactory().buildGetRequest(
Expand Down