Skip to content

Commit fbdca95

Browse files
authored
Release Firestore bundles (#7717)
* Introduce new bundle.proto (#7283) * Create leveldb "table"s for bundles (#7303) * Add bundle cache (#7320) * Bundle json parsing (#7356) * BundleSerializer complete. (#7454) * Bundles LocalStore change (#7466) * Implement bundle reader to parse a bundle inputstream (#7527) * Implement BundleLoader (#7536) * Add public API for bundles and integration tests (#7621) * Json parser as a hard copy (#7678) * fix asan errors (#7686) * Changelog entry * Add public comments for bundles (#7694) * Should 7.9.0 * API amendment
1 parent e6bb4d8 commit fbdca95

File tree

170 files changed

+39073
-487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+39073
-487
lines changed

CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ target_include_directories(
251251
$<BUILD_INTERFACE:${FIREBASE_EXTERNAL_SOURCE_DIR}/nanopb>
252252
)
253253

254-
255254
# XCTest
256255
if(APPLE)
257256
find_package(XCTest)

CoreOnly/NOTICES

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4511,6 +4511,30 @@ redistribute it freely, subject to the following restrictions:
45114511
3. This notice may not be removed or altered from any source
45124512
distribution.
45134513

4514+
nlohmann_json
4515+
4516+
MIT License
4517+
4518+
Copyright (c) 2013-2020 Niels Lohmann
4519+
4520+
Permission is hereby granted, free of charge, to any person obtaining a copy
4521+
of this software and associated documentation files (the "Software"), to deal
4522+
in the Software without restriction, including without limitation the rights
4523+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4524+
copies of the Software, and to permit persons to whom the Software is
4525+
furnished to do so, subject to the following conditions:
4526+
4527+
The above copyright notice and this permission notice shall be included in all
4528+
copies or substantial portions of the Software.
4529+
4530+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4531+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4532+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4533+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4534+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4535+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4536+
SOFTWARE.
4537+
45144538
Ooura FFT
45154539
Copyright(C) 1997,2001 Takuya OOURA (email: [email protected]).
45164540
You may use, copy, modify this code for any purpose and

Firestore/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# v7.9.0
2+
- [feature] Added support for Firestore Bundles via
3+
`FIRFirestore.loadBundle`, `FIRFirestore.loadBundleStream` and
4+
`FIRFirestore.getQueryNamed`. Bundles contain pre-packaged data produced
5+
with the Server SDKs and can be used to populate Firestore's cache
6+
without reading documents from the backend.
7+
18
# v7.7.0
29
- [fixed] Fixed a crash that could happen when the App is being deleted and
310
there's an active listener (#6909).

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 192 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#import <FirebaseFirestore/FirebaseFirestore.h>
17+
18+
#import <XCTest/XCTest.h>
19+
20+
#import "Firestore/Source/API/FIRLoadBundleTask+Internal.h"
21+
22+
#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
23+
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
24+
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
25+
26+
#import "../../../../../../../../../Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/experimental/string"
27+
#include "Firestore/core/src/util/string_apple.h"
28+
#include "Firestore/core/test/unit/testutil/bundle_builder.h"
29+
30+
namespace testutil = firebase::firestore::testutil;
31+
namespace util = firebase::firestore::util;
32+
33+
@interface FIRBundlesTests : FSTIntegrationTestCase
34+
@end
35+
36+
@implementation FIRBundlesTests
37+
38+
// Clears persistence for each test method to have a clean start.
39+
- (void)setUp {
40+
[super setUp];
41+
self.db = [self firestore];
42+
XCTestExpectation* exp = [self expectationWithDescription:@"clear persistence"];
43+
[self.db clearPersistenceWithCompletion:^(NSError*) {
44+
[exp fulfill];
45+
}];
46+
[self awaitExpectation:exp];
47+
}
48+
49+
- (void)verifyProgress:(FIRLoadBundleTaskProgress*)progress hasLoadedDocument:(int32_t)loaded {
50+
XCTAssertEqual(progress.state, FIRLoadBundleTaskStateInProgress);
51+
XCTAssertLessThanOrEqual(progress.bytesLoaded, progress.totalBytes);
52+
XCTAssertLessThanOrEqual(progress.documentsLoaded, progress.totalDocuments);
53+
XCTAssertEqual(progress.documentsLoaded, loaded);
54+
}
55+
56+
- (void)verifySuccessProgress:(FIRLoadBundleTaskProgress*)progress {
57+
XCTAssertEqual(progress.state, FIRLoadBundleTaskStateSuccess);
58+
XCTAssertGreaterThan(progress.bytesLoaded, 0);
59+
XCTAssertEqual(progress.bytesLoaded, progress.totalBytes);
60+
XCTAssertGreaterThan(progress.documentsLoaded, 0);
61+
XCTAssertEqual(progress.documentsLoaded, progress.totalDocuments);
62+
}
63+
64+
- (void)verifyErrorProgress:(FIRLoadBundleTaskProgress*)progress {
65+
XCTAssertEqual(progress.state, FIRLoadBundleTaskStateError);
66+
XCTAssertEqual(progress.bytesLoaded, 0);
67+
XCTAssertEqual(progress.documentsLoaded, 0);
68+
}
69+
70+
- (std::string)defaultBundle {
71+
return testutil::CreateBundle(util::MakeString([FSTIntegrationTestCase projectID]));
72+
}
73+
74+
- (std::string)bundleForProject:(NSString*)projectID {
75+
return testutil::CreateBundle(util::MakeString(projectID));
76+
}
77+
78+
- (void)verifyQueryResults {
79+
FIRCollectionReference* query = [self.db collectionWithPath:@"coll-1"];
80+
FIRQuerySnapshot* snapshot = [self readDocumentSetForRef:query source:FIRFirestoreSourceCache];
81+
NSArray* expected = @[ @{@"bar" : @1L, @"k" : @"a"}, @{@"bar" : @2L, @"k" : @"b"} ];
82+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected);
83+
84+
[self verifyNamedQuery:@"limit" hasResult:@[ @{@"bar" : @2L, @"k" : @"b"} ]];
85+
[self verifyNamedQuery:@"limit-to-last" hasResult:@[ @{@"bar" : @1L, @"k" : @"a"} ]];
86+
}
87+
88+
- (void)verifyNamedQuery:(NSString*)name hasResult:(NSArray*)expected {
89+
XCTestExpectation* expectation = [self expectationWithDescription:@"namedQuery"];
90+
__block FIRQuery* query;
91+
[self.db getQueryNamed:name
92+
completion:^(FIRQuery* q) {
93+
query = q;
94+
[expectation fulfill];
95+
}];
96+
[self awaitExpectation:expectation];
97+
FIRQuerySnapshot* snapshot = [self readDocumentSetForRef:query source:FIRFirestoreSourceCache];
98+
XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), expected);
99+
}
100+
101+
- (void)testLoadWithDocumentsThatAreAlreadyPulledFromBackend {
102+
[self writeDocumentRef:[self.db documentWithPath:@"coll-1/a"] data:@{@"bar" : @"newValueA"}];
103+
[self writeDocumentRef:[self.db documentWithPath:@"coll-1/b"] data:@{@"bar" : @"newValueB"}];
104+
105+
// Finishing receiving backend event.
106+
FIRCollectionReference* collection = [self.db collectionWithPath:@"coll-1"];
107+
id<FIRListenerRegistration> registration =
108+
[collection addSnapshotListener:self.eventAccumulator.valueEventHandler];
109+
[self.eventAccumulator awaitRemoteEvent];
110+
111+
// We should see no more snapshots from loading the bundle, because the data there is older.
112+
[self.eventAccumulator assertNoAdditionalEvents];
113+
114+
auto bundle = [self defaultBundle];
115+
NSMutableArray* progresses = [[NSMutableArray alloc] init];
116+
__block FIRLoadBundleTaskProgress* result;
117+
XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"];
118+
FIRLoadBundleTask* task =
119+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]
120+
completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) {
121+
result = progress;
122+
XCTAssertNil(error);
123+
[expectation fulfill];
124+
}];
125+
[task addObserver:^(FIRLoadBundleTaskProgress* progress) {
126+
[progresses addObject:progress];
127+
}];
128+
129+
[self awaitExpectation:expectation];
130+
131+
XCTAssertEqual(4ul, progresses.count);
132+
[self verifyProgress:progresses[0] hasLoadedDocument:0];
133+
[self verifyProgress:progresses[1] hasLoadedDocument:1];
134+
[self verifyProgress:progresses[2] hasLoadedDocument:2];
135+
[self verifySuccessProgress:progresses[3]];
136+
XCTAssertEqualObjects(progresses[3], result);
137+
138+
[self verifyNamedQuery:@"limit" hasResult:@[ @{@"bar" : @"newValueB"} ]];
139+
[self verifyNamedQuery:@"limit-to-last" hasResult:@[ @{@"bar" : @"newValueA"} ]];
140+
141+
[registration remove];
142+
}
143+
144+
- (void)testLoadDocumentsWithProgressUpdates {
145+
NSMutableArray* progresses = [[NSMutableArray alloc] init];
146+
auto bundle = [self defaultBundle];
147+
148+
__block FIRLoadBundleTaskProgress* result;
149+
XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"];
150+
FIRLoadBundleTask* task =
151+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]
152+
completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) {
153+
result = progress;
154+
XCTAssertNil(error);
155+
[expectation fulfill];
156+
}];
157+
[task addObserver:^(FIRLoadBundleTaskProgress* progress) {
158+
[progresses addObject:progress];
159+
}];
160+
161+
[self awaitExpectation:expectation];
162+
163+
XCTAssertEqual(4ul, progresses.count);
164+
[self verifyProgress:progresses[0] hasLoadedDocument:0];
165+
[self verifyProgress:progresses[1] hasLoadedDocument:1];
166+
[self verifyProgress:progresses[2] hasLoadedDocument:2];
167+
[self verifySuccessProgress:progresses[3]];
168+
XCTAssertEqualObjects(progresses[3], result);
169+
170+
[self verifyQueryResults];
171+
}
172+
173+
- (void)testLoadForASecondTimeSkips {
174+
auto bundle = [self defaultBundle];
175+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]];
176+
177+
// Load for a second time
178+
NSMutableArray* progresses = [[NSMutableArray alloc] init];
179+
__block FIRLoadBundleTaskProgress* result;
180+
XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"];
181+
FIRLoadBundleTask* task =
182+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]
183+
completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) {
184+
result = progress;
185+
XCTAssertNil(error);
186+
[expectation fulfill];
187+
}];
188+
[task addObserver:^(FIRLoadBundleTaskProgress* progress) {
189+
[progresses addObject:progress];
190+
}];
191+
192+
[self awaitExpectation:expectation];
193+
194+
XCTAssertEqual(1ul, progresses.count);
195+
[self verifySuccessProgress:progresses[0]];
196+
XCTAssertEqualObjects(progresses[0], result);
197+
198+
[self verifyQueryResults];
199+
}
200+
201+
- (void)testLoadedDocumentsShouldNotBeGarbageCollectedRightAway {
202+
auto settings = [self.db settings];
203+
[settings setPersistenceEnabled:FALSE];
204+
[self.db setSettings:settings];
205+
206+
auto bundle = [self defaultBundle];
207+
__block FIRLoadBundleTaskProgress* result;
208+
XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"];
209+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]
210+
completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) {
211+
result = progress;
212+
XCTAssertNil(error);
213+
[expectation fulfill];
214+
}];
215+
[self awaitExpectation:expectation];
216+
[self verifySuccessProgress:result];
217+
218+
// Read a different collection. This will trigger GC.
219+
[self readDocumentSetForRef:[self.db collectionWithPath:@"coll-other"]];
220+
221+
// Read the loaded documents, expecting them to exist in cache. With memory GC, the documents
222+
// would get GC-ed if we did not hold the document keys in an "umbrella" target. See
223+
// LocalStore for details.
224+
[self verifyQueryResults];
225+
}
226+
227+
- (void)testLoadBundlesFromOtherProjectFails {
228+
NSMutableArray* progresses = [[NSMutableArray alloc] init];
229+
__block FIRLoadBundleTaskProgress* result;
230+
XCTestExpectation* expectation = [self expectationWithDescription:@"loading complete"];
231+
auto bundle = [self bundleForProject:@"OtherProject"];
232+
FIRLoadBundleTask* task =
233+
[self.db loadBundle:[util::MakeNSString(bundle) dataUsingEncoding:NSUTF8StringEncoding]
234+
completion:^(FIRLoadBundleTaskProgress* progress, NSError* error) {
235+
result = progress;
236+
XCTAssertNotNil(error);
237+
[expectation fulfill];
238+
}];
239+
[task addObserver:^(FIRLoadBundleTaskProgress* progress) {
240+
[progresses addObject:progress];
241+
}];
242+
[self awaitExpectation:expectation];
243+
244+
XCTAssertEqual(2ul, progresses.count);
245+
[self verifyProgress:progresses[0] hasLoadedDocument:0];
246+
[self verifyErrorProgress:progresses[1]];
247+
XCTAssertEqualObjects(progresses[1], result);
248+
}
249+
250+
@end

0 commit comments

Comments
 (0)