Skip to content

Commit af1e510

Browse files
committed
Adding http client to call fis backend service (#659)
* Adding Firebase Installations module * Readding .idea files that were deleted in previous commit * Revert "Adding Firebase Installations module" This reverts commit 2ec4aef. * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Adding Firebase Installations module. * Readding .idea files that were deleted in previous commit * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Revert "Adding Firebase Installations module" with hidden files This reverts commit 2ec4aef. * Addressing review comments. * Http client to call FIS backend service. * Http client to call FIS backend service. * Http client to call FIS backend service. * Adding Firebase Installations module * Adding Firebase Installations module. * Readding .idea files that were deleted in previous commit * Readding .idea files that were deleted in previous commit * Revert "Adding Firebase Installations module" This reverts commit 2ec4aef. * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Revert "Adding Firebase Installations module" with hidden files This reverts commit 2ec4aef. * Addressing review comments. * Http client to call FIS backend service. * Http client to call FIS backend service. * Initial Code structure for FIS Android SDK (#648) * Adding an interface library for Firebase Installations SDK * Adding Firebase Installations module * Adding Firebase Installations module. * Readding .idea files that were deleted in previous commit * Revert "Adding Firebase Installations module" This reverts commit 2ec4aef. * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Add firebase installations project path * Adding Firebase Installations module. * Readding .idea files that were deleted in previous commit * Revert "Adding Firebase Installations module" This reverts commit 2ec4aef. * Revert "Readding .idea files that were deleted in previous commit" This reverts commit 7b4ebcf. * Add firebase installations project path * Fixing formattinf issues. * Revert "Adding Firebase Installations module" with hidden files This reverts commit 2ec4aef. * Addressing review comments. * Making InstallationTokenResult an AutoValue class. * Http client to call FIS backend service. * Addresing comments and introducing new FirebaseInstallationService Exception.
1 parent 1980321 commit af1e510

File tree

6 files changed

+393
-4
lines changed

6 files changed

+393
-4
lines changed

firebase-installations-interop/src/main/java/com/google/firebase/installations/InstallationTokenResult.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,27 @@ public abstract class InstallationTokenResult {
2828
* The amount of time, in milliseconds, before the auth-token expires for this firebase
2929
* installation.
3030
*/
31+
@NonNull
3132
public abstract long getTokenExpirationTimestampMillis();
3233

3334
@NonNull
34-
public static InstallationTokenResult create(
35-
@NonNull String authToken, long tokenExpirationTimestampMillis) {
36-
return new AutoValue_InstallationTokenResult(authToken, tokenExpirationTimestampMillis);
35+
public abstract Builder toBuilder();
36+
37+
/** Returns a default Builder object to create an InstallationResponse object */
38+
@NonNull
39+
public static InstallationTokenResult.Builder builder() {
40+
return new AutoValue_InstallationTokenResult.Builder();
41+
}
42+
43+
@AutoValue.Builder
44+
public abstract static class Builder {
45+
@NonNull
46+
public abstract Builder setAuthToken(@NonNull String value);
47+
48+
@NonNull
49+
public abstract Builder setTokenExpirationTimestampMillis(@NonNull long value);
50+
51+
@NonNull
52+
public abstract InstallationTokenResult build();
3753
}
3854
}

firebase-installations/firebase-installations.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ dependencies {
4545
implementation 'androidx.multidex:multidex:2.0.1'
4646
implementation 'com.google.android.gms:play-services-tasks:17.0.0'
4747

48+
49+
compileOnly "com.google.auto.value:auto-value-annotations:1.6.5"
50+
annotationProcessor "com.google.auto.value:auto-value:1.6.2"
51+
4852
testImplementation 'androidx.test:core:1.2.0'
4953
testImplementation 'junit:junit:4.12'
5054
testImplementation "org.robolectric:robolectric:$robolectricVersion"

firebase-installations/src/main/java/com/google/firebase/installations/FirebaseInstallations.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public Task<String> getId() {
7878
@NonNull
7979
@Override
8080
public Task<InstallationTokenResult> getAuthToken(boolean forceRefresh) {
81-
return Tasks.forResult(InstallationTokenResult.create("dummy_auth_token", 1000l));
81+
return Tasks.forResult(InstallationTokenResult.builder().build());
8282
}
8383

8484
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.installations.remote;
16+
17+
import android.util.JsonReader;
18+
import androidx.annotation.NonNull;
19+
import com.google.firebase.installations.InstallationTokenResult;
20+
import java.io.IOException;
21+
import java.io.InputStreamReader;
22+
import java.net.URL;
23+
import java.nio.charset.Charset;
24+
import java.util.zip.GZIPOutputStream;
25+
import javax.net.ssl.HttpsURLConnection;
26+
import org.json.JSONException;
27+
import org.json.JSONObject;
28+
29+
/** Http client that sends request to Firebase Installations backend API. */
30+
public class FirebaseInstallationServiceClient {
31+
private static final String FIREBASE_INSTALLATIONS_API_DOMAIN =
32+
"firebaseinstallations.googleapis.com";
33+
private static final String CREATE_REQUEST_RESOURCE_NAME_FORMAT = "projects/%s/installations";
34+
private static final String GENERATE_AUTH_TOKEN_REQUEST_RESOURCE_NAME_FORMAT =
35+
"projects/%s/installations/%s/auth:generate";
36+
private static final String DELETE_REQUEST_RESOURCE_NAME_FORMAT = "projects/%s/installations/%s";
37+
private static final String FIREBASE_INSTALLATIONS_API_VERSION = "v1";
38+
private static final String FIREBASE_INSTALLATION_AUTH_VERSION = "FIS_V2";
39+
40+
private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
41+
private static final String ACCEPT_HEADER_KEY = "Accept";
42+
private static final String JSON_CONTENT_TYPE = "application/json";
43+
private static final String CONTENT_ENCODING_HEADER_KEY = "Content-Encoding";
44+
private static final String GZIP_CONTENT_ENCODING = "gzip";
45+
46+
private static final String UNAUTHORIZED_ERROR_MESSAGE =
47+
"The request did not have the required credentials.";
48+
private static final String INTERNAL_SERVER_ERROR_MESSAGE = "There was an internal server error.";
49+
private static final String NETWORK_ERROR_MESSAGE = "The server returned an unexpected error:";
50+
51+
@NonNull
52+
public InstallationResponse createFirebaseInstallation(
53+
long projectNumber,
54+
@NonNull String apiKey,
55+
@NonNull String firebaseInstallationId,
56+
@NonNull String appId)
57+
throws FirebaseInstallationServiceException {
58+
String resourceName = String.format(CREATE_REQUEST_RESOURCE_NAME_FORMAT, projectNumber);
59+
try {
60+
URL url =
61+
new URL(
62+
String.format(
63+
"https://%s/%s/%s?key=%s",
64+
FIREBASE_INSTALLATIONS_API_DOMAIN,
65+
FIREBASE_INSTALLATIONS_API_VERSION,
66+
resourceName,
67+
apiKey));
68+
69+
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
70+
httpsURLConnection.setDoOutput(true);
71+
httpsURLConnection.setRequestMethod("POST");
72+
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
73+
httpsURLConnection.addRequestProperty(ACCEPT_HEADER_KEY, JSON_CONTENT_TYPE);
74+
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
75+
GZIPOutputStream gzipOutputStream =
76+
new GZIPOutputStream(httpsURLConnection.getOutputStream());
77+
try {
78+
gzipOutputStream.write(
79+
buildCreateFirebaseInstallationRequestBody(firebaseInstallationId, appId)
80+
.toString()
81+
.getBytes("UTF-8"));
82+
} catch (JSONException e) {
83+
throw new IllegalStateException(e);
84+
} finally {
85+
gzipOutputStream.close();
86+
}
87+
88+
int httpResponseCode = httpsURLConnection.getResponseCode();
89+
switch (httpResponseCode) {
90+
case 200:
91+
return readCreateResponse(httpsURLConnection);
92+
case 401:
93+
throw new FirebaseInstallationServiceException(
94+
UNAUTHORIZED_ERROR_MESSAGE, FirebaseInstallationServiceException.Code.UNAUTHORIZED);
95+
default:
96+
throw new FirebaseInstallationServiceException(
97+
INTERNAL_SERVER_ERROR_MESSAGE,
98+
FirebaseInstallationServiceException.Code.SERVER_ERROR);
99+
}
100+
} catch (IOException e) {
101+
throw new FirebaseInstallationServiceException(
102+
NETWORK_ERROR_MESSAGE + e.getMessage(),
103+
FirebaseInstallationServiceException.Code.NETWORK_ERROR);
104+
}
105+
}
106+
107+
private static JSONObject buildCreateFirebaseInstallationRequestBody(String fid, String appId)
108+
throws JSONException {
109+
JSONObject firebaseInstallationData = new JSONObject();
110+
firebaseInstallationData.put("fid", fid);
111+
firebaseInstallationData.put("appId", appId);
112+
firebaseInstallationData.put("appVersion", FIREBASE_INSTALLATION_AUTH_VERSION);
113+
return firebaseInstallationData;
114+
}
115+
116+
@NonNull
117+
public void deleteFirebaseInstallation(
118+
long projectNumber, @NonNull String apiKey, @NonNull String fid, @NonNull String refreshToken)
119+
throws FirebaseInstallationServiceException {
120+
String resourceName = String.format(DELETE_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, fid);
121+
try {
122+
URL url =
123+
new URL(
124+
String.format(
125+
"https://%s/%s/%s?key=%s",
126+
FIREBASE_INSTALLATIONS_API_DOMAIN,
127+
FIREBASE_INSTALLATIONS_API_VERSION,
128+
resourceName,
129+
apiKey));
130+
131+
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
132+
httpsURLConnection.setDoOutput(true);
133+
httpsURLConnection.setRequestMethod("DELETE");
134+
httpsURLConnection.addRequestProperty("Authorization", "FIS_V2 " + refreshToken);
135+
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
136+
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
137+
138+
int httpResponseCode = httpsURLConnection.getResponseCode();
139+
switch (httpResponseCode) {
140+
case 200:
141+
return;
142+
case 401:
143+
throw new FirebaseInstallationServiceException(
144+
UNAUTHORIZED_ERROR_MESSAGE, FirebaseInstallationServiceException.Code.UNAUTHORIZED);
145+
default:
146+
throw new FirebaseInstallationServiceException(
147+
INTERNAL_SERVER_ERROR_MESSAGE,
148+
FirebaseInstallationServiceException.Code.SERVER_ERROR);
149+
}
150+
} catch (IOException e) {
151+
throw new FirebaseInstallationServiceException(
152+
NETWORK_ERROR_MESSAGE + e.getMessage(),
153+
FirebaseInstallationServiceException.Code.NETWORK_ERROR);
154+
}
155+
}
156+
157+
@NonNull
158+
public InstallationTokenResult generateAuthToken(
159+
long projectNumber, @NonNull String apiKey, @NonNull String fid, @NonNull String refreshToken)
160+
throws FirebaseInstallationServiceException {
161+
String resourceName =
162+
String.format(GENERATE_AUTH_TOKEN_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, fid);
163+
try {
164+
URL url =
165+
new URL(
166+
String.format(
167+
"https://%s/%s/%s?key=%s",
168+
FIREBASE_INSTALLATIONS_API_DOMAIN,
169+
FIREBASE_INSTALLATIONS_API_VERSION,
170+
resourceName,
171+
apiKey));
172+
173+
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
174+
httpsURLConnection.setDoOutput(true);
175+
httpsURLConnection.setRequestMethod("POST");
176+
httpsURLConnection.addRequestProperty("Authorization", "FIS_V2 " + refreshToken);
177+
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
178+
httpsURLConnection.addRequestProperty(ACCEPT_HEADER_KEY, JSON_CONTENT_TYPE);
179+
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
180+
181+
int httpResponseCode = httpsURLConnection.getResponseCode();
182+
switch (httpResponseCode) {
183+
case 200:
184+
return readGenerateAuthTokenResponse(httpsURLConnection);
185+
case 401:
186+
throw new FirebaseInstallationServiceException(
187+
UNAUTHORIZED_ERROR_MESSAGE, FirebaseInstallationServiceException.Code.UNAUTHORIZED);
188+
default:
189+
throw new FirebaseInstallationServiceException(
190+
INTERNAL_SERVER_ERROR_MESSAGE,
191+
FirebaseInstallationServiceException.Code.SERVER_ERROR);
192+
}
193+
} catch (IOException e) {
194+
throw new FirebaseInstallationServiceException(
195+
NETWORK_ERROR_MESSAGE + e.getMessage(),
196+
FirebaseInstallationServiceException.Code.NETWORK_ERROR);
197+
}
198+
}
199+
// Read the response from the createFirebaseInstallation API.
200+
private InstallationResponse readCreateResponse(HttpsURLConnection conn) throws IOException {
201+
JsonReader reader =
202+
new JsonReader(new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()));
203+
InstallationTokenResult.Builder installationTokenResult = InstallationTokenResult.builder();
204+
InstallationResponse.Builder builder = InstallationResponse.builder();
205+
reader.beginObject();
206+
while (reader.hasNext()) {
207+
String name = reader.nextName();
208+
if (name.equals("name")) {
209+
builder.setName(reader.nextString());
210+
} else if (name.equals("refreshToken")) {
211+
builder.setRefreshToken(reader.nextString());
212+
} else if (name.equals("authToken")) {
213+
reader.beginObject();
214+
while (reader.hasNext()) {
215+
String key = reader.nextName();
216+
if (key.equals("token")) {
217+
installationTokenResult.setAuthToken(reader.nextString());
218+
} else if (key.equals("expiresIn")) {
219+
installationTokenResult.setTokenExpirationTimestampMillis(reader.nextLong());
220+
} else {
221+
reader.skipValue();
222+
}
223+
}
224+
builder.setAuthToken(installationTokenResult.build());
225+
reader.endObject();
226+
} else {
227+
reader.skipValue();
228+
}
229+
}
230+
reader.endObject();
231+
232+
return builder.build();
233+
}
234+
235+
// Read the response from the generateAuthToken FirebaseInstallation API.
236+
private InstallationTokenResult readGenerateAuthTokenResponse(HttpsURLConnection conn)
237+
throws IOException {
238+
JsonReader reader =
239+
new JsonReader(new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()));
240+
InstallationTokenResult.Builder builder = InstallationTokenResult.builder();
241+
reader.beginObject();
242+
while (reader.hasNext()) {
243+
String name = reader.nextName();
244+
if (name.equals("token")) {
245+
builder.setAuthToken(reader.nextString());
246+
} else if (name.equals("expiresIn")) {
247+
builder.setTokenExpirationTimestampMillis(reader.nextLong());
248+
} else {
249+
reader.skipValue();
250+
}
251+
}
252+
reader.endObject();
253+
254+
return builder.build();
255+
}
256+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.installations.remote;
16+
17+
import androidx.annotation.NonNull;
18+
import com.google.firebase.FirebaseException;
19+
20+
/** The class for all Exceptions thrown by {@link FirebaseInstallationServiceClient}. */
21+
public class FirebaseInstallationServiceException extends FirebaseException {
22+
23+
public enum Code {
24+
SERVER_ERROR,
25+
26+
NETWORK_ERROR,
27+
28+
UNAUTHORIZED
29+
}
30+
31+
@NonNull private final Code code;
32+
33+
FirebaseInstallationServiceException(@NonNull Code code) {
34+
this.code = code;
35+
}
36+
37+
FirebaseInstallationServiceException(@NonNull String message, @NonNull Code code) {
38+
super(message);
39+
this.code = code;
40+
}
41+
42+
FirebaseInstallationServiceException(
43+
@NonNull String message, @NonNull Code code, Throwable cause) {
44+
super(message, cause);
45+
this.code = code;
46+
}
47+
48+
/**
49+
* Gets the status code for the operation that failed.
50+
*
51+
* @return the code for the FirebaseInstallationServiceException
52+
*/
53+
@NonNull
54+
public Code getCode() {
55+
return code;
56+
}
57+
}

0 commit comments

Comments
 (0)