Skip to content

Commit dc3efe6

Browse files
Merge
2 parents 0df060a + 6aa59b9 commit dc3efe6

File tree

12 files changed

+134
-335
lines changed

12 files changed

+134
-335
lines changed

ci/fireci/fireci/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ def api_information(auth_token, repo_name, issue_number):
9898
repo = github_client.get_repo(repo_name)
9999
pr = repo.get_pull(int(issue_number))
100100
pr.create_issue_comment(comment_string)
101+
exit(1)

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ in your app:
2525

2626
* [`firebase-common`](ktx/common.md)
2727
* [`firebase-firestore`](ktx/firestore.md)
28+
* [`firebase-functions`](ktx/functions.md)
2829

2930
[android-setup]: https://firebase.google.com/docs/android/setup
3031
[main-readme]: https://github.com/firebase/firebase-android-sdk/blob/master/README.md

docs/ktx/functions.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Cloud Functions Kotlin Extensions
2+
3+
## Getting Started
4+
5+
To use the Cloud Functions Android SDK with Kotlin Extensions, add the following
6+
to your app's `build.gradle` file:
7+
8+
```groovy
9+
// See maven.google.com for the latest versions
10+
// This library transitively includes the firebase-functions library
11+
implementation 'com.google.firebase:firebase-functions-ktx:$VERSION'
12+
```
13+
14+
## Features
15+
16+
### Get the FirebaseFunctions instance of the default app
17+
18+
**Kotlin**
19+
```kotlin
20+
val functions = FirebaseFunctions.getInstance()
21+
```
22+
23+
**Kotlin + KTX**
24+
```kotlin
25+
val functions = Firebase.functions
26+
```
27+
28+
### Get the FirebaseFunctions of a given region
29+
30+
**Kotlin**
31+
```kotlin
32+
val functions = FirebaseFunctions.getInstance(region)
33+
```
34+
35+
**Kotlin + KTX**
36+
```kotlin
37+
val functions = Firebase.functions(region)
38+
```
39+
40+
### Get the FirebaseFunctions of a given FirebaseApp
41+
42+
**Kotlin**
43+
```kotlin
44+
val functions = FirebaseFunctions.getInstance(app)
45+
```
46+
47+
**Kotlin + KTX**
48+
```kotlin
49+
val functions = Firebase.functions(app)
50+
```
51+
52+
### Get the FirebaseFunctions of a given region and FirebaseApp
53+
54+
**Kotlin**
55+
```kotlin
56+
val functions = FirebaseFunctions.getInstance(app, region)
57+
```
58+
59+
**Kotlin + KTX**
60+
```kotlin
61+
val functions = Firebase.functions(app, region)
62+
```

firebase-common/firebase-common.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ android {
5555
includeAndroidResources = true
5656
}
5757
}
58+
lintOptions {
59+
baseline file("lint-baseline.xml")
60+
}
5861
}
5962

6063
dependencies {

firebase-common/lint-baseline.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<issues format="5" by="lint 3.4.1" client="gradle" variant="all" version="3.4.1">
3+
<!-- TODO: remove when bazel support tools:targetApi -->
4+
<issue
5+
id="UnusedAttribute"
6+
message="Attribute `directBootAware` is only used in API level 24 and higher (current min is 14)"
7+
errorLine1=" android:directBootAware=&quot;true&quot; android:exported=&quot;false&quot;/>"
8+
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
9+
<location
10+
file="src/main/AndroidManifest.xml"
11+
line="11"
12+
column="9"/>
13+
</issue>
14+
15+
</issues>

firebase-common/src/main/AndroidManifest.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
<application>
99

1010
<service android:name="com.google.firebase.components.ComponentDiscoveryService"
11-
android:directBootAware="true" android:exported="false"
12-
tools:targetApi="n" />
11+
android:directBootAware="true" android:exported="false"/>
1312

1413
<provider
1514
android:name="com.google.firebase.provider.FirebaseInitProvider"

firebase-firestore/api.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,6 @@ package com.google.firebase.firestore {
165165
method public static void setLoggingEnabled(boolean);
166166
}
167167

168-
public static interface FirebaseFirestore.InstanceRegistry {
169-
method public void remove(@NonNull String);
170-
}
171-
172168
public class FirebaseFirestoreException {
173169
ctor public FirebaseFirestoreException(@NonNull String, @NonNull com.google.firebase.firestore.FirebaseFirestoreException.Code);
174170
ctor public FirebaseFirestoreException(@NonNull String, @NonNull com.google.firebase.firestore.FirebaseFirestoreException.Code, @Nullable Throwable);

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -326,56 +326,43 @@ public SnapshotVersion getLastRemoteSnapshotVersion() {
326326
* <p>LocalDocuments are re-calculated if there are remaining mutations in the queue.
327327
*/
328328
public ImmutableSortedMap<DocumentKey, MaybeDocument> applyRemoteEvent(RemoteEvent remoteEvent) {
329+
SnapshotVersion remoteVersion = remoteEvent.getSnapshotVersion();
330+
329331
// TODO: Call queryEngine.handleDocumentChange() appropriately.
330332
return persistence.runTransaction(
331333
"Apply remote event",
332334
() -> {
335+
Map<Integer, TargetChange> targetChanges = remoteEvent.getTargetChanges();
333336
long sequenceNumber = persistence.getReferenceDelegate().getCurrentSequenceNumber();
334-
Set<DocumentKey> authoritativeUpdates = new HashSet<>();
335337

336-
Map<Integer, TargetChange> targetChanges = remoteEvent.getTargetChanges();
337338
for (Map.Entry<Integer, TargetChange> entry : targetChanges.entrySet()) {
338339
Integer boxedTargetId = entry.getKey();
339340
int targetId = boxedTargetId;
340341
TargetChange change = entry.getValue();
341342

342-
// Do not ref/unref unassigned targetIds - it may lead to leaks.
343-
QueryData queryData = targetIds.get(targetId);
344-
if (queryData == null) {
343+
QueryData oldQueryData = targetIds.get(targetId);
344+
if (oldQueryData == null) {
345+
// We don't update the remote keys if the query is not active. This ensures that
346+
// we persist the updated query data along with the updated assignment.
345347
continue;
346348
}
347349

348-
// When a global snapshot contains updates (either add or modify) we can completely
349-
// trust these updates as authoritative and blindly apply them to our cache (as a
350-
// defensive measure to promote self-healing in the unfortunate case that our cache
351-
// is ever somehow corrupted / out-of-sync).
352-
//
353-
// If the document is only updated while removing it from a target then watch isn't
354-
// obligated to send the absolute latest version: it can send the first version that
355-
// caused the document not to match.
356-
for (DocumentKey key : change.getAddedDocuments()) {
357-
authoritativeUpdates.add(key);
358-
}
359-
for (DocumentKey key : change.getModifiedDocuments()) {
360-
authoritativeUpdates.add(key);
361-
}
362-
363350
queryCache.removeMatchingKeys(change.getRemovedDocuments(), targetId);
364351
queryCache.addMatchingKeys(change.getAddedDocuments(), targetId);
365352

366-
// Update the resume token if the change includes one. Don't clear any preexisting
367-
// value.
368353
ByteString resumeToken = change.getResumeToken();
354+
// Update the resume token if the change includes one.
369355
if (!resumeToken.isEmpty()) {
370-
QueryData oldQueryData = queryData;
371-
queryData =
372-
queryData
356+
QueryData newQueryData =
357+
oldQueryData
373358
.withResumeToken(resumeToken, remoteEvent.getSnapshotVersion())
374359
.withSequenceNumber(sequenceNumber);
375-
targetIds.put(boxedTargetId, queryData);
360+
targetIds.put(boxedTargetId, newQueryData);
376361

377-
if (shouldPersistQueryData(oldQueryData, queryData, change)) {
378-
queryCache.updateQueryData(queryData);
362+
// Update the query data if there are target changes (or if sufficient time has
363+
// passed since the last update).
364+
if (shouldPersistQueryData(oldQueryData, newQueryData, change)) {
365+
queryCache.updateQueryData(newQueryData);
379366
}
380367
}
381368
}
@@ -394,10 +381,9 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> applyRemoteEvent(RemoteEve
394381
MaybeDocument existingDoc = existingDocs.get(key);
395382

396383
if (existingDoc == null
397-
|| (authoritativeUpdates.contains(doc.getKey()) && !existingDoc.hasPendingWrites())
398-
|| doc.getVersion().compareTo(existingDoc.getVersion()) >= 0) {
399-
// If a document update isn't authoritative, make sure we don't apply an old document
400-
// version to the remote cache.
384+
|| doc.getVersion().compareTo(existingDoc.getVersion()) > 0
385+
|| (doc.getVersion().compareTo(existingDoc.getVersion()) == 0
386+
&& existingDoc.hasPendingWrites())) {
401387
remoteDocuments.add(doc, remoteEvent.getSnapshotVersion());
402388
changedDocs.put(key, doc);
403389
} else if (doc instanceof NoDocument && doc.getVersion().equals(SnapshotVersion.NONE)) {
@@ -425,7 +411,6 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> applyRemoteEvent(RemoteEve
425411
// remote events when we get permission denied errors while trying to resolve the
426412
// state of a locally cached document that is in limbo.
427413
SnapshotVersion lastRemoteVersion = queryCache.getLastRemoteSnapshotVersion();
428-
SnapshotVersion remoteVersion = remoteEvent.getSnapshotVersion();
429414
if (!remoteVersion.equals(SnapshotVersion.NONE)) {
430415
hardAssert(
431416
remoteVersion.compareTo(lastRemoteVersion) >= 0,
@@ -451,10 +436,11 @@ public ImmutableSortedMap<DocumentKey, MaybeDocument> applyRemoteEvent(RemoteEve
451436
*/
452437
private static boolean shouldPersistQueryData(
453438
QueryData oldQueryData, QueryData newQueryData, TargetChange change) {
454-
// Avoid clearing any existing value
455-
if (newQueryData.getResumeToken().isEmpty()) return false;
439+
hardAssert(
440+
!newQueryData.getResumeToken().isEmpty(),
441+
"Attempted to persist query data with empty resume token");
456442

457-
// Any resume token is interesting if there isn't one already.
443+
// Always persist query data if we don't already have a resume token.
458444
if (oldQueryData.getResumeToken().isEmpty()) return true;
459445

460446
// Don't allow resume token changes to be buffered indefinitely. This allows us to be reasonably

firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,4 +1386,27 @@ public void testGetHighestUnacknowledgedBatchId() {
13861386
rejectMutation();
13871387
assertEquals(MutationBatch.UNKNOWN, localStore.getHighestUnacknowledgedBatchId());
13881388
}
1389+
1390+
@Test
1391+
public void testOnlyPersistsUpdatesForDocumentsWhenVersionChanges() {
1392+
Query query = Query.atPath(ResourcePath.fromString("foo"));
1393+
allocateQuery(query);
1394+
assertTargetId(2);
1395+
1396+
applyRemoteEvent(
1397+
addedRemoteEvent(doc("foo/bar", 1, map("val", "old")), asList(2), emptyList()));
1398+
assertChanged(doc("foo/bar", 1, map("val", "old"), Document.DocumentState.SYNCED));
1399+
assertContains(doc("foo/bar", 1, map("val", "old"), Document.DocumentState.SYNCED));
1400+
1401+
applyRemoteEvent(
1402+
addedRemoteEvent(
1403+
asList(doc("foo/bar", 1, map("val", "new")), doc("foo/baz", 2, map("val", "new"))),
1404+
asList(2),
1405+
emptyList()));
1406+
1407+
assertChanged(doc("foo/baz", 2, map("val", "new"), Document.DocumentState.SYNCED));
1408+
// The update for foo/bar is ignored.
1409+
assertContains(doc("foo/bar", 1, map("val", "old"), Document.DocumentState.SYNCED));
1410+
assertContains(doc("foo/baz", 2, map("val", "new"), Document.DocumentState.SYNCED));
1411+
}
13891412
}

firebase-firestore/src/test/resources/json/limbo_spec_test.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@
611611
"docs": [
612612
{
613613
"key": "collection/a",
614-
"version": 1000,
614+
"version": 1002,
615615
"value": {
616616
"key": "b"
617617
},
@@ -948,7 +948,7 @@
948948
"docs": [
949949
{
950950
"key": "collection/a",
951-
"version": 1000,
951+
"version": 1002,
952952
"value": {
953953
"key": "b"
954954
},
@@ -1055,7 +1055,7 @@
10551055
"added": [
10561056
{
10571057
"key": "collection/a",
1058-
"version": 1000,
1058+
"version": 1002,
10591059
"value": {
10601060
"key": "b"
10611061
},

0 commit comments

Comments
 (0)