Skip to content

Adding http client to call fis backend service #659

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 31 commits into from
Jul 30, 2019
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d39159b
Adding Firebase Installations module
ankitaj224 Jul 22, 2019
e321d7d
Readding .idea files that were deleted in previous commit
ankitaj224 Jul 22, 2019
c25ca83
Revert "Adding Firebase Installations module"
ankitaj224 Jul 22, 2019
6c54e73
Revert "Readding .idea files that were deleted in previous commit"
ankitaj224 Jul 22, 2019
1923730
Adding Firebase Installations module.
ankitaj224 Jul 22, 2019
e79e3ad
Readding .idea files that were deleted in previous commit
ankitaj224 Jul 22, 2019
bb41402
Revert "Readding .idea files that were deleted in previous commit"
ankitaj224 Jul 22, 2019
2851062
Revert "Adding Firebase Installations module" with hidden files
ankitaj224 Jul 22, 2019
a2f7f64
Addressing review comments.
ankitaj224 Jul 23, 2019
cde2320
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
a27b347
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
53dc90b
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
a7982e1
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Jul 29, 2019
7285408
Initial Code structure for FIS Android SDK (#648)
ankitaj224 Jul 24, 2019
19c5573
Adding Firebase Installations module
ankitaj224 Jul 22, 2019
892fddf
Readding .idea files that were deleted in previous commit
ankitaj224 Jul 22, 2019
8d3dc73
Revert "Adding Firebase Installations module"
ankitaj224 Jul 22, 2019
4ad27d7
Revert "Readding .idea files that were deleted in previous commit"
ankitaj224 Jul 22, 2019
7f45186
Adding Firebase Installations module.
ankitaj224 Jul 22, 2019
1b1766f
Readding .idea files that were deleted in previous commit
ankitaj224 Jul 22, 2019
4ac3956
Revert "Readding .idea files that were deleted in previous commit"
ankitaj224 Jul 22, 2019
da9aac6
Revert "Adding Firebase Installations module" with hidden files
ankitaj224 Jul 22, 2019
e9c10c2
Addressing review comments.
ankitaj224 Jul 23, 2019
ef6cf7d
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
284ab52
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
4332d7f
Http client to call FIS backend service.
ankitaj224 Jul 24, 2019
113216e
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Jul 29, 2019
936e2f8
Merge branch 'fis_sdk' of github.com:firebase/firebase-android-sdk in…
ankitaj224 Jul 29, 2019
1f427eb
Merge branch 'fis_sdk' of github.com:firebase/firebase-android-sdk in…
ankitaj224 Jul 29, 2019
5462241
Merge branch 'ankita_fis' of github.com:firebase/firebase-android-sdk…
ankitaj224 Jul 29, 2019
aa21227
Addresing comments and introducing new FirebaseInstallationService
ankitaj224 Jul 30, 2019
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 @@ -31,8 +31,23 @@ public abstract class InstallationTokenResult {
public abstract long getTokenExpirationTimestampMillis();

@NonNull
public static InstallationTokenResult create(
@NonNull String authToken, long tokenExpirationTimestampMillis) {
return new AutoValue_InstallationTokenResult(authToken, tokenExpirationTimestampMillis);
public abstract Builder toBuilder();

/** Returns a default Builder object to create an InstallationResponse object */
@NonNull
public static InstallationTokenResult.Builder builder() {
return new AutoValue_InstallationTokenResult.Builder();
}

@AutoValue.Builder
public abstract static class Builder {
@NonNull
public abstract Builder setAuthToken(@NonNull String value);

@NonNull
public abstract Builder setTokenExpirationTimestampMillis(long value);

@NonNull
public abstract InstallationTokenResult build();
}
}
4 changes: 4 additions & 0 deletions firebase-installations/firebase-installations.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.google.android.gms:play-services-tasks:17.0.0'


compileOnly "com.google.auto.value:auto-value-annotations:1.6.5"
annotationProcessor "com.google.auto.value:auto-value:1.6.2"

testImplementation 'androidx.test:core:1.2.0'
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public Task<String> getId() {
@NonNull
@Override
public Task<InstallationTokenResult> getAuthToken(boolean forceRefresh) {
return Tasks.forResult(InstallationTokenResult.create("dummy_auth_token", 1000l));
return Tasks.forResult(InstallationTokenResult.builder().build());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// Copyright 2019 Google LLC
//
// 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.installations.remote;

import android.util.JsonReader;
import androidx.annotation.NonNull;
import com.google.firebase.FirebaseException;
import com.google.firebase.installations.InstallationTokenResult;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HttpsURLConnection;
import org.json.JSONException;
import org.json.JSONObject;

/** Http client that sends request to Firebase Installations backend API. */
public class FirebaseInstallationServiceClient {
private static final String FIREBASE_INSTALLATIONS_API_DOMAIN =
"firebaseinstallations.googleapis.com";
private static final String CREATE_REQUEST_RESOURCE_NAME_FORMAT = "projects/%s/installations";
private static final String GENERATE_AUTH_TOKEN_REQUEST_RESOURCE_NAME_FORMAT =
"projects/%s/installations/%s/auth:generate";
private static final String DELETE_REQUEST_RESOURCE_NAME_FORMAT = "projects/%s/installations/%s";
private static final String FIREBASE_INSTALLATIONS_API_VERSION = "v1";
private static final String FIREBASE_INSTALLATION_AUTH_VERSION = "FIS_V2";

private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String CONTENT_ENCODING_HEADER_KEY = "Content-Encoding";
private static final String GZIP_CONTENT_ENCODING = "gzip";

public enum Code {
OK,

NETWORK_ERROR,

SERVER_ERROR,

UNAUTHORIZED,
}

@NonNull
public InstallationResponse createFirebaseInstallation(
long projectNumber,
@NonNull String apiKey,
@NonNull String firebaseInstallationId,
@NonNull String appId)
throws FirebaseException {
String resourceName = String.format(CREATE_REQUEST_RESOURCE_NAME_FORMAT, projectNumber);
try {
URL url =
new URL(
String.format(
"https://%s/%s/%s?key=%s",
FIREBASE_INSTALLATIONS_API_DOMAIN,
FIREBASE_INSTALLATIONS_API_VERSION,
resourceName,
apiKey));

HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setDoOutput(true);
httpsURLConnection.setRequestMethod("POST");
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
GZIPOutputStream gzipOutputStream =
new GZIPOutputStream(httpsURLConnection.getOutputStream());
try {
gzipOutputStream.write(
buildCreateFirebaseInstallationRequestBody(firebaseInstallationId, appId)
.toString()
.getBytes("UTF-8"));
} catch (JSONException e) {
throw new IllegalStateException(e);
} finally {
gzipOutputStream.close();
}

int httpResponseCode = httpsURLConnection.getResponseCode();
switch (httpResponseCode) {
case 200:
return readCreateResponse(httpsURLConnection);
case 401:
throw new FirebaseException("The request did not have the required credentials.");
default:
throw new FirebaseException("There was an internal server error.");
}
} catch (IOException e) {
throw new FirebaseException("The server returned an unexpected error.: " + e.getMessage());
}
}

private static JSONObject buildCreateFirebaseInstallationRequestBody(String fid, String appId)
throws JSONException {
JSONObject firebaseInstallationData = new JSONObject();
firebaseInstallationData.put("fid", fid);
firebaseInstallationData.put("appId", appId);
firebaseInstallationData.put("appVersion", FIREBASE_INSTALLATION_AUTH_VERSION);
return firebaseInstallationData;
}

@NonNull
public Code deleteFirebaseInstallation(
long projectNumber,
@NonNull String apiKey,
@NonNull String fid,
@NonNull String refreshToken) {
String resourceName = String.format(DELETE_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, fid);
try {
URL url =
new URL(
String.format(
"https://%s/%s/%s?key=%s",
FIREBASE_INSTALLATIONS_API_DOMAIN,
FIREBASE_INSTALLATIONS_API_VERSION,
resourceName,
apiKey));

HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setDoOutput(true);
httpsURLConnection.setRequestMethod("DELETE");
httpsURLConnection.addRequestProperty("Authorization", "FIS_V2 " + refreshToken);
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);

int httpResponseCode = httpsURLConnection.getResponseCode();
switch (httpResponseCode) {
case 200:
return Code.OK;
case 401:
return Code.UNAUTHORIZED;
default:
return Code.SERVER_ERROR;
}
} catch (IOException e) {
return Code.NETWORK_ERROR;
}
}

@NonNull
public InstallationTokenResult generateAuthToken(
long projectNumber, @NonNull String apiKey, @NonNull String fid, @NonNull String refreshToken)
throws FirebaseException {
String resourceName =
String.format(GENERATE_AUTH_TOKEN_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, fid);
try {
URL url =
new URL(
String.format(
"https://%s/%s/%s?key=%s",
FIREBASE_INSTALLATIONS_API_DOMAIN,
FIREBASE_INSTALLATIONS_API_VERSION,
resourceName,
apiKey));

HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setDoOutput(true);
httpsURLConnection.setRequestMethod("POST");
httpsURLConnection.addRequestProperty("Authorization", "FIS_V2 " + refreshToken);
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);

int httpResponseCode = httpsURLConnection.getResponseCode();
switch (httpResponseCode) {
case 200:
return readGenerateAuthTokenResponse(httpsURLConnection);
case 401:
throw new FirebaseException("The request did not have the required credentials.");
default:
throw new FirebaseException("There was an internal server error.");
}
} catch (IOException e) {
throw new FirebaseException("The server returned an unexpected error.: " + e.getMessage());
}
}
// Read the response from the createFirebaseInstallation API.
private InstallationResponse readCreateResponse(HttpsURLConnection conn) throws IOException {
JsonReader reader =
new JsonReader(new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()));
InstallationTokenResult.Builder installationTokenResult = InstallationTokenResult.builder();
InstallationResponse.Builder builder = InstallationResponse.builder();
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("name")) {
builder.setName(reader.nextString());
} else if (name.equals("refreshToken")) {
builder.setRefreshToken(reader.nextString());
} else if (name.equals("authToken")) {
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
if (key.equals("token")) {
installationTokenResult.setAuthToken(reader.nextString());
} else if (key.equals("expiresIn")) {
installationTokenResult.setTokenExpirationTimestampMillis(reader.nextLong());
} else {
reader.skipValue();
}
}
builder.setAuthToken(installationTokenResult.build());
reader.endObject();
} else {
reader.skipValue();
}
}
reader.endObject();

return builder.build();
}

// Read the response from the generateAuthToken FirebaseInstallation API.
private InstallationTokenResult readGenerateAuthTokenResponse(HttpsURLConnection conn)
throws IOException {
JsonReader reader =
new JsonReader(new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()));
InstallationTokenResult.Builder builder = InstallationTokenResult.builder();
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("token")) {
builder.setAuthToken(reader.nextString());
} else if (name.equals("expiresIn")) {
builder.setTokenExpirationTimestampMillis(reader.nextLong());
} else {
reader.skipValue();
}
}
reader.endObject();

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2019 Google LLC
//
// 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.installations.remote;

import androidx.annotation.NonNull;
import com.google.auto.value.AutoValue;
import com.google.firebase.installations.InstallationTokenResult;

@AutoValue
public abstract class InstallationResponse {

@NonNull
public abstract String getName();

@NonNull
public abstract String getRefreshToken();

@NonNull
public abstract InstallationTokenResult getAuthToken();

@NonNull
public abstract Builder toBuilder();

/** Returns a default Builder object to create an InstallationResponse object */
@NonNull
public static InstallationResponse.Builder builder() {
return new AutoValue_InstallationResponse.Builder();
}

@AutoValue.Builder
public abstract static class Builder {
@NonNull
public abstract Builder setName(@NonNull String value);

@NonNull
public abstract Builder setRefreshToken(@NonNull String value);

@NonNull
public abstract Builder setAuthToken(@NonNull InstallationTokenResult value);

@NonNull
public abstract InstallationResponse build();
}
}