Skip to content

Commit bcf86b8

Browse files
authored
Making transport schemas more testable (#575)
* Moving schema manager to separate file Injecting sql queries for better testability * Extracting index queries * Removed migrations * Fix schema manager * Fixed tests * Fixed instrumentation tests * Java format * Adding an "extras" column to transport databases (#578) * Legacy Firelog backend impl (#610) * Added tests for upgrade * Added tests for nullability * Modelling creates as migrations * Review feedback * Review feedbackwq * Throwing exception * added test * java formatting * Fixed tests * Apis to wire in extras (#581) * Review feedback * Review feedback * Apis to wire in extras * 1 * Flg destination * Tests * Fixed test * Review feedback * Review feedback * Added lflg transport * Review feedback
1 parent 3a40871 commit bcf86b8

File tree

29 files changed

+947
-176
lines changed

29 files changed

+947
-176
lines changed

transport/transport-backend-cct/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
android:exported="false">
2727
<meta-data
2828
android:name="backend:com.google.android.datatransport.cct.CctBackendFactory"
29-
android:value="cct" />
29+
android:value="cct,lflg" />
3030
</service>
3131
</application>
3232
</manifest>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.android.datatransport.cct;
16+
17+
import androidx.annotation.NonNull;
18+
import androidx.annotation.Nullable;
19+
import com.google.android.datatransport.runtime.Destination;
20+
21+
public final class CCTDestination implements Destination {
22+
static final String DESTINATION_NAME = "cct";
23+
public static final CCTDestination INSTANCE = new CCTDestination();
24+
25+
private CCTDestination() {}
26+
27+
@NonNull
28+
@Override
29+
public String getName() {
30+
return DESTINATION_NAME;
31+
}
32+
33+
@Nullable
34+
@Override
35+
public byte[] getExtras() {
36+
return null;
37+
}
38+
}

transport/transport-backend-cct/src/main/java/com/google/android/datatransport/cct/CctBackendFactory.java

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,33 @@
1515
package com.google.android.datatransport.cct;
1616

1717
import androidx.annotation.Keep;
18-
import androidx.annotation.VisibleForTesting;
1918
import com.google.android.datatransport.runtime.backends.BackendFactory;
2019
import com.google.android.datatransport.runtime.backends.CreationContext;
2120
import com.google.android.datatransport.runtime.backends.TransportBackend;
2221

2322
@Keep
2423
public class CctBackendFactory implements BackendFactory {
25-
private static final String URL =
26-
mergeStrings("hts/frbslgiggolai.o/0clgbth", "tp:/ieaeogn.ogepscmvc/o/ac");
24+
static final String CCT_URL =
25+
StringMerger.mergeStrings("hts/frbslgiggolai.o/0clgbth", "tp:/ieaeogn.ogepscmvc/o/ac");
26+
27+
static final String LFLG_URL =
28+
StringMerger.mergeStrings(
29+
"hts/frbslgigp.ogepscmv/ieo/eaylg", "tp:/ieaeogn-agolai.o/1frlglgc/o");
2730

2831
@Override
2932
public TransportBackend create(CreationContext creationContext) {
33+
final String url;
34+
// Since legacy flg and clearcut APIs are identical, they share the same backend.
35+
if (creationContext.getBackendName().equals(LegacyFlgDestination.DESTINATION_NAME)) {
36+
url = LFLG_URL;
37+
} else {
38+
url = CCT_URL;
39+
}
40+
3041
return new CctTransportBackend(
3142
creationContext.getApplicationContext(),
32-
URL,
43+
url,
3344
creationContext.getWallClock(),
3445
creationContext.getMonotonicClock());
3546
}
36-
37-
@VisibleForTesting
38-
static String mergeStrings(String part1, String part2) {
39-
int sizeDiff = part1.length() - part2.length();
40-
if (sizeDiff < 0 || sizeDiff > 1) {
41-
throw new IllegalArgumentException("Invalid input received");
42-
}
43-
44-
StringBuilder url = new StringBuilder(part1.length() + part2.length());
45-
46-
for (int i = 0; i < part1.length(); i++) {
47-
url.append(part1.charAt(i));
48-
if (part2.length() > i) {
49-
url.append(part2.charAt(i));
50-
}
51-
}
52-
53-
return url.toString();
54-
}
5547
}

transport/transport-backend-cct/src/main/java/com/google/android/datatransport/cct/CctTransportBackend.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ final class CctTransportBackend implements TransportBackend {
6565
private static final String CONTENT_ENCODING_HEADER_KEY = "Content-Encoding";
6666
private static final String GZIP_CONTENT_ENCODING = "gzip";
6767
private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
68+
static final String API_KEY_HEADER_KEY = "X-Goog-Api-Key";
6869
private static final String PROTOBUF_CONTENT_TYPE = "application/x-protobuf";
6970

7071
@VisibleForTesting static final String KEY_NETWORK_TYPE = "net-type";
@@ -81,7 +82,7 @@ final class CctTransportBackend implements TransportBackend {
8182
private static final String KEY_TIMEZONE_OFFSET = "tz-offset";
8283

8384
private final ConnectivityManager connectivityManager;
84-
private final URL endPoint;
85+
final URL endPoint;
8586
private final Clock uptimeClock;
8687
private final Clock wallTimeClock;
8788
private final int readTimeout;
@@ -211,7 +212,7 @@ private BatchedLogRequest getRequestBody(BackendRequest backendRequest) {
211212
return batchedRequestBuilder.build();
212213
}
213214

214-
private BackendResponse doSend(BatchedLogRequest requestBody) throws IOException {
215+
private BackendResponse doSend(BatchedLogRequest requestBody, String apiKey) throws IOException {
215216
HttpURLConnection connection = (HttpURLConnection) endPoint.openConnection();
216217
connection.setConnectTimeout(CONNECTION_TIME_OUT);
217218
connection.setReadTimeout(readTimeout);
@@ -223,6 +224,10 @@ private BackendResponse doSend(BatchedLogRequest requestBody) throws IOException
223224
connection.setRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
224225
connection.setRequestProperty(CONTENT_TYPE_HEADER_KEY, PROTOBUF_CONTENT_TYPE);
225226

227+
if (apiKey != null) {
228+
connection.setRequestProperty(API_KEY_HEADER_KEY, apiKey);
229+
}
230+
226231
WritableByteChannel channel = Channels.newChannel(connection.getOutputStream());
227232
try {
228233
ByteArrayOutputStream output = new ByteArrayOutputStream();
@@ -263,8 +268,14 @@ private BackendResponse doSend(BatchedLogRequest requestBody) throws IOException
263268
@Override
264269
public BackendResponse send(BackendRequest request) {
265270
BatchedLogRequest requestBody = getRequestBody(request);
271+
// CCT backend supports 2 different endpoints
272+
// We route to CCT backend if extras are null and to LegacyFlg otherwise.
273+
// This (anti-) pattern should not be required for other backends
274+
final String apiKey =
275+
request.getExtras() == null ? null : LegacyFlgDestination.decodeExtras(request.getExtras());
276+
266277
try {
267-
return doSend(requestBody);
278+
return doSend(requestBody, apiKey);
268279
} catch (IOException e) {
269280
LOGGER.log(Level.SEVERE, "Could not make request to the backend", e);
270281
return BackendResponse.transientError();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.android.datatransport.cct;
16+
17+
import androidx.annotation.NonNull;
18+
import com.google.android.datatransport.runtime.Destination;
19+
import java.io.UnsupportedEncodingException;
20+
21+
public class LegacyFlgDestination implements Destination {
22+
static final String DESTINATION_NAME = "lflg";
23+
private static final String DEFAULT_API_KEY =
24+
StringMerger.mergeStrings("AzSCki82AwsLzKd5O8zo", "IayckHiZRO1EFl1aGoK");
25+
26+
public static final LegacyFlgDestination DEFAULT_INSTANCE =
27+
new LegacyFlgDestination(DEFAULT_API_KEY);
28+
29+
private final String apiKey;
30+
31+
private LegacyFlgDestination(String apiKey) {
32+
this.apiKey = apiKey;
33+
}
34+
35+
@NonNull
36+
String getAPIKey() {
37+
return apiKey;
38+
}
39+
40+
@NonNull
41+
@Override
42+
public String getName() {
43+
return DESTINATION_NAME;
44+
}
45+
46+
@NonNull
47+
@Override
48+
public byte[] getExtras() {
49+
return encodeString(apiKey);
50+
}
51+
52+
@NonNull
53+
static byte[] encodeString(@NonNull String s) {
54+
try {
55+
return s.getBytes("UTF-8");
56+
} catch (UnsupportedEncodingException e) {
57+
throw new IllegalStateException("UTF-8 encoding not found.");
58+
}
59+
}
60+
61+
@NonNull
62+
static String decodeExtras(@NonNull byte[] a) {
63+
try {
64+
return new String(a, "UTF-8");
65+
} catch (UnsupportedEncodingException e) {
66+
throw new IllegalStateException("UTF-8 encoding not found.");
67+
}
68+
}
69+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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.android.datatransport.cct;
16+
17+
public final class StringMerger {
18+
static String mergeStrings(String part1, String part2) {
19+
int sizeDiff = part1.length() - part2.length();
20+
if (sizeDiff < 0 || sizeDiff > 1) {
21+
throw new IllegalArgumentException("Invalid input received");
22+
}
23+
24+
StringBuilder url = new StringBuilder(part1.length() + part2.length());
25+
26+
for (int i = 0; i < part1.length(); i++) {
27+
url.append(part1.charAt(i));
28+
if (part2.length() > i) {
29+
url.append(part2.charAt(i));
30+
}
31+
}
32+
33+
return url.toString();
34+
}
35+
}

transport/transport-backend-cct/src/test/java/com/google/android/datatransport/cct/CctBackendFactoryTest.java

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,61 @@
1414

1515
package com.google.android.datatransport.cct;
1616

17+
import static com.google.android.datatransport.cct.CctBackendFactory.CCT_URL;
18+
import static com.google.android.datatransport.cct.CctBackendFactory.LFLG_URL;
1719
import static com.google.common.truth.Truth.assertThat;
18-
import static org.junit.Assert.assertThrows;
1920

21+
import com.google.android.datatransport.runtime.backends.CreationContext;
22+
import com.google.android.datatransport.runtime.time.TestClock;
23+
import java.net.MalformedURLException;
24+
import java.net.URL;
2025
import org.junit.Test;
2126
import org.junit.runner.RunWith;
22-
import org.junit.runners.JUnit4;
27+
import org.robolectric.RobolectricTestRunner;
28+
import org.robolectric.RuntimeEnvironment;
2329

24-
@RunWith(JUnit4.class)
30+
@RunWith(RobolectricTestRunner.class)
2531
public class CctBackendFactoryTest {
32+
private static final long INITIAL_WALL_TIME = 200L;
33+
private static final long INITIAL_UPTIME = 10L;
34+
private TestClock wallClock = new TestClock(INITIAL_WALL_TIME);
35+
private TestClock uptimeClock = new TestClock(INITIAL_UPTIME);
36+
2637
@Test
27-
public void mergeStrings_whenPartsAreUnequalLength() {
28-
String part1 = "hts/eapecm";
29-
String part2 = "tp:/xml.o";
30-
assertThat(CctBackendFactory.mergeStrings(part1, part2)).isEqualTo("https://example.com");
38+
public void create_returnCCTBackend_WhenBackendNameIsCCT() throws MalformedURLException {
39+
CctBackendFactory cctBackendFactory = new CctBackendFactory();
40+
CreationContext creationContext =
41+
CreationContext.create(
42+
RuntimeEnvironment.application,
43+
wallClock,
44+
uptimeClock,
45+
CCTDestination.DESTINATION_NAME);
46+
47+
CctTransportBackend backend = (CctTransportBackend) cctBackendFactory.create(creationContext);
48+
assertThat(backend.endPoint).isEqualTo(new URL(CCT_URL));
3149
}
3250

3351
@Test
34-
public void mergeStrings_whenPartsAreEqualLength() {
35-
String part1 = "hts/eape.o";
36-
String part2 = "tp:/xmlscm";
37-
assertThat(CctBackendFactory.mergeStrings(part1, part2)).isEqualTo("https://examples.com");
52+
public void create_returnCCTBackend_WhenNoBackendIsSpecified() throws MalformedURLException {
53+
CctBackendFactory cctBackendFactory = new CctBackendFactory();
54+
CreationContext creationContext =
55+
CreationContext.create(RuntimeEnvironment.application, wallClock, uptimeClock);
56+
57+
CctTransportBackend backend = (CctTransportBackend) cctBackendFactory.create(creationContext);
58+
assertThat(backend.endPoint).isEqualTo(new URL(CCT_URL));
3859
}
3960

4061
@Test
41-
public void mergeStrings_whenPart2IsLongerThanPart1() {
42-
String part1 = "135";
43-
String part2 = "2467";
44-
assertThrows(
45-
IllegalArgumentException.class, () -> CctBackendFactory.mergeStrings(part1, part2));
62+
public void create_returnCCTBackend_WhenBackendNameIslflg() throws MalformedURLException {
63+
CctBackendFactory cctBackendFactory = new CctBackendFactory();
64+
CreationContext creationContext =
65+
CreationContext.create(
66+
RuntimeEnvironment.application,
67+
wallClock,
68+
uptimeClock,
69+
LegacyFlgDestination.DESTINATION_NAME);
70+
71+
CctTransportBackend backend = (CctTransportBackend) cctBackendFactory.create(creationContext);
72+
assertThat(backend.endPoint).isEqualTo(new URL(LFLG_URL));
4673
}
4774
}

0 commit comments

Comments
 (0)