Skip to content

Commit a22e5d3

Browse files
committed
Merge remote-tracking branch 'origin/master' into rc-realtime-dev
2 parents a6f69c2 + 56c238c commit a22e5d3

File tree

10 files changed

+162
-61
lines changed

10 files changed

+162
-61
lines changed

firebase-appdistribution-api/ktx/api.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ package com.google.firebase.appdistribution.ktx {
33

44
public final class FirebaseAppDistributionKt {
55
ctor public FirebaseAppDistributionKt();
6+
method @NonNull public static operator com.google.firebase.appdistribution.BinaryType component1(@NonNull com.google.firebase.appdistribution.AppDistributionRelease);
7+
method public static operator long component1(@NonNull com.google.firebase.appdistribution.UpdateProgress);
8+
method @NonNull public static operator String component2(@NonNull com.google.firebase.appdistribution.AppDistributionRelease);
9+
method public static operator long component2(@NonNull com.google.firebase.appdistribution.UpdateProgress);
10+
method public static operator long component3(@NonNull com.google.firebase.appdistribution.AppDistributionRelease);
11+
method @NonNull public static operator com.google.firebase.appdistribution.UpdateStatus component3(@NonNull com.google.firebase.appdistribution.UpdateProgress);
12+
method @Nullable public static operator String component4(@NonNull com.google.firebase.appdistribution.AppDistributionRelease);
613
method @NonNull public static com.google.firebase.appdistribution.FirebaseAppDistribution getAppDistribution(@NonNull com.google.firebase.ktx.Firebase);
714
}
815

firebase-appdistribution-api/ktx/src/androidTest/kotlin/com/google/firebase/app/distribution/ktx/FirebaseAppDistributionTests.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
1919
import com.google.common.truth.Truth.assertThat
2020
import com.google.firebase.FirebaseApp
2121
import com.google.firebase.FirebaseOptions
22+
import com.google.firebase.appdistribution.AppDistributionRelease
23+
import com.google.firebase.appdistribution.BinaryType
2224
import com.google.firebase.appdistribution.FirebaseAppDistribution
25+
import com.google.firebase.appdistribution.UpdateProgress
26+
import com.google.firebase.appdistribution.UpdateStatus
2327
import com.google.firebase.ktx.Firebase
2428
import com.google.firebase.ktx.app
2529
import com.google.firebase.ktx.initialize
@@ -70,6 +74,43 @@ class FirebaseAppDistributionTests : BaseTestCase() {
7074
fun appDistribution_default_callsDefaultGetInstance() {
7175
assertThat(Firebase.appDistribution).isSameInstanceAs(FirebaseAppDistribution.getInstance())
7276
}
77+
78+
@Test
79+
fun appDistributionReleaseDestructuringDeclarationsWork() {
80+
val mockAppDistributionRelease = object : AppDistributionRelease {
81+
override fun getDisplayVersion(): String = "1.0.0"
82+
83+
override fun getVersionCode(): Long = 1L
84+
85+
override fun getReleaseNotes(): String = "Changelog..."
86+
87+
override fun getBinaryType(): BinaryType = BinaryType.AAB
88+
}
89+
90+
val (type, displayVersion, versionCode, notes) = mockAppDistributionRelease
91+
92+
assertThat(type).isEqualTo(mockAppDistributionRelease.binaryType)
93+
assertThat(displayVersion).isEqualTo(mockAppDistributionRelease.displayVersion)
94+
assertThat(versionCode).isEqualTo(mockAppDistributionRelease.versionCode)
95+
assertThat(notes).isEqualTo(mockAppDistributionRelease.releaseNotes)
96+
}
97+
98+
@Test
99+
fun updateProgressDestructuringDeclarationsWork() {
100+
val mockUpdateProgress = object : UpdateProgress {
101+
override fun getApkBytesDownloaded(): Long = 1200L
102+
103+
override fun getApkFileTotalBytes(): Long = 9000L
104+
105+
override fun getUpdateStatus(): UpdateStatus = UpdateStatus.DOWNLOADING
106+
}
107+
108+
val (downloaded, total, status) = mockUpdateProgress
109+
110+
assertThat(downloaded).isEqualTo(mockUpdateProgress.apkBytesDownloaded)
111+
assertThat(total).isEqualTo(mockUpdateProgress.apkFileTotalBytes)
112+
assertThat(status).isEqualTo(mockUpdateProgress.updateStatus)
113+
}
73114
}
74115

75116
internal const val LIBRARY_NAME: String = "fire-appdistribution-ktx"

firebase-appdistribution-api/ktx/src/main/kotlin/com/google/firebase/appdistribution/ktx/FirebaseAppDistribution.kt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ package com.google.firebase.appdistribution.ktx
1717
import androidx.annotation.Keep
1818
import com.google.firebase.BuildConfig
1919
import com.google.firebase.FirebaseApp
20+
import com.google.firebase.appdistribution.AppDistributionRelease
2021
import com.google.firebase.appdistribution.FirebaseAppDistribution
22+
import com.google.firebase.appdistribution.UpdateProgress
2123
import com.google.firebase.components.Component
2224
import com.google.firebase.components.ComponentRegistrar
2325
import com.google.firebase.ktx.Firebase
@@ -27,6 +29,55 @@ import com.google.firebase.platforminfo.LibraryVersionComponent
2729
val Firebase.appDistribution: FirebaseAppDistribution
2830
get() = FirebaseAppDistribution.getInstance()
2931

32+
/**
33+
* Destructuring declaration for [AppDistributionRelease] to provide binaryType.
34+
*
35+
* @return the binaryType of the [AppDistributionRelease]
36+
*/
37+
operator fun AppDistributionRelease.component1() = binaryType
38+
39+
/**
40+
* Destructuring declaration for [AppDistributionRelease] to provide displayVersion.
41+
*
42+
* @return the displayVersion of the [AppDistributionRelease]
43+
*/
44+
operator fun AppDistributionRelease.component2() = displayVersion
45+
46+
/**
47+
* Destructuring declaration for [AppDistributionRelease] to provide versionCode.
48+
*
49+
* @return the versionCode of the [AppDistributionRelease]
50+
*/
51+
operator fun AppDistributionRelease.component3() = versionCode
52+
53+
/**
54+
* Destructuring declaration for [AppDistributionRelease] to provide releaseNotes.
55+
*
56+
* @return the releaseNotes of the [AppDistributionRelease]
57+
*/
58+
operator fun AppDistributionRelease.component4() = releaseNotes
59+
60+
/**
61+
* Destructuring declaration for [UpdateProgress] to provide apkBytesDownloaded.
62+
*
63+
* @return the apkBytesDownloaded of the [UpdateProgress]
64+
*/
65+
operator fun UpdateProgress.component1() = apkBytesDownloaded
66+
67+
/**
68+
* Destructuring declaration for [UpdateProgress] to provide apkFileTotalBytes.
69+
*
70+
* @return the apkFileTotalBytes of the [UpdateProgress]
71+
*/
72+
operator fun UpdateProgress.component2() = apkFileTotalBytes
73+
74+
/**
75+
* Destructuring declaration for [UpdateProgress] to provide updateStatus.
76+
*
77+
* @return the updateStatus of the [UpdateProgress]
78+
*/
79+
operator fun UpdateProgress.component3() = updateStatus
80+
3081
internal const val LIBRARY_NAME: String = "fire-appdistribution-ktx"
3182

3283
/** @suppress */

firebase-firestore/src/main/java/com/google/firebase/firestore/Query.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.firebase.firestore;
1616

17+
import static com.google.firebase.firestore.core.Query.LimitType.LIMIT_TO_LAST;
1718
import static com.google.firebase.firestore.util.Assert.fail;
1819
import static com.google.firebase.firestore.util.Assert.hardAssert;
1920
import static com.google.firebase.firestore.util.Preconditions.checkNotNull;
@@ -1216,7 +1217,7 @@ private ListenerRegistration addSnapshotListenerInternal(
12161217
}
12171218

12181219
private void validateHasExplicitOrderByForLimitToLast() {
1219-
if (query.hasLimitToLast() && query.getExplicitOrderBy().isEmpty()) {
1220+
if (query.getLimitType().equals(LIMIT_TO_LAST) && query.getExplicitOrderBy().isEmpty()) {
12201221
throw new IllegalStateException(
12211222
"limitToLast() queries require specifying at least one orderBy() clause");
12221223
}

firebase-firestore/src/main/java/com/google/firebase/firestore/core/Query.java

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -147,34 +147,16 @@ public List<Filter> getFilters() {
147147
return filters;
148148
}
149149

150-
/**
151-
* The maximum number of results to return. If there is no limit on the query, then this will
152-
* cause an assertion failure.
153-
*/
154-
public long getLimitToFirst() {
155-
hardAssert(hasLimitToFirst(), "Called getLimitToFirst when no limit was set");
156-
return limit;
157-
}
158-
159-
public boolean hasLimitToFirst() {
160-
return limitType == LimitType.LIMIT_TO_FIRST && limit != Target.NO_LIMIT;
161-
}
162-
163-
/**
164-
* The maximum number of last-matching results to return. If there is no limit on the query, then
165-
* this will cause an assertion failure.
166-
*/
167-
public long getLimitToLast() {
168-
hardAssert(hasLimitToLast(), "Called getLimitToLast when no limit was set");
150+
/** The maximum number of results to return or {@link Target#NO_LIMIT} if there is no limit. */
151+
public long getLimit() {
169152
return limit;
170153
}
171154

172-
public boolean hasLimitToLast() {
173-
return limitType == LimitType.LIMIT_TO_LAST && limit != Target.NO_LIMIT;
155+
public boolean hasLimit() {
156+
return limit != Target.NO_LIMIT;
174157
}
175158

176159
public LimitType getLimitType() {
177-
hardAssert(hasLimitToLast() || hasLimitToFirst(), "Called getLimitType when no limit was set");
178160
return limitType;
179161
}
180162

firebase-firestore/src/main/java/com/google/firebase/firestore/core/View.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
package com.google.firebase.firestore.core;
1616

17+
import static com.google.firebase.firestore.core.Query.LimitType.LIMIT_TO_FIRST;
18+
import static com.google.firebase.firestore.core.Query.LimitType.LIMIT_TO_LAST;
1719
import static com.google.firebase.firestore.util.Assert.hardAssert;
1820
import static com.google.firebase.firestore.util.Util.compareIntegers;
1921

@@ -147,11 +149,11 @@ public DocumentChanges computeDocChanges(
147149
// Note that this should never get used in a refill (when previousChanges is set), because there
148150
// will only be adds -- no deletes or updates.
149151
Document lastDocInLimit =
150-
(query.hasLimitToFirst() && oldDocumentSet.size() == query.getLimitToFirst())
152+
(query.getLimitType().equals(LIMIT_TO_FIRST) && oldDocumentSet.size() == query.getLimit())
151153
? oldDocumentSet.getLastDocument()
152154
: null;
153155
Document firstDocInLimit =
154-
(query.hasLimitToLast() && oldDocumentSet.size() == query.getLimitToLast())
156+
(query.getLimitType().equals(LIMIT_TO_LAST) && oldDocumentSet.size() == query.getLimit())
155157
? oldDocumentSet.getFirstDocument()
156158
: null;
157159

@@ -222,11 +224,10 @@ public DocumentChanges computeDocChanges(
222224
}
223225

224226
// Drop documents out to meet limitToFirst/limitToLast requirement.
225-
if (query.hasLimitToFirst() || query.hasLimitToLast()) {
226-
long limit = query.hasLimitToFirst() ? query.getLimitToFirst() : query.getLimitToLast();
227-
for (long i = newDocumentSet.size() - limit; i > 0; --i) {
227+
if (query.hasLimit()) {
228+
for (long i = newDocumentSet.size() - query.getLimit(); i > 0; --i) {
228229
Document oldDoc =
229-
query.hasLimitToFirst()
230+
query.getLimitType().equals(LIMIT_TO_FIRST)
230231
? newDocumentSet.getLastDocument()
231232
: newDocumentSet.getFirstDocument();
232233
newDocumentSet = newDocumentSet.remove(oldDoc.getKey());

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package com.google.firebase.firestore.local;
1616

1717
import static com.google.firebase.firestore.util.Assert.hardAssert;
18-
import static com.google.firebase.firestore.util.Util.values;
1918

2019
import com.google.firebase.database.collection.ImmutableSortedMap;
2120
import com.google.firebase.database.collection.ImmutableSortedSet;
@@ -117,15 +116,14 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
117116
return null;
118117
}
119118

120-
if (indexType.equals(IndexType.PARTIAL)) {
119+
if (query.hasLimit() && indexType.equals(IndexType.PARTIAL)) {
121120
// We cannot apply a limit for targets that are served using a partial index.
122121
// If a partial index will be used to serve the target, the query may return a superset of
123122
// documents that match the target (e.g. if the index doesn't include all the target's
124123
// filters), or may return the correct set of documents in the wrong order (e.g. if the index
125124
// doesn't include a segment for one of the orderBys). Therefore a limit should not be applied
126125
// in such cases.
127-
query = query.limitToFirst(Target.NO_LIMIT);
128-
target = query.toTarget();
126+
return performQueryUsingIndex(query.limitToFirst(Target.NO_LIMIT));
129127
}
130128

131129
List<DocumentKey> keys = indexManager.getDocumentsMatchingTarget(target);
@@ -136,12 +134,15 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
136134
IndexOffset offset = indexManager.getMinOffset(target);
137135

138136
ImmutableSortedSet<Document> previousResults = applyQuery(query, indexedDocuments);
139-
if ((query.hasLimitToFirst() || query.hasLimitToLast())
140-
&& needsRefill(query.getLimitType(), keys.size(), previousResults, offset.getReadTime())) {
141-
return null;
137+
if (needsRefill(query, keys.size(), previousResults, offset.getReadTime())) {
138+
// A limit query whose boundaries change due to local edits can be re-run against the cache
139+
// by excluding the limit. This ensures that all documents that match the query's filters are
140+
// included in the result set. The SDK can then apply the limit once all local edits are
141+
// incorporated.
142+
return performQueryUsingIndex(query.limitToFirst(Target.NO_LIMIT));
142143
}
143144

144-
return appendRemainingResults(values(indexedDocuments), query, offset);
145+
return appendRemainingResults(previousResults, query, offset);
145146
}
146147

147148
/**
@@ -167,12 +168,7 @@ && needsRefill(query.getLimitType(), keys.size(), previousResults, offset.getRea
167168
localDocumentsView.getDocuments(remoteKeys);
168169
ImmutableSortedSet<Document> previousResults = applyQuery(query, documents);
169170

170-
if ((query.hasLimitToFirst() || query.hasLimitToLast())
171-
&& needsRefill(
172-
query.getLimitType(),
173-
remoteKeys.size(),
174-
previousResults,
175-
lastLimboFreeSnapshotVersion)) {
171+
if (needsRefill(query, remoteKeys.size(), previousResults, lastLimboFreeSnapshotVersion)) {
176172
return null;
177173
}
178174

@@ -211,7 +207,7 @@ private ImmutableSortedSet<Document> applyQuery(
211207
* Determines if a limit query needs to be refilled from cache, making it ineligible for
212208
* index-free execution.
213209
*
214-
* @param limitType The type of limit query for refill calculation.
210+
* @param query The query.
215211
* @param expectedDocumentCount The number of documents keys that matched the query at the last
216212
* snapshot.
217213
* @param sortedPreviousResults The documents that match the query based on the previous result,
@@ -221,12 +217,17 @@ private ImmutableSortedSet<Document> applyQuery(
221217
* synchronized.
222218
*/
223219
private boolean needsRefill(
224-
Query.LimitType limitType,
220+
Query query,
225221
int expectedDocumentCount,
226222
ImmutableSortedSet<Document> sortedPreviousResults,
227223
SnapshotVersion limboFreeSnapshotVersion) {
228-
// The query needs to be refilled if a previously matching document no longer matches.
224+
if (!query.hasLimit()) {
225+
// Queries without limits do not need to be refilled.
226+
return false;
227+
}
228+
229229
if (expectedDocumentCount != sortedPreviousResults.size()) {
230+
// The query needs to be refilled if a previously matching document no longer matches.
230231
return true;
231232
}
232233

@@ -237,7 +238,7 @@ private boolean needsRefill(
237238
// did not change and documents from cache will continue to be "rejected" by this boundary.
238239
// Therefore, we can ignore any modifications that don't affect the last document.
239240
Document documentAtLimitEdge =
240-
limitType == Query.LimitType.LIMIT_TO_FIRST
241+
query.getLimitType() == Query.LimitType.LIMIT_TO_FIRST
241242
? sortedPreviousResults.getMaxEntry()
242243
: sortedPreviousResults.getMinEntry();
243244
if (documentAtLimitEdge == null) {

firebase-firestore/src/test/java/com/google/firebase/firestore/bundle/BundleSerializerTest.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -712,30 +712,26 @@ private void assertDecodesNamedQuery(String json, Query query) throws JSONExcept
712712
+ json
713713
+ ",\n"
714714
+ " limitType: '"
715-
+ (query.hasLimitToLast() ? "LAST" : "FIRST")
715+
+ (query.getLimitType().equals(Query.LimitType.LIMIT_TO_LAST) ? "LAST" : "FIRST")
716716
+ "'\n"
717717
+ " },\n"
718718
+ " readTime: '2020-01-01T00:00:01.000000001Z'\n"
719719
+ "}";
720720
NamedQuery actualNamedQuery = serializer.decodeNamedQuery(new JSONObject(queryJson));
721721

722-
long limit =
723-
query.hasLimitToFirst()
724-
? query.getLimitToFirst()
725-
: (query.hasLimitToLast() ? query.getLimitToLast() : Target.NO_LIMIT);
726722
Target target =
727723
new Target(
728724
query.getPath(),
729725
query.getCollectionGroup(),
730726
query.getFilters(),
731727
query.getExplicitOrderBy(),
732-
limit,
728+
query.getLimit(),
733729
query.getStartAt(),
734730
query.getEndAt());
735731
BundledQuery bundledQuery =
736732
new BundledQuery(
737733
target,
738-
query.hasLimitToLast()
734+
query.getLimitType().equals(Query.LimitType.LIMIT_TO_LAST)
739735
? Query.LimitType.LIMIT_TO_LAST
740736
: Query.LimitType.LIMIT_TO_FIRST);
741737
NamedQuery expectedNamedQuery =

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public void testUsesPartiallyIndexedOverlaysWhenAvailable() {
213213
}
214214

215215
@Test
216-
public void testDoesNotUseIndexForLimitQueryWhenIndexIsOutdated() {
216+
public void testDoesNotUseLimitWhenIndexIsOutdated() {
217217
FieldIndex index =
218218
fieldIndex("coll", 0, FieldIndex.INITIAL_STATE, "count", FieldIndex.Segment.Kind.ASCENDING);
219219
configureFieldIndexes(singletonList(index));
@@ -234,10 +234,10 @@ public void testDoesNotUseIndexForLimitQueryWhenIndexIsOutdated() {
234234
writeMutation(deleteMutation("coll/b"));
235235

236236
executeQuery(query);
237-
// The query engine first reads the documents by key and then discards the results, which means
238-
// that we read both by key and by collection.
239-
assertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 3);
240-
assertOverlaysRead(/* byKey= */ 2, /* byCollection= */ 1);
237+
238+
// The query engine first reads the documents by key and then re-runs the query without limit.
239+
assertRemoteDocumentsRead(/* byKey= */ 5, /* byCollection= */ 0);
240+
assertOverlaysRead(/* byKey= */ 5, /* byCollection= */ 1);
241241
assertQueryReturned("coll/a", "coll/c");
242242
}
243243

0 commit comments

Comments
 (0)