aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Example/Core/Tests/FIROptionsTest.m2
-rw-r--r--Example/Database/Tests/Integration/FData.m6
-rw-r--r--Example/Database/Tests/Integration/FRealtime.m1
-rw-r--r--Example/Database/Tests/Unit/FSyncPointTests.m6
-rw-r--r--Example/Firebase.xcodeproj/project.pbxproj4
-rw-r--r--Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m2
-rw-r--r--Firebase/Core/FIRMutableDictionary.m14
-rw-r--r--Firebase/Core/FIRNetworkURLSession.m14
-rw-r--r--Firebase/Core/FIROptions.m8
-rw-r--r--Firebase/Database/Persistence/FLevelDBStorageEngine.m2
-rw-r--r--Firebase/Database/third_party/SocketRocket/FSRWebSocket.m34
-rw-r--r--Firebase/Messaging/Public/FIRMessaging.h5
-rw-r--r--Firebase/Storage/FIRStorageDeleteTask.m2
-rw-r--r--Firebase/Storage/FIRStorageDownloadTask.m6
-rw-r--r--Firebase/Storage/FIRStorageGetMetadataTask.m4
-rw-r--r--Firebase/Storage/FIRStorageMetadata.m5
-rw-r--r--Firebase/Storage/FIRStorageUpdateMetadataTask.m4
-rw-r--r--Firebase/Storage/FIRStorageUploadTask.m8
-rw-r--r--Firestore/CMakeLists.txt1
-rw-r--r--Firestore/Example/Firestore.xcodeproj/project.pbxproj49
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm29
-rw-r--r--Firestore/Example/Tests/Local/FSTQueryCacheTests.mm3
-rw-r--r--Firestore/Example/Tests/Util/FSTHelpers.mm4
-rw-r--r--Firestore/Protos/CMakeLists.txt45
-rw-r--r--Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj2
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/any.pb.c36
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/any.pb.h69
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/empty.pb.c34
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/empty.pb.h66
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c81
-rw-r--r--Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h147
-rw-r--r--Firestore/Protos/objc/firestore/local/Target.pbobjc.h4
-rw-r--r--Firestore/Protos/objc/firestore/local/Target.pbobjc.m11
-rw-r--r--Firestore/Protos/protos/firestore/local/target.proto3
-rw-r--r--Firestore/Protos/protos/google/protobuf/any.proto149
-rw-r--r--Firestore/Protos/protos/google/protobuf/empty.proto52
-rw-r--r--Firestore/Protos/protos/google/protobuf/wrappers.proto118
-rw-r--r--Firestore/Source/Local/FSTLevelDBMigrations.mm42
-rw-r--r--Firestore/Source/Local/FSTLevelDBQueryCache.mm54
-rw-r--r--Firestore/Source/Local/FSTLocalStore.mm2
-rw-r--r--Firestore/Source/Local/FSTMemoryQueryCache.mm14
-rw-r--r--Firestore/Source/Local/FSTQueryCache.h19
-rw-r--r--Firestore/Source/Remote/FSTStream.mm1
-rw-r--r--Firestore/core/CMakeLists.txt2
-rw-r--r--Firestore/core/include/firebase/firestore/document_reference.h375
-rw-r--r--Firestore/core/include/firebase/firestore/event_listener.h53
-rw-r--r--Firestore/core/include/firebase/firestore/firestore.h160
-rw-r--r--Firestore/core/src/firebase/firestore/auth/CMakeLists.txt52
-rw-r--r--Firestore/core/src/firebase/firestore/auth/credentials_provider.cc31
-rw-r--r--Firestore/core/src/firebase/firestore/auth/credentials_provider.h79
-rw-r--r--Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc42
-rw-r--r--Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h37
-rw-r--r--Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h112
-rw-r--r--Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm134
-rw-r--r--Firestore/core/src/firebase/firestore/auth/token.cc29
-rw-r--r--Firestore/core/src/firebase/firestore/auth/token.h68
-rw-r--r--Firestore/core/src/firebase/firestore/auth/user.cc39
-rw-r--r--Firestore/core/src/firebase/firestore/auth/user.h76
-rw-r--r--Firestore/core/src/firebase/firestore/model/CMakeLists.txt12
-rw-r--r--Firestore/core/src/firebase/firestore/model/base_path.h2
-rw-r--r--Firestore/core/src/firebase/firestore/model/document.cc50
-rw-r--r--Firestore/core/src/firebase/firestore/model/document.h72
-rw-r--r--Firestore/core/src/firebase/firestore/model/document_key.cc54
-rw-r--r--Firestore/core/src/firebase/firestore/model/document_key.h101
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_path.cc11
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_path.h5
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.cc41
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.h19
-rw-r--r--Firestore/core/src/firebase/firestore/model/maybe_document.cc38
-rw-r--r--Firestore/core/src/firebase/firestore/model/maybe_document.h100
-rw-r--r--Firestore/core/src/firebase/firestore/model/no_document.cc32
-rw-r--r--Firestore/core/src/firebase/firestore/model/no_document.h36
-rw-r--r--Firestore/core/src/firebase/firestore/model/resource_path.cc3
-rw-r--r--Firestore/core/src/firebase/firestore/model/resource_path.h7
-rw-r--r--Firestore/core/src/firebase/firestore/model/snapshot_version.cc34
-rw-r--r--Firestore/core/src/firebase/firestore/model/snapshot_version.h74
-rw-r--r--Firestore/core/src/firebase/firestore/remote/CMakeLists.txt2
-rw-r--r--Firestore/core/src/firebase/firestore/remote/serializer.cc88
-rw-r--r--Firestore/core/src/firebase/firestore/remote/serializer.h112
-rw-r--r--Firestore/core/src/firebase/firestore/util/firebase_assert.h11
-rw-r--r--Firestore/core/test/firebase/firestore/auth/CMakeLists.txt34
-rw-r--r--Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc53
-rw-r--r--Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc50
-rw-r--r--Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm98
-rw-r--r--Firestore/core/test/firebase/firestore/auth/token_test.cc33
-rw-r--r--Firestore/core/test/firebase/firestore/auth/user_test.cc54
-rw-r--r--Firestore/core/test/firebase/firestore/model/CMakeLists.txt7
-rw-r--r--Firestore/core/test/firebase/firestore/model/document_key_test.cc153
-rw-r--r--Firestore/core/test/firebase/firestore/model/document_test.cc76
-rw-r--r--Firestore/core/test/firebase/firestore/model/field_path_test.cc46
-rw-r--r--Firestore/core/test/firebase/firestore/model/field_value_test.cc53
-rw-r--r--Firestore/core/test/firebase/firestore/model/maybe_document_test.cc66
-rw-r--r--Firestore/core/test/firebase/firestore/model/no_document_test.cc51
-rw-r--r--Firestore/core/test/firebase/firestore/model/resource_path_test.cc14
-rw-r--r--Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc56
-rw-r--r--Firestore/core/test/firebase/firestore/remote/serializer_test.cc71
-rwxr-xr-xFirestore/test.sh23
-rw-r--r--README.md6
-rw-r--r--cmake/FindNanopb.cmake2
-rw-r--r--cmake/external/grpc.cmake10
-rw-r--r--cmake/external/nanopb.cmake9
102 files changed, 4174 insertions, 152 deletions
diff --git a/.travis.yml b/.travis.yml
index 2f8f497..042fab1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,8 @@ before_install:
- bundle exec pod install --project-directory=Example --repo-update
- bundle exec pod install --project-directory=Firestore/Example --no-repo-update
- brew install clang-format
+ - brew install cmake
+ - brew install go # Somehow the build for Abseil requires this.
- echo "$TRAVIS_COMMIT_RANGE"
- echo "$TRAVIS_PULL_REQUEST"
- |
@@ -37,6 +39,10 @@ script:
fi
- |
if [ $SKIP_FIRESTORE != 1 ]; then
+ ./scripts/lint.sh # Google C++ style compliance
+ fi
+ - |
+ if [ $SKIP_FIRESTORE != 1 ]; then
./Firestore/test.sh
fi
diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m
index 8170405..5d66ca9 100644
--- a/Example/Core/Tests/FIROptionsTest.m
+++ b/Example/Core/Tests/FIROptionsTest.m
@@ -128,7 +128,7 @@ extern NSString *const kFIRLibraryVersionID;
#pragma clang diagnostic pop
}
-- (void)testinitWithContentsOfFile {
+- (void)testInitWithContentsOfFile {
NSString *filePath =
[[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
diff --git a/Example/Database/Tests/Integration/FData.m b/Example/Database/Tests/Integration/FData.m
index aef15e1..d036f77 100644
--- a/Example/Database/Tests/Integration/FData.m
+++ b/Example/Database/Tests/Integration/FData.m
@@ -487,7 +487,7 @@
[[ref child:@"100003354884401"] setValue:@"alpha"];
__block BOOL ready = NO;
- [ref observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
+ [ref observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
id val = [snapshot value];
XCTAssertTrue([val isKindOfClass:[NSDictionary class]], @"Expected a dictionary.");
ready = YES;
@@ -678,6 +678,8 @@
[self waitUntil:^BOOL{
return setDone && calls == 1;
}];
+
+ [node removeAllObservers];
}
- (void) testHasChildrenWorksCorrectly {
@@ -878,6 +880,7 @@
[self waitUntil:^BOOL{
return calls == 1;
}];
+ [reader removeAllObservers];
}
- (void) testSetPriorityOnNonexistentNodeFails {
@@ -2208,6 +2211,7 @@
}];
WAIT_FOR(done);
+ [deleter removeAllObservers];
}
- (void) testParentDeleteShadowsChildListenersWithNonDefaultQuery {
diff --git a/Example/Database/Tests/Integration/FRealtime.m b/Example/Database/Tests/Integration/FRealtime.m
index 5c7d186..5acda07 100644
--- a/Example/Database/Tests/Integration/FRealtime.m
+++ b/Example/Database/Tests/Integration/FRealtime.m
@@ -481,6 +481,7 @@
WAIT_FOR(count == 2);
// cleanup
+ [reader removeAllObservers];
[FRepoManager disposeRepos:writerCfg];
}
diff --git a/Example/Database/Tests/Unit/FSyncPointTests.m b/Example/Database/Tests/Unit/FSyncPointTests.m
index 797a5aa..de4680f 100644
--- a/Example/Database/Tests/Unit/FSyncPointTests.m
+++ b/Example/Database/Tests/Unit/FSyncPointTests.m
@@ -86,15 +86,15 @@ typedef NSDictionary* (^fbt_nsdictionary_void)(void);
}
- (void) fireEvent:(id<FEvent>)event queue:(dispatch_queue_t)queue {
- [NSException raise:@"NotImplementedError" format:@"Method not implemneted."];
+ [NSException raise:@"NotImplementedError" format:@"Method not implemented."];
}
- (FCancelEvent *) createCancelEventFromError:(NSError *)error path:(FPath *)path {
- [NSException raise:@"NotImplementedError" format:@"Method not implemneted."];
+ [NSException raise:@"NotImplementedError" format:@"Method not implemented."];
return nil;
}
- (FIRDatabaseHandle) handle {
- [NSException raise:@"NotImplementedError" format:@"Method not implemneted."];
+ [NSException raise:@"NotImplementedError" format:@"Method not implemented."];
return 0;
}
@end
diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj
index 9431ae0..c8400d6 100644
--- a/Example/Firebase.xcodeproj/project.pbxproj
+++ b/Example/Firebase.xcodeproj/project.pbxproj
@@ -8111,6 +8111,7 @@
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/Database/App/iOS/Database-Info.plist";
@@ -8129,6 +8130,7 @@
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
@@ -8650,6 +8652,7 @@
baseConfigurationReference = 06F3D16439F061DE9973902D /* Pods-Storage_Example_iOS.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -8671,6 +8674,7 @@
baseConfigurationReference = D440FB786B320FCF836B508F /* Pods-Storage_Example_iOS.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
diff --git a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m
index 703c2c6..8e826c9 100644
--- a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m
+++ b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m
@@ -33,7 +33,7 @@ NSTimeInterval kFIRStorageIntegrationTestTimeout = 30;
* A sample configuration may look like:
*
* service firebase.storage {
- * match /b/{YOUR_PROJECT_ID}.appspot.com/o {
+ * match /b/{bucket}/o {
* ...
* match /ios {
* match /public/{allPaths=**} {
diff --git a/Firebase/Core/FIRMutableDictionary.m b/Firebase/Core/FIRMutableDictionary.m
index 1d6ef3a..31941bc 100644
--- a/Firebase/Core/FIRMutableDictionary.m
+++ b/Firebase/Core/FIRMutableDictionary.m
@@ -37,7 +37,7 @@
- (NSString *)description {
__block NSString *description;
dispatch_sync(_queue, ^{
- description = _objects.description;
+ description = self->_objects.description;
});
return description;
}
@@ -45,33 +45,33 @@
- (id)objectForKey:(id)key {
__block id object;
dispatch_sync(_queue, ^{
- object = _objects[key];
+ object = self->_objects[key];
});
return object;
}
- (void)setObject:(id)object forKey:(id<NSCopying>)key {
dispatch_async(_queue, ^{
- _objects[key] = object;
+ self->_objects[key] = object;
});
}
- (void)removeObjectForKey:(id)key {
dispatch_async(_queue, ^{
- [_objects removeObjectForKey:key];
+ [self->_objects removeObjectForKey:key];
});
}
- (void)removeAllObjects {
dispatch_async(_queue, ^{
- [_objects removeAllObjects];
+ [self->_objects removeAllObjects];
});
}
- (NSUInteger)count {
__block NSUInteger count;
dispatch_sync(_queue, ^{
- count = _objects.count;
+ count = self->_objects.count;
});
return count;
}
@@ -89,7 +89,7 @@
- (NSDictionary *)dictionary {
__block NSDictionary *dictionary;
dispatch_sync(_queue, ^{
- dictionary = [_objects copy];
+ dictionary = [self->_objects copy];
});
return dictionary;
}
diff --git a/Firebase/Core/FIRNetworkURLSession.m b/Firebase/Core/FIRNetworkURLSession.m
index c3da674..6b5ce3a 100644
--- a/Firebase/Core/FIRNetworkURLSession.m
+++ b/Firebase/Core/FIRNetworkURLSession.m
@@ -314,11 +314,11 @@
if (allow) {
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
} else {
- [_loggerDelegate
+ [self->_loggerDelegate
firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
messageCode:kFIRNetworkMessageCodeURLSession007
message:@"Cancelling authentication challenge for host. Host"
- context:_request.URL];
+ context:self->_request.URL];
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
};
@@ -344,10 +344,10 @@
}
if (trustError != errSecSuccess) {
- [_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
- messageCode:kFIRNetworkMessageCodeURLSession008
- message:@"Cannot evaluate server trust. Error, host"
- contexts:@[ @(trustError), _request.URL ]];
+ [self->_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
+ messageCode:kFIRNetworkMessageCodeURLSession008
+ message:@"Cannot evaluate server trust. Error, host"
+ contexts:@[ @(trustError), self->_request.URL ]];
shouldAllow = NO;
} else {
// Having a trust level "unspecified" by the user is the usual result, described at
@@ -651,7 +651,7 @@
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
- handler(response, data, _sessionID, error);
+ handler(response, data, self->_sessionID, error);
});
}
}
diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m
index 6d52173..75f2ed2 100644
--- a/Firebase/Core/FIROptions.m
+++ b/Firebase/Core/FIROptions.m
@@ -367,7 +367,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
}
tempAnalyticsOptions[key] = value;
}
- _analyticsOptionsDictionary = tempAnalyticsOptions;
+ self->_analyticsOptionsDictionary = tempAnalyticsOptions;
});
return _analyticsOptionsDictionary;
}
@@ -386,7 +386,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
- if (!value) {
+ if (value == nil) {
return YES; // Enable Measurement by default when the key is not in the dictionary.
}
return [value boolValue];
@@ -397,7 +397,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
- if (!value) {
+ if (value == nil) {
return self.isMeasurementEnabled; // Fall back to older plist flag.
}
return [value boolValue];
@@ -405,7 +405,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
- (BOOL)isAnalyticsCollectionDeactivated {
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
- if (!value) {
+ if (value == nil) {
return NO; // Analytics Collection is not deactivated when the key is not in the dictionary.
}
return [value boolValue];
diff --git a/Firebase/Database/Persistence/FLevelDBStorageEngine.m b/Firebase/Database/Persistence/FLevelDBStorageEngine.m
index 7de9ebf..e49d6bc 100644
--- a/Firebase/Database/Persistence/FLevelDBStorageEngine.m
+++ b/Firebase/Database/Persistence/FLevelDBStorageEngine.m
@@ -687,7 +687,7 @@ static NSString* trackedQueryKeysKey(NSUInteger trackedQueryId, NSString *key) {
NSString *doubleString = [value stringValue];
return [NSNumber numberWithDouble:[doubleString doubleValue]];
} else {
- return [NSNumber numberWithLong:[value longValue]];
+ return [NSNumber numberWithLongLong:[value longLongValue]];
}
}
}
diff --git a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m
index c80dbb0..9b3dad0 100644
--- a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m
+++ b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m
@@ -515,10 +515,10 @@ static __strong NSData *CRLFCRLF;
}
[self _readUntilHeaderCompleteWithCallback:^(FSRWebSocket *self, NSData *data) {
- CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
+ CFHTTPMessageAppendBytes(self->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
- if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
- SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
+ if (CFHTTPMessageIsHeaderComplete(self->_receivedHTTPHeaders)) {
+ SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_receivedHTTPHeaders)));
[self _HTTPHeadersDidFinish];
} else {
[self _readHTTPHeader];
@@ -696,7 +696,7 @@ static __strong NSData *CRLFCRLF;
// Need to shunt this on the _callbackQueue first to see if they received any messages
[self _performDelegateBlock:^{
[self closeWithCode:SRStatusCodeProtocolError reason:message];
- dispatch_async(_workQueue, ^{
+ dispatch_async(self->_workQueue, ^{
[self _disconnect];
});
}];
@@ -706,7 +706,7 @@ static __strong NSData *CRLFCRLF;
{
dispatch_async(_workQueue, ^{
if (self.readyState != SR_CLOSED) {
- _failed = YES;
+ self->_failed = YES;
[self _performDelegateBlock:^{
if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) {
[self.delegate webSocket:self didFailWithError:error];
@@ -756,7 +756,7 @@ static __strong NSData *CRLFCRLF;
{
// Need to pingpong this off _callbackQueue first to make sure messages happen in order
[self _performDelegateBlock:^{
- dispatch_async(_workQueue, ^{
+ dispatch_async(self->_workQueue, ^{
[self _sendFrameWithOpcode:SROpCodePong data:pingData];
});
}];
@@ -1031,7 +1031,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
[self _closeWithProtocolError:@"Client must receive unmasked data"];
}
- size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
+ size_t extra_bytes_needed = header.masked ? sizeof(self->_currentReadMaskKey) : 0;
if (header.payload_length == 126) {
extra_bytes_needed += sizeof(uint16_t);
@@ -1062,7 +1062,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
if (header.masked) {
- assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
+ assert(mapped_size >= sizeof(self->_currentReadMaskOffset) + offset);
memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
}
@@ -1075,12 +1075,12 @@ static const uint8_t SRPayloadLenMask = 0x7F;
- (void)_readFrameNew;
{
dispatch_async(_workQueue, ^{
- [_currentFrameData setLength:0];
+ [self->_currentFrameData setLength:0];
- _currentFrameOpcode = 0;
- _currentFrameCount = 0;
- _readOpCount = 0;
- _currentStringScanPosition = 0;
+ self->_currentFrameOpcode = 0;
+ self->_currentFrameCount = 0;
+ self->_readOpCount = 0;
+ self->_currentStringScanPosition = 0;
[self _readFrameContinue];
});
@@ -1123,7 +1123,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
if (!_failed) {
[self _performDelegateBlock:^{
if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
- [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES];
+ [self.delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES];
}
}];
}
@@ -1184,7 +1184,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
// Cleanup selfRetain in the same GCD queue as usual
dispatch_async(_workQueue, ^{
- _selfRetain = nil;
+ self->_selfRetain = nil;
});
}
@@ -1525,8 +1525,8 @@ static const size_t SRFrameHeaderOverhead = 32;
[self _scheduleCleanup];
}
- if (!_sentClose && !_failed) {
- _sentClose = YES;
+ if (!self->_sentClose && !self->_failed) {
+ self->_sentClose = YES;
// If we get closed in this state it's probably not clean because we should be sending this when we send messages
[self _performDelegateBlock:^{
if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h
index 33dd596..31e8625 100644
--- a/Firebase/Messaging/Public/FIRMessaging.h
+++ b/Firebase/Messaging/Public/FIRMessaging.h
@@ -356,6 +356,11 @@ NS_SWIFT_NAME(Messaging)
* Is Firebase Messaging token auto generation enabled? If this flag is disabled,
* Firebase Messaging will not generate token automatically for message delivery.
*
+ * If this flag is disabled, Firebase Messaging does not generate new tokens automatically for
+ * message delivery. If this flag is enabled, FCM generates a registration token on application
+ * start when there is no existing valid token. FCM also generates a new token when an existing
+ * token is deleted.
+ *
* This setting is persisted, and is applied on future
* invocations of your application. Once explicitly set, it overrides any
* settings in your Info.plist.
diff --git a/Firebase/Storage/FIRStorageDeleteTask.m b/Firebase/Storage/FIRStorageDeleteTask.m
index 738d8a5..b41f06e 100644
--- a/Firebase/Storage/FIRStorageDeleteTask.m
+++ b/Firebase/Storage/FIRStorageDeleteTask.m
@@ -60,7 +60,7 @@
if (callback) {
callback(self.error);
}
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
};
#pragma clang diangostic pop
diff --git a/Firebase/Storage/FIRStorageDownloadTask.m b/Firebase/Storage/FIRStorageDownloadTask.m
index c410f05..91da4b7 100644
--- a/Firebase/Storage/FIRStorageDownloadTask.m
+++ b/Firebase/Storage/FIRStorageDownloadTask.m
@@ -116,7 +116,7 @@
self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference];
[self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot];
[self removeAllObservers];
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
return;
}
@@ -124,12 +124,12 @@
self.state = FIRStorageTaskStateSuccess;
if (data) {
- _downloadData = data;
+ self->_downloadData = data;
}
[self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot];
[self removeAllObservers];
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
};
#pragma clang diagnostic pop
diff --git a/Firebase/Storage/FIRStorageGetMetadataTask.m b/Firebase/Storage/FIRStorageGetMetadataTask.m
index 78d8a16..2623652 100644
--- a/Firebase/Storage/FIRStorageGetMetadataTask.m
+++ b/Firebase/Storage/FIRStorageGetMetadataTask.m
@@ -65,7 +65,7 @@
if (callback) {
callback(nil, self.error);
}
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
return;
}
@@ -90,7 +90,7 @@
callback(nil, self.error);
}
}
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
};
#pragma clang diagnostic pop
diff --git a/Firebase/Storage/FIRStorageMetadata.m b/Firebase/Storage/FIRStorageMetadata.m
index 34ac86c..ab25076 100644
--- a/Firebase/Storage/FIRStorageMetadata.m
+++ b/Firebase/Storage/FIRStorageMetadata.m
@@ -60,8 +60,9 @@
NSURLComponents *components = [[NSURLComponents alloc] init];
components.scheme = kFIRStorageScheme;
components.host = kFIRStorageHost;
- NSString *path = [FIRStorageUtils GCSEscapedString:_path];
- NSString *fullPath = [NSString stringWithFormat:kFIRStorageFullPathFormat, _bucket, path];
+ NSString *path = [FIRStorageUtils GCSEscapedString:self->_path];
+ NSString *fullPath =
+ [NSString stringWithFormat:kFIRStorageFullPathFormat, self->_bucket, path];
components.percentEncodedPath = fullPath;
components.query = [NSString stringWithFormat:@"alt=media&token=%@", token];
diff --git a/Firebase/Storage/FIRStorageUpdateMetadataTask.m b/Firebase/Storage/FIRStorageUpdateMetadataTask.m
index cf1bf93..fa5955a 100644
--- a/Firebase/Storage/FIRStorageUpdateMetadataTask.m
+++ b/Firebase/Storage/FIRStorageUpdateMetadataTask.m
@@ -71,7 +71,7 @@
if (callback) {
callback(nil, self.error);
}
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
return;
}
@@ -96,7 +96,7 @@
callback(nil, self.error);
}
}
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
};
#pragma clang diagnostic pop
diff --git a/Firebase/Storage/FIRStorageUploadTask.m b/Firebase/Storage/FIRStorageUploadTask.m
index 0df0bf4..f84c2c7 100644
--- a/Firebase/Storage/FIRStorageUploadTask.m
+++ b/Firebase/Storage/FIRStorageUploadTask.m
@@ -117,7 +117,7 @@
weakSelf.state = FIRStorageTaskStateProgress;
weakSelf.progress.completedUnitCount = totalBytesSent;
weakSelf.progress.totalUnitCount = totalBytesExpectedToSend;
- weakSelf.metadata = _uploadMetadata;
+ weakSelf.metadata = self->_uploadMetadata;
[weakSelf fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:weakSelf.snapshot];
weakSelf.state = FIRStorageTaskStateRunning;
}];
@@ -137,10 +137,10 @@
if (error) {
self.state = FIRStorageTaskStateFailed;
self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference];
- self.metadata = _uploadMetadata;
+ self.metadata = self->_uploadMetadata;
[self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot];
[self removeAllObservers];
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
return;
}
@@ -166,7 +166,7 @@
[self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot];
[self removeAllObservers];
- _fetcherCompletion = nil;
+ self->_fetcherCompletion = nil;
};
#pragma clang diagnostic pop
diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt
index 25f27fa..28ac08f 100644
--- a/Firestore/CMakeLists.txt
+++ b/Firestore/CMakeLists.txt
@@ -68,3 +68,4 @@ include_directories(${FIREBASE_SOURCE_DIR})
include_directories(${FIREBASE_SOURCE_DIR}/Firestore/Protos/nanopb)
add_subdirectory(core)
+add_subdirectory(Protos)
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
index 8d99eb6..3b91c76 100644
--- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj
+++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
@@ -140,10 +140,20 @@
AB380D02201BC69F00D97691 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; };
AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D03201BC6E400D97691 /* ordered_code_test.cc */; };
AB38D93020236E21000A432D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; };
+ AB6B908420322E4D00CC290A /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; };
+ AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908520322E6D00CC290A /* maybe_document_test.cc */; };
+ AB6B908820322E8800CC290A /* no_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908720322E8800CC290A /* no_document_test.cc */; };
AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; };
+ ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; };
+ ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; };
+ ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93620239689000A432D /* empty_credentials_provider_test.cc */; };
+ ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93220239654000A432D /* user_test.cc */; };
+ ABC1D7E12023A40C00BA84F0 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */; };
+ ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; };
ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; };
ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; };
AFE6114F0D4DAECBA7B7C089 /* Pods_Firestore_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */; };
+ B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; };
B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; };
B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; };
C4E749275AD0FBDF9F4716A8 /* Pods_SwiftBuildTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */; };
@@ -336,10 +346,20 @@
AB380D01201BC69F00D97691 /* bits_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bits_test.cc; path = ../../core/test/firebase/firestore/util/bits_test.cc; sourceTree = "<group>"; };
AB380D03201BC6E400D97691 /* ordered_code_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ordered_code_test.cc; path = ../../core/test/firebase/firestore/util/ordered_code_test.cc; sourceTree = "<group>"; };
AB38D92E20235D22000A432D /* database_info_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_info_test.cc; sourceTree = "<group>"; };
+ AB38D93220239654000A432D /* user_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_test.cc; sourceTree = "<group>"; };
+ AB38D9342023966E000A432D /* credentials_provider_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = credentials_provider_test.cc; sourceTree = "<group>"; };
+ AB38D93620239689000A432D /* empty_credentials_provider_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = empty_credentials_provider_test.cc; sourceTree = "<group>"; };
+ AB6B908320322E4D00CC290A /* document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = document_test.cc; sourceTree = "<group>"; };
+ AB6B908520322E6D00CC290A /* maybe_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = maybe_document_test.cc; sourceTree = "<group>"; };
+ AB6B908720322E8800CC290A /* no_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = no_document_test.cc; sourceTree = "<group>"; };
AB71064B201FA60300344F18 /* database_id_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_id_test.cc; sourceTree = "<group>"; };
AB7BAB332012B519001E0872 /* geo_point_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = geo_point_test.cc; path = ../../core/test/firebase/firestore/geo_point_test.cc; sourceTree = "<group>"; };
+ ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = snapshot_version_test.cc; sourceTree = "<group>"; };
+ ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = token_test.cc; sourceTree = "<group>"; };
+ ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = firebase_credentials_provider_test.mm; sourceTree = "<group>"; };
ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = "<group>"; };
B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ B6152AD5202A5385000E5744 /* document_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = document_key_test.cc; sourceTree = "<group>"; };
B686F2AD2023DDB20028D6BE /* field_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = field_path_test.cc; sourceTree = "<group>"; };
B686F2B02024FFD70028D6BE /* resource_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_path_test.cc; sourceTree = "<group>"; };
CE00BABB5A3AAB44A4C209E2 /* Pods-Firestore_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -430,6 +450,7 @@
54764FAC1FAA0C390085E60A /* GoogleTests */ = {
isa = PBXGroup;
children = (
+ AB38D9312023962A000A432D /* auth */,
AB380CF7201937B800D97691 /* core */,
54EB764B202277970088B8F3 /* immutable */,
AB356EF5200E9D1A0089B766 /* model */,
@@ -569,10 +590,15 @@
AB356EF5200E9D1A0089B766 /* model */ = {
isa = PBXGroup;
children = (
+ B6152AD5202A5385000E5744 /* document_key_test.cc */,
+ AB6B908320322E4D00CC290A /* document_test.cc */,
B686F2B02024FFD70028D6BE /* resource_path_test.cc */,
B686F2AD2023DDB20028D6BE /* field_path_test.cc */,
AB71064B201FA60300344F18 /* database_id_test.cc */,
AB356EF6200EA5EB0089B766 /* field_value_test.cc */,
+ AB6B908520322E6D00CC290A /* maybe_document_test.cc */,
+ AB6B908720322E8800CC290A /* no_document_test.cc */,
+ ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */,
ABF6506B201131F8005F2C74 /* timestamp_test.cc */,
);
name = model;
@@ -589,6 +615,19 @@
path = ../../core/test/firebase/firestore/core;
sourceTree = "<group>";
};
+ AB38D9312023962A000A432D /* auth */ = {
+ isa = PBXGroup;
+ children = (
+ AB38D9342023966E000A432D /* credentials_provider_test.cc */,
+ AB38D93620239689000A432D /* empty_credentials_provider_test.cc */,
+ ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */,
+ ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */,
+ AB38D93220239654000A432D /* user_test.cc */,
+ );
+ name = auth;
+ path = ../../core/test/firebase/firestore/auth;
+ sourceTree = "<group>";
+ };
DE0761E51F2FE611003233AF /* SwiftBuildTest */ = {
isa = PBXGroup;
children = (
@@ -1252,7 +1291,9 @@
5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */,
AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */,
5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */,
+ ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */,
5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */,
+ ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */,
DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */,
B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */,
5492E03120213FFC00B64F25 /* FSTLevelDBSpecTests.mm in Sources */,
@@ -1272,11 +1313,13 @@
5492E0AC2021552D00B64F25 /* FSTMutationQueueTests.mm in Sources */,
5492E056202154AB00B64F25 /* FIRFieldPathTests.mm in Sources */,
5492E03220213FFC00B64F25 /* FSTMockDatastore.mm in Sources */,
+ ABC1D7E12023A40C00BA84F0 /* token_test.cc in Sources */,
AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */,
AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */,
5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */,
5492E051202154AA00B64F25 /* FIRQueryTests.mm in Sources */,
5492E054202154AB00B64F25 /* FIRFieldValueTests.mm in Sources */,
+ AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */,
5492E09F2021552D00B64F25 /* FSTLevelDBMigrationsTests.mm in Sources */,
5492E053202154AB00B64F25 /* FIRDocumentReferenceTests.mm in Sources */,
5492E09D2021552D00B64F25 /* FSTLocalStoreTests.mm in Sources */,
@@ -1284,22 +1327,28 @@
5492E0A72021552D00B64F25 /* FSTLevelDBKeyTests.mm in Sources */,
5492E0A22021552D00B64F25 /* FSTQueryCacheTests.mm in Sources */,
5492E0A52021552D00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */,
+ AB6B908820322E8800CC290A /* no_document_test.cc in Sources */,
5492E0BD2021555100B64F25 /* FSTDocumentTests.mm in Sources */,
5492E0B92021555100B64F25 /* FSTDocumentKeyTests.mm in Sources */,
DE2EF0871F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m in Sources */,
5492E0C82021557E00B64F25 /* FSTDatastoreTests.mm in Sources */,
5492E065202154B900B64F25 /* FSTViewTests.mm in Sources */,
5492E03C2021401F00B64F25 /* XCTestCase+Await.mm in Sources */,
+ B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */,
54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */,
AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */,
5492E03F2021401F00B64F25 /* FSTHelpers.mm in Sources */,
5492E068202154B900B64F25 /* FSTQueryTests.mm in Sources */,
5492E0AB2021552D00B64F25 /* StringViewTests.mm in Sources */,
5492E0C92021557E00B64F25 /* FSTRemoteEventTests.mm in Sources */,
+ ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */,
+ AB6B908420322E4D00CC290A /* document_test.cc in Sources */,
ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */,
5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */,
+ ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */,
5492E059202154AB00B64F25 /* FIRQuerySnapshotTests.mm in Sources */,
5492E050202154AA00B64F25 /* FIRCollectionReferenceTests.mm in Sources */,
+ ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */,
5492E0A02021552D00B64F25 /* FSTLevelDBMutationQueueTests.mm in Sources */,
54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */,
5492E03420213FFC00B64F25 /* FSTMemorySpecTests.mm in Sources */,
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm
index 8ef0e94..3559d5d 100644
--- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm
+++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm
@@ -18,13 +18,18 @@
#include <leveldb/db.h>
#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
+#import "Firestore/Source/Local/FSTLevelDBKey.h"
#import "Firestore/Source/Local/FSTLevelDBMigrations.h"
#import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
+#import "Firestore/Source/Local/FSTWriteGroup.h"
+
+#include "Firestore/core/src/firebase/firestore/util/ordered_code.h"
#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h"
NS_ASSUME_NONNULL_BEGIN
+using firebase::firestore::util::OrderedCode;
using leveldb::DB;
using leveldb::Options;
using leveldb::Status;
@@ -70,6 +75,30 @@ using leveldb::Status;
XCTAssertGreaterThan(actual, 0, @"Expected to migrate to a schema version > 0");
}
+- (void)testCountsQueries {
+ NSUInteger expected = 50;
+ FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"Setup"];
+ for (int i = 0; i < expected; i++) {
+ std::string key = [FSTLevelDBTargetKey keyWithTargetID:i];
+ [group setData:"dummy" forKey:key];
+ }
+ // Add a dummy entry after the targets to make sure the iteration is correctly bounded.
+ // Use a table that would sort logically right after that table 'target'.
+ std::string dummyKey;
+ // Magic number that indicates a table name follows. Needed to mimic the prefix to the target
+ // table.
+ OrderedCode::WriteSignedNumIncreasing(&dummyKey, 5);
+ OrderedCode::WriteString(&dummyKey, "targetA");
+ [group setData:"dummy" forKey:dummyKey];
+
+ Status status = [group writeToDB:_db];
+ XCTAssertTrue(status.ok(), @"Failed to write targets");
+
+ [FSTLevelDBMigrations runMigrationsOnDB:_db];
+ FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db];
+ XCTAssertEqual(expected, metadata.targetCount, @"Failed to count all of the targets we added");
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm
index 0c6a2a4..6ab655a 100644
--- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm
+++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm
@@ -89,6 +89,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *data2 = [self queryDataWithQuery:q2];
[self addQueryData:data2];
+ XCTAssertEqual(2, [self.queryCache count]);
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1);
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2);
@@ -96,10 +97,12 @@ NS_ASSUME_NONNULL_BEGIN
[self removeQueryData:data1];
XCTAssertNil([self.queryCache queryDataForQuery:q1]);
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2);
+ XCTAssertEqual(1, [self.queryCache count]);
[self removeQueryData:data2];
XCTAssertNil([self.queryCache queryDataForQuery:q1]);
XCTAssertNil([self.queryCache queryDataForQuery:q2]);
+ XCTAssertEqual(0, [self.queryCache count]);
}
- (void)testSetQueryToNewValue {
diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm
index 406d4dd..452e9a6 100644
--- a/Firestore/Example/Tests/Util/FSTHelpers.mm
+++ b/Firestore/Example/Tests/Util/FSTHelpers.mm
@@ -17,7 +17,7 @@
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
#include <inttypes.h>
-#include <vector>
+#include <list>
#import <FirebaseFirestore/FIRFieldPath.h>
#import <FirebaseFirestore/FIRGeoPoint.h>
@@ -174,7 +174,7 @@ FSTResourcePath *FSTTestPath(NSString *path) {
FSTDocumentKeyReference *FSTTestRef(NSString *projectID, NSString *database, NSString *path) {
// This owns the DatabaseIds since we do not have FirestoreClient instance to own them.
- static std::vector<DatabaseId> database_ids;
+ static std::list<DatabaseId> database_ids;
database_ids.emplace_back(util::MakeStringView(projectID), util::MakeStringView(database));
return [[FSTDocumentKeyReference alloc] initWithKey:FSTTestDocKey(path)
databaseID:&database_ids.back()];
diff --git a/Firestore/Protos/CMakeLists.txt b/Firestore/Protos/CMakeLists.txt
new file mode 100644
index 0000000..f20f702
--- /dev/null
+++ b/Firestore/Protos/CMakeLists.txt
@@ -0,0 +1,45 @@
+cc_library(
+ firebase_firestore_protos_nanopb
+ SOURCES
+ nanopb/firestore/local/maybe_document.pb.c
+ nanopb/firestore/local/maybe_document.pb.h
+ nanopb/firestore/local/mutation.pb.c
+ nanopb/firestore/local/mutation.pb.h
+ nanopb/firestore/local/target.pb.c
+ nanopb/firestore/local/target.pb.h
+ nanopb/google/api/annotations.pb.c
+ nanopb/google/api/annotations.pb.h
+ nanopb/google/api/http.pb.c
+ nanopb/google/api/http.pb.h
+ nanopb/google/firestore/v1beta1/common.pb.c
+ nanopb/google/firestore/v1beta1/common.pb.h
+ nanopb/google/firestore/v1beta1/document.pb.c
+ nanopb/google/firestore/v1beta1/document.pb.h
+ nanopb/google/firestore/v1beta1/firestore.pb.c
+ nanopb/google/firestore/v1beta1/firestore.pb.h
+ nanopb/google/firestore/v1beta1/query.pb.c
+ nanopb/google/firestore/v1beta1/query.pb.h
+ nanopb/google/firestore/v1beta1/write.pb.c
+ nanopb/google/firestore/v1beta1/write.pb.h
+ nanopb/google/protobuf/any.pb.c
+ nanopb/google/protobuf/any.pb.h
+ nanopb/google/protobuf/empty.pb.c
+ nanopb/google/protobuf/empty.pb.h
+ nanopb/google/protobuf/struct.pb.c
+ nanopb/google/protobuf/struct.pb.h
+ nanopb/google/protobuf/timestamp.pb.c
+ nanopb/google/protobuf/timestamp.pb.h
+ nanopb/google/protobuf/wrappers.pb.c
+ nanopb/google/protobuf/wrappers.pb.h
+ nanopb/google/rpc/status.pb.c
+ nanopb/google/rpc/status.pb.h
+ nanopb/google/type/latlng.pb.c
+ nanopb/google/type/latlng.pb.h
+ DEPENDS
+ nanopb
+)
+
+target_compile_definitions(
+ firebase_firestore_protos_nanopb PUBLIC
+ -DPB_FIELD_16BIT
+)
diff --git a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj
index 51a61b8..2efcb21 100644
--- a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj
+++ b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj
@@ -201,7 +201,7 @@
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-FrameworkMaker_iOS/Pods-FrameworkMaker_iOS-resources.sh",
- "$PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle",
+ "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.c b/Firestore/Protos/nanopb/google/protobuf/any.pb.c
new file mode 100644
index 0000000..b28d0ba
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#include "any.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_protobuf_Any_fields[3] = {
+ PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_Any, type_url, type_url, 0),
+ PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_protobuf_Any, value, type_url, 0),
+ PB_LAST_FIELD
+};
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.h b/Firestore/Protos/nanopb/google/protobuf/any.pb.h
new file mode 100644
index 0000000..10a722e
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#ifndef PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED
+#define PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED
+#include <pb.h>
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _google_protobuf_Any {
+ pb_callback_t type_url;
+ pb_callback_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_Any) */
+} google_protobuf_Any;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_protobuf_Any_init_default {{{NULL}, NULL}, {{NULL}, NULL}}
+#define google_protobuf_Any_init_zero {{{NULL}, NULL}, {{NULL}, NULL}}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define google_protobuf_Any_type_url_tag 1
+#define google_protobuf_Any_value_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_protobuf_Any_fields[3];
+
+/* Maximum encoded size of messages (where known) */
+/* google_protobuf_Any_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define ANY_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.c b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c
new file mode 100644
index 0000000..050af9c
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#include "empty.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_protobuf_Empty_fields[1] = {
+ PB_LAST_FIELD
+};
+
+
+/* @@protoc_insertion_point(eof) */
diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.h b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h
new file mode 100644
index 0000000..466e1fd
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#ifndef PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED
+#define PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED
+#include <pb.h>
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _google_protobuf_Empty {
+ char dummy_field;
+/* @@protoc_insertion_point(struct:google_protobuf_Empty) */
+} google_protobuf_Empty;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_protobuf_Empty_init_default {0}
+#define google_protobuf_Empty_init_zero {0}
+
+/* Field tags (for use in manual encoding/decoding) */
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_protobuf_Empty_fields[1];
+
+/* Maximum encoded size of messages (where known) */
+#define google_protobuf_Empty_size 0
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define EMPTY_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c
new file mode 100644
index 0000000..41ab3c6
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#include "wrappers.pb.h"
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t google_protobuf_DoubleValue_fields[2] = {
+ PB_FIELD( 1, DOUBLE , SINGULAR, STATIC , FIRST, google_protobuf_DoubleValue, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_FloatValue_fields[2] = {
+ PB_FIELD( 1, FLOAT , SINGULAR, STATIC , FIRST, google_protobuf_FloatValue, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_Int64Value_fields[2] = {
+ PB_FIELD( 1, INT64 , SINGULAR, STATIC , FIRST, google_protobuf_Int64Value, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_UInt64Value_fields[2] = {
+ PB_FIELD( 1, UINT64 , SINGULAR, STATIC , FIRST, google_protobuf_UInt64Value, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_Int32Value_fields[2] = {
+ PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_protobuf_Int32Value, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_UInt32Value_fields[2] = {
+ PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, google_protobuf_UInt32Value, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_BoolValue_fields[2] = {
+ PB_FIELD( 1, BOOL , SINGULAR, STATIC , FIRST, google_protobuf_BoolValue, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_StringValue_fields[2] = {
+ PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_StringValue, value, value, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t google_protobuf_BytesValue_fields[2] = {
+ PB_FIELD( 1, BYTES , SINGULAR, CALLBACK, FIRST, google_protobuf_BytesValue, value, value, 0),
+ PB_LAST_FIELD
+};
+
+
+/* On some platforms (such as AVR), double is really float.
+ * These are not directly supported by nanopb, but see example_avr_double.
+ * To get rid of this error, remove any double fields from your .proto.
+ */
+PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
+
+/* @@protoc_insertion_point(eof) */
diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h
new file mode 100644
index 0000000..0e98785
--- /dev/null
+++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */
+
+#ifndef PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED
+#define PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED
+#include <pb.h>
+
+/* @@protoc_insertion_point(includes) */
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Struct definitions */
+typedef struct _google_protobuf_BytesValue {
+ pb_callback_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_BytesValue) */
+} google_protobuf_BytesValue;
+
+typedef struct _google_protobuf_StringValue {
+ pb_callback_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_StringValue) */
+} google_protobuf_StringValue;
+
+typedef struct _google_protobuf_BoolValue {
+ bool value;
+/* @@protoc_insertion_point(struct:google_protobuf_BoolValue) */
+} google_protobuf_BoolValue;
+
+typedef struct _google_protobuf_DoubleValue {
+ double value;
+/* @@protoc_insertion_point(struct:google_protobuf_DoubleValue) */
+} google_protobuf_DoubleValue;
+
+typedef struct _google_protobuf_FloatValue {
+ float value;
+/* @@protoc_insertion_point(struct:google_protobuf_FloatValue) */
+} google_protobuf_FloatValue;
+
+typedef struct _google_protobuf_Int32Value {
+ int32_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_Int32Value) */
+} google_protobuf_Int32Value;
+
+typedef struct _google_protobuf_Int64Value {
+ int64_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_Int64Value) */
+} google_protobuf_Int64Value;
+
+typedef struct _google_protobuf_UInt32Value {
+ uint32_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_UInt32Value) */
+} google_protobuf_UInt32Value;
+
+typedef struct _google_protobuf_UInt64Value {
+ uint64_t value;
+/* @@protoc_insertion_point(struct:google_protobuf_UInt64Value) */
+} google_protobuf_UInt64Value;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define google_protobuf_DoubleValue_init_default {0}
+#define google_protobuf_FloatValue_init_default {0}
+#define google_protobuf_Int64Value_init_default {0}
+#define google_protobuf_UInt64Value_init_default {0}
+#define google_protobuf_Int32Value_init_default {0}
+#define google_protobuf_UInt32Value_init_default {0}
+#define google_protobuf_BoolValue_init_default {0}
+#define google_protobuf_StringValue_init_default {{{NULL}, NULL}}
+#define google_protobuf_BytesValue_init_default {{{NULL}, NULL}}
+#define google_protobuf_DoubleValue_init_zero {0}
+#define google_protobuf_FloatValue_init_zero {0}
+#define google_protobuf_Int64Value_init_zero {0}
+#define google_protobuf_UInt64Value_init_zero {0}
+#define google_protobuf_Int32Value_init_zero {0}
+#define google_protobuf_UInt32Value_init_zero {0}
+#define google_protobuf_BoolValue_init_zero {0}
+#define google_protobuf_StringValue_init_zero {{{NULL}, NULL}}
+#define google_protobuf_BytesValue_init_zero {{{NULL}, NULL}}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define google_protobuf_BytesValue_value_tag 1
+#define google_protobuf_StringValue_value_tag 1
+#define google_protobuf_BoolValue_value_tag 1
+#define google_protobuf_DoubleValue_value_tag 1
+#define google_protobuf_FloatValue_value_tag 1
+#define google_protobuf_Int32Value_value_tag 1
+#define google_protobuf_Int64Value_value_tag 1
+#define google_protobuf_UInt32Value_value_tag 1
+#define google_protobuf_UInt64Value_value_tag 1
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t google_protobuf_DoubleValue_fields[2];
+extern const pb_field_t google_protobuf_FloatValue_fields[2];
+extern const pb_field_t google_protobuf_Int64Value_fields[2];
+extern const pb_field_t google_protobuf_UInt64Value_fields[2];
+extern const pb_field_t google_protobuf_Int32Value_fields[2];
+extern const pb_field_t google_protobuf_UInt32Value_fields[2];
+extern const pb_field_t google_protobuf_BoolValue_fields[2];
+extern const pb_field_t google_protobuf_StringValue_fields[2];
+extern const pb_field_t google_protobuf_BytesValue_fields[2];
+
+/* Maximum encoded size of messages (where known) */
+#define google_protobuf_DoubleValue_size 9
+#define google_protobuf_FloatValue_size 5
+#define google_protobuf_Int64Value_size 11
+#define google_protobuf_UInt64Value_size 11
+#define google_protobuf_Int32Value_size 11
+#define google_protobuf_UInt32Value_size 6
+#define google_protobuf_BoolValue_size 2
+/* google_protobuf_StringValue_size depends on runtime parameters */
+/* google_protobuf_BytesValue_size depends on runtime parameters */
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define WRAPPERS_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+/* @@protoc_insertion_point(eof) */
+
+#endif
diff --git a/Firestore/Protos/objc/firestore/local/Target.pbobjc.h b/Firestore/Protos/objc/firestore/local/Target.pbobjc.h
index d8bf49c..0672a6e 100644
--- a/Firestore/Protos/objc/firestore/local/Target.pbobjc.h
+++ b/Firestore/Protos/objc/firestore/local/Target.pbobjc.h
@@ -160,6 +160,7 @@ typedef GPB_ENUM(FSTPBTargetGlobal_FieldNumber) {
FSTPBTargetGlobal_FieldNumber_HighestTargetId = 1,
FSTPBTargetGlobal_FieldNumber_HighestListenSequenceNumber = 2,
FSTPBTargetGlobal_FieldNumber_LastRemoteSnapshotVersion = 3,
+ FSTPBTargetGlobal_FieldNumber_TargetCount = 4,
};
/**
@@ -197,6 +198,9 @@ typedef GPB_ENUM(FSTPBTargetGlobal_FieldNumber) {
/** Test to see if @c lastRemoteSnapshotVersion has been set. */
@property(nonatomic, readwrite) BOOL hasLastRemoteSnapshotVersion;
+/** On platforms that need it, holds the number of targets persisted. */
+@property(nonatomic, readwrite) int32_t targetCount;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Protos/objc/firestore/local/Target.pbobjc.m b/Firestore/Protos/objc/firestore/local/Target.pbobjc.m
index 6f6ccf2..567c86d 100644
--- a/Firestore/Protos/objc/firestore/local/Target.pbobjc.m
+++ b/Firestore/Protos/objc/firestore/local/Target.pbobjc.m
@@ -183,10 +183,12 @@ void FSTPBTarget_ClearTargetTypeOneOfCase(FSTPBTarget *message) {
@dynamic highestTargetId;
@dynamic highestListenSequenceNumber;
@dynamic hasLastRemoteSnapshotVersion, lastRemoteSnapshotVersion;
+@dynamic targetCount;
typedef struct FSTPBTargetGlobal__storage_ {
uint32_t _has_storage_[1];
int32_t highestTargetId;
+ int32_t targetCount;
GPBTimestamp *lastRemoteSnapshotVersion;
int64_t highestListenSequenceNumber;
} FSTPBTargetGlobal__storage_;
@@ -224,6 +226,15 @@ typedef struct FSTPBTargetGlobal__storage_ {
.flags = GPBFieldOptional,
.dataType = GPBDataTypeMessage,
},
+ {
+ .name = "targetCount",
+ .dataTypeSpecific.className = NULL,
+ .number = FSTPBTargetGlobal_FieldNumber_TargetCount,
+ .hasIndex = 3,
+ .offset = (uint32_t)offsetof(FSTPBTargetGlobal__storage_, targetCount),
+ .flags = GPBFieldOptional,
+ .dataType = GPBDataTypeInt32,
+ },
};
GPBDescriptor *localDescriptor =
[GPBDescriptor allocDescriptorForClass:[FSTPBTargetGlobal class]
diff --git a/Firestore/Protos/protos/firestore/local/target.proto b/Firestore/Protos/protos/firestore/local/target.proto
index 7f34515..7f0a886 100644
--- a/Firestore/Protos/protos/firestore/local/target.proto
+++ b/Firestore/Protos/protos/firestore/local/target.proto
@@ -87,4 +87,7 @@ message TargetGlobal {
// This is updated whenever our we get a TargetChange with a read_time and
// empty target_ids.
google.protobuf.Timestamp last_remote_snapshot_version = 3;
+
+ // On platforms that need it, holds the number of targets persisted.
+ int32 target_count = 4;
}
diff --git a/Firestore/Protos/protos/google/protobuf/any.proto b/Firestore/Protos/protos/google/protobuf/any.proto
new file mode 100644
index 0000000..c748667
--- /dev/null
+++ b/Firestore/Protos/protos/google/protobuf/any.proto
@@ -0,0 +1,149 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "github.com/golang/protobuf/ptypes/any";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// `Any` contains an arbitrary serialized protocol buffer message along with a
+// URL that describes the type of the serialized message.
+//
+// Protobuf library provides support to pack/unpack Any values in the form
+// of utility functions or additional generated methods of the Any type.
+//
+// Example 1: Pack and unpack a message in C++.
+//
+// Foo foo = ...;
+// Any any;
+// any.PackFrom(foo);
+// ...
+// if (any.UnpackTo(&foo)) {
+// ...
+// }
+//
+// Example 2: Pack and unpack a message in Java.
+//
+// Foo foo = ...;
+// Any any = Any.pack(foo);
+// ...
+// if (any.is(Foo.class)) {
+// foo = any.unpack(Foo.class);
+// }
+//
+// Example 3: Pack and unpack a message in Python.
+//
+// foo = Foo(...)
+// any = Any()
+// any.Pack(foo)
+// ...
+// if any.Is(Foo.DESCRIPTOR):
+// any.Unpack(foo)
+// ...
+//
+// Example 4: Pack and unpack a message in Go
+//
+// foo := &pb.Foo{...}
+// any, err := ptypes.MarshalAny(foo)
+// ...
+// foo := &pb.Foo{}
+// if err := ptypes.UnmarshalAny(any, foo); err != nil {
+// ...
+// }
+//
+// The pack methods provided by protobuf library will by default use
+// 'type.googleapis.com/full.type.name' as the type URL and the unpack
+// methods only use the fully qualified type name after the last '/'
+// in the type URL, for example "foo.bar.com/x/y.z" will yield type
+// name "y.z".
+//
+//
+// JSON
+// ====
+// The JSON representation of an `Any` value uses the regular
+// representation of the deserialized, embedded message, with an
+// additional field `@type` which contains the type URL. Example:
+//
+// package google.profile;
+// message Person {
+// string first_name = 1;
+// string last_name = 2;
+// }
+//
+// {
+// "@type": "type.googleapis.com/google.profile.Person",
+// "firstName": <string>,
+// "lastName": <string>
+// }
+//
+// If the embedded message type is well-known and has a custom JSON
+// representation, that representation will be embedded adding a field
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
+//
+// {
+// "@type": "type.googleapis.com/google.protobuf.Duration",
+// "value": "1.212s"
+// }
+//
+message Any {
+ // A URL/resource name whose content describes the type of the
+ // serialized protocol buffer message.
+ //
+ // For URLs which use the scheme `http`, `https`, or no scheme, the
+ // following restrictions and interpretations apply:
+ //
+ // * If no scheme is provided, `https` is assumed.
+ // * The last segment of the URL's path must represent the fully
+ // qualified name of the type (as in `path/google.protobuf.Duration`).
+ // The name should be in a canonical form (e.g., leading "." is
+ // not accepted).
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ // value in binary format, or produce an error.
+ // * Applications are allowed to cache lookup results based on the
+ // URL, or have them precompiled into a binary to avoid any
+ // lookup. Therefore, binary compatibility needs to be preserved
+ // on changes to types. (Use versioned type names to manage
+ // breaking changes.)
+ //
+ // Schemes other than `http`, `https` (or the empty scheme) might be
+ // used with implementation specific semantics.
+ //
+ string type_url = 1;
+
+ // Must be a valid serialized protocol buffer of the above specified type.
+ bytes value = 2;
+}
diff --git a/Firestore/Protos/protos/google/protobuf/empty.proto b/Firestore/Protos/protos/google/protobuf/empty.proto
new file mode 100644
index 0000000..03cacd2
--- /dev/null
+++ b/Firestore/Protos/protos/google/protobuf/empty.proto
@@ -0,0 +1,52 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "github.com/golang/protobuf/ptypes/empty";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "EmptyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+// A generic empty message that you can re-use to avoid defining duplicated
+// empty messages in your APIs. A typical example is to use it as the request
+// or the response type of an API method. For instance:
+//
+// service Foo {
+// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
+// }
+//
+// The JSON representation for `Empty` is empty JSON object `{}`.
+message Empty {}
diff --git a/Firestore/Protos/protos/google/protobuf/wrappers.proto b/Firestore/Protos/protos/google/protobuf/wrappers.proto
new file mode 100644
index 0000000..0194763
--- /dev/null
+++ b/Firestore/Protos/protos/google/protobuf/wrappers.proto
@@ -0,0 +1,118 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Wrappers for primitive (non-message) types. These types are useful
+// for embedding primitives in the `google.protobuf.Any` type and for places
+// where we need to distinguish between the absence of a primitive
+// typed field and its default value.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "github.com/golang/protobuf/ptypes/wrappers";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "WrappersProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// Wrapper message for `double`.
+//
+// The JSON representation for `DoubleValue` is JSON number.
+message DoubleValue {
+ // The double value.
+ double value = 1;
+}
+
+// Wrapper message for `float`.
+//
+// The JSON representation for `FloatValue` is JSON number.
+message FloatValue {
+ // The float value.
+ float value = 1;
+}
+
+// Wrapper message for `int64`.
+//
+// The JSON representation for `Int64Value` is JSON string.
+message Int64Value {
+ // The int64 value.
+ int64 value = 1;
+}
+
+// Wrapper message for `uint64`.
+//
+// The JSON representation for `UInt64Value` is JSON string.
+message UInt64Value {
+ // The uint64 value.
+ uint64 value = 1;
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+ // The int32 value.
+ int32 value = 1;
+}
+
+// Wrapper message for `uint32`.
+//
+// The JSON representation for `UInt32Value` is JSON number.
+message UInt32Value {
+ // The uint32 value.
+ uint32 value = 1;
+}
+
+// Wrapper message for `bool`.
+//
+// The JSON representation for `BoolValue` is JSON `true` and `false`.
+message BoolValue {
+ // The bool value.
+ bool value = 1;
+}
+
+// Wrapper message for `string`.
+//
+// The JSON representation for `StringValue` is JSON string.
+message StringValue {
+ // The string value.
+ string value = 1;
+}
+
+// Wrapper message for `bytes`.
+//
+// The JSON representation for `BytesValue` is JSON string.
+message BytesValue {
+ // The bytes value.
+ bytes value = 1;
+}
diff --git a/Firestore/Source/Local/FSTLevelDBMigrations.mm b/Firestore/Source/Local/FSTLevelDBMigrations.mm
index 49af893..7595c53 100644
--- a/Firestore/Source/Local/FSTLevelDBMigrations.mm
+++ b/Firestore/Source/Local/FSTLevelDBMigrations.mm
@@ -16,21 +16,22 @@
#include "Firestore/Source/Local/FSTLevelDBMigrations.h"
-#include <leveldb/db.h>
-#include <leveldb/write_batch.h>
+#include "leveldb/write_batch.h"
#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
#import "Firestore/Source/Local/FSTLevelDB.h"
#import "Firestore/Source/Local/FSTLevelDBKey.h"
#import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
#import "Firestore/Source/Local/FSTWriteGroup.h"
+#import "Firestore/Source/Util/FSTAssert.h"
NS_ASSUME_NONNULL_BEGIN
// Current version of the schema defined in this file.
-static FSTLevelDBSchemaVersion kSchemaVersion = 1;
+static FSTLevelDBSchemaVersion kSchemaVersion = 2;
using leveldb::DB;
+using leveldb::Iterator;
using leveldb::Status;
using leveldb::Slice;
using leveldb::WriteOptions;
@@ -57,6 +58,31 @@ static void SaveVersion(FSTLevelDBSchemaVersion version, FSTWriteGroup *group) {
[group setData:version_string forKey:key];
}
+/**
+ * This function counts the number of targets that currently exist in the given db. It
+ * then reads the target global row, adds the count to the metadata from that row, and writes
+ * the metadata back.
+ *
+ * It assumes the metadata has already been written and is able to be read in this transaction.
+ */
+static void AddTargetCount(std::shared_ptr<DB> db, FSTWriteGroup *group) {
+ std::unique_ptr<Iterator> it(db->NewIterator([FSTLevelDB standardReadOptions]));
+ Slice start_key = [FSTLevelDBTargetKey keyPrefix];
+ it->Seek(start_key);
+
+ int32_t count = 0;
+ while (it->Valid() && it->key().starts_with(start_key)) {
+ count++;
+ it->Next();
+ }
+
+ FSTPBTargetGlobal *targetGlobal = [FSTLevelDBQueryCache readTargetMetadataFromDB:db];
+ FSTCAssert(targetGlobal != nil,
+ @"We should have a metadata row as it was added in an earlier migration");
+ targetGlobal.targetCount = count;
+ [group setMessage:targetGlobal forKey:[FSTLevelDBTargetGlobalKey key]];
+}
+
@implementation FSTLevelDBMigrations
+ (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr<DB>)db {
@@ -80,6 +106,16 @@ static void SaveVersion(FSTLevelDBSchemaVersion version, FSTWriteGroup *group) {
case 0:
EnsureTargetGlobal(db, group);
// Fallthrough
+ case 1:
+ // We need to make sure we have metadata, since we're going to read and modify it
+ // in this migration. Commit the current transaction and start a new one. Since we're
+ // committing, we need to save a version. It's safe to save this one, if we crash
+ // after saving we'll resume from this step when we try to migrate.
+ SaveVersion(1, group);
+ [group writeToDB:db];
+ group = [FSTWriteGroup groupWithAction:@"Migrations"];
+ AddTargetCount(db, group);
+ // Fallthrough
default:
if (currentVersion < kSchemaVersion) {
SaveVersion(kSchemaVersion, group);
diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
index b3f4822..fe1bf19 100644
--- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm
+++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
@@ -18,7 +18,6 @@
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
-#include <string>
#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
#import "Firestore/Source/Core/FSTQuery.h"
@@ -127,31 +126,50 @@ using leveldb::WriteOptions;
_db.reset();
}
-- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
+- (void)saveQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
FSTTargetID targetID = queryData.targetID;
std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
[group setMessage:[self.serializer encodedQueryData:queryData] forKey:key];
+}
+
+- (void)saveMetadataInGroup:(FSTWriteGroup *)group {
+ [group setMessage:self.metadata forKey:[FSTLevelDBTargetGlobalKey key]];
+}
+
+- (BOOL)updateMetadataForQueryData:(FSTQueryData *)queryData {
+ BOOL updatedMetadata = NO;
+
+ if (queryData.targetID > self.metadata.highestTargetId) {
+ self.metadata.highestTargetId = queryData.targetID;
+ updatedMetadata = YES;
+ }
+
+ if (queryData.sequenceNumber > self.metadata.highestListenSequenceNumber) {
+ self.metadata.highestListenSequenceNumber = queryData.sequenceNumber;
+ updatedMetadata = YES;
+ }
+ return updatedMetadata;
+}
+
+- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
+ [self saveQueryData:queryData group:group];
NSString *canonicalID = queryData.query.canonicalID;
std::string indexKey =
- [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:targetID];
+ [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:queryData.targetID];
std::string emptyBuffer;
[group setData:emptyBuffer forKey:indexKey];
- BOOL saveMetadata = NO;
- FSTPBTargetGlobal *metadata = self.metadata;
- if (targetID > metadata.highestTargetId) {
- metadata.highestTargetId = targetID;
- saveMetadata = YES;
- }
+ self.metadata.targetCount += 1;
+ [self updateMetadataForQueryData:queryData];
+ [self saveMetadataInGroup:group];
+}
- if (queryData.sequenceNumber > metadata.highestListenSequenceNumber) {
- metadata.highestListenSequenceNumber = queryData.sequenceNumber;
- saveMetadata = YES;
- }
+- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
+ [self saveQueryData:queryData group:group];
- if (saveMetadata) {
- [group setMessage:metadata forKey:[FSTLevelDBTargetGlobalKey key]];
+ if ([self updateMetadataForQueryData:queryData]) {
+ [self saveMetadataInGroup:group];
}
}
@@ -166,6 +184,12 @@ using leveldb::WriteOptions;
std::string indexKey =
[FSTLevelDBQueryTargetKey keyWithCanonicalID:queryData.query.canonicalID targetID:targetID];
[group removeMessageForKey:indexKey];
+ self.metadata.targetCount -= 1;
+ [self saveMetadataInGroup:group];
+}
+
+- (int32_t)count {
+ return self.metadata.targetCount;
}
/**
diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm
index d30177a..8a383e5 100644
--- a/Firestore/Source/Local/FSTLocalStore.mm
+++ b/Firestore/Source/Local/FSTLocalStore.mm
@@ -312,7 +312,7 @@ NS_ASSUME_NONNULL_BEGIN
queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion
resumeToken:resumeToken];
self.targetIDs[targetIDNumber] = queryData;
- [self.queryCache addQueryData:queryData group:group];
+ [self.queryCache updateQueryData:queryData group:group];
}
}];
diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.mm b/Firestore/Source/Local/FSTMemoryQueryCache.mm
index bcab174..56d5699 100644
--- a/Firestore/Source/Local/FSTMemoryQueryCache.mm
+++ b/Firestore/Source/Local/FSTMemoryQueryCache.mm
@@ -90,6 +90,20 @@ NS_ASSUME_NONNULL_BEGIN
}
}
+- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
+ self.queries[queryData.query] = queryData;
+ if (queryData.targetID > self.highestTargetID) {
+ self.highestTargetID = queryData.targetID;
+ }
+ if (queryData.sequenceNumber > self.highestListenSequenceNumber) {
+ self.highestListenSequenceNumber = queryData.sequenceNumber;
+ }
+}
+
+- (int32_t)count {
+ return (int32_t)[self.queries count];
+}
+
- (void)removeQueryData:(FSTQueryData *)queryData group:(__unused FSTWriteGroup *)group {
[self.queries removeObjectForKey:queryData.query];
[self.references removeReferencesForID:queryData.targetID];
diff --git a/Firestore/Source/Local/FSTQueryCache.h b/Firestore/Source/Local/FSTQueryCache.h
index 88c9df9..5c43de4 100644
--- a/Firestore/Source/Local/FSTQueryCache.h
+++ b/Firestore/Source/Local/FSTQueryCache.h
@@ -78,18 +78,29 @@ NS_ASSUME_NONNULL_BEGIN
group:(FSTWriteGroup *)group;
/**
- * Adds or replaces an entry in the cache.
+ * Adds an entry in the cache.
*
- * The cache key is extracted from `queryData.query`. If there is already a cache entry for the
- * key, it will be replaced.
+ * The cache key is extracted from `queryData.query`. The key must not already exist in the cache.
*
- * @param queryData An FSTQueryData instance to put in the cache.
+ * @param queryData A new FSTQueryData instance to put in the cache.
*/
- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group;
+/**
+ * Updates an entry in the cache.
+ *
+ * The cache key is extracted from `queryData.query`. The entry must already exist in the cache,
+ * and it will be replaced.
+ * @param queryData An FSTQueryData instance to replace an existing entry in the cache
+ */
+- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group;
+
/** Removes the cached entry for the given query data (no-op if no entry exists). */
- (void)removeQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group;
+/** Returns the number of targets cached. */
+- (int32_t)count;
+
/**
* Looks up an FSTQueryData entry in the cache.
*
diff --git a/Firestore/Source/Remote/FSTStream.mm b/Firestore/Source/Remote/FSTStream.mm
index e5cbf87..c1479c5 100644
--- a/Firestore/Source/Remote/FSTStream.mm
+++ b/Firestore/Source/Remote/FSTStream.mm
@@ -548,6 +548,7 @@ static const NSTimeInterval kIdleTimeout = 60.0;
FSTStrongify(self);
if (![self isStarted]) {
FSTLog(@"%@ Ignoring stream message from inactive stream.", NSStringFromClass([self class]));
+ return;
}
if (!self.messageReceived) {
diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt
index 2fc88c6..e70647d 100644
--- a/Firestore/core/CMakeLists.txt
+++ b/Firestore/core/CMakeLists.txt
@@ -13,6 +13,7 @@
# limitations under the License.
add_subdirectory(src/firebase/firestore)
+add_subdirectory(src/firebase/firestore/auth)
add_subdirectory(src/firebase/firestore/core)
add_subdirectory(src/firebase/firestore/immutable)
add_subdirectory(src/firebase/firestore/model)
@@ -20,6 +21,7 @@ add_subdirectory(src/firebase/firestore/remote)
add_subdirectory(src/firebase/firestore/util)
add_subdirectory(test/firebase/firestore)
+add_subdirectory(test/firebase/firestore/auth)
add_subdirectory(test/firebase/firestore/core)
add_subdirectory(test/firebase/firestore/immutable)
add_subdirectory(test/firebase/firestore/model)
diff --git a/Firestore/core/include/firebase/firestore/document_reference.h b/Firestore/core/include/firebase/firestore/document_reference.h
new file mode 100644
index 0000000..58310b5
--- /dev/null
+++ b/Firestore/core/include/firebase/firestore/document_reference.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+// TODO(rsgowman): This file isn't intended to be used just yet. It's just an
+// outline of what the API might eventually look like. Most of this was
+// shamelessly stolen and modified from rtdb's header file, melded with the
+// (java) firestore api.
+
+#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_
+#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_
+
+#include <string>
+#include <unordered_map>
+
+#if defined(FIREBASE_USE_STD_FUNCTION)
+#include <functional>
+#endif
+
+// TODO(rsgowman): Note that RTDB uses:
+// #if defined(FIREBASE_USE_MOVE_OPERATORS) || defined(DOXYGEN
+// to protect move operators from older compilers. But all our supported
+// compilers support this, so we've skipped the #if guard. This TODO comment is
+// here so we don't forget to mention this during the API review, and should be
+// removed once this note has migrated to the API review doc.
+
+// TODO(rsgowman): replace these forward decl's with appropriate includes (once
+// they exist)
+namespace firebase {
+class App;
+template <typename T>
+class Future;
+} // namespace firebase
+
+namespace firebase {
+namespace firestore {
+
+// TODO(rsgowman): replace these forward decl's with appropriate includes (once
+// they exist)
+class FieldValue;
+class DocumentSnapshot;
+class Firestore;
+class Error;
+template <typename T>
+class EventListener;
+class ListenerRegistration;
+class CollectionReference;
+class DocumentListenOptions;
+// TODO(rsgowman): not quite a forward decl, but required to make the default
+// parameter to Set() "compile".
+class SetOptions {
+ public:
+ SetOptions();
+};
+
+// TODO(rsgowman): move this into the FieldValue header
+#ifdef STLPORT
+using MapFieldValue = std::tr1::unordered_map<std::string, FieldValue>;
+#else
+using MapFieldValue = std::unordered_map<std::string, FieldValue>;
+#endif
+
+/**
+ * A DocumentReference refers to a document location in a Firestore database and
+ * can be used to write, read, or listen to the location. There may or may not
+ * exist a document at the referenced location. A DocumentReference can also be
+ * used to create a CollectionReference to a subcollection.
+ *
+ * Create a DocumentReference via Firebase::Document(const string& path).
+ *
+ * Subclassing Note: Firestore classes are not meant to be subclassed except for
+ * use in test mocks. Subclassing is not supported in production code and new
+ * SDK releases may break code that does so.
+ */
+class DocumentReference {
+ public:
+ /**
+ * @brief Default constructor. This creates an invalid DocumentReference.
+ * Attempting to perform any operations on this reference will fail (and cause
+ * a crash) unless a valid DocumentReference has been assigned to it.
+ */
+ DocumentReference();
+
+ /**
+ * @brief Copy constructor. It's totally okay (and efficient) to copy
+ * DocumentReference instances, as they simply point to the same location in
+ * the database.
+ *
+ * @param[in] reference DocumentReference to copy from.
+ */
+ DocumentReference(const DocumentReference& reference);
+
+ /**
+ * @brief Move constructor. Moving is an efficient operation for
+ * DocumentReference instances.
+ *
+ * @param[in] reference DocumentReference to move data from.
+ */
+ DocumentReference(DocumentReference&& reference);
+
+ virtual ~DocumentReference();
+
+ /**
+ * @brief Copy assignment operator. It's totally okay (and efficient) to copy
+ * DocumentReference instances, as they simply point to the same location in
+ * the database.
+ *
+ * @param[in] reference DocumentReference to copy from.
+ *
+ * @returns Reference to the destination DocumentReference.
+ */
+ DocumentReference& operator=(const DocumentReference& reference);
+
+ /**
+ * @brief Move assignment operator. Moving is an efficient operation for
+ * DocumentReference instances.
+ *
+ * @param[in] reference DocumentReference to move data from.
+ *
+ * @returns Reference to the destination DocumentReference.
+ */
+ DocumentReference& operator=(DocumentReference&& reference);
+
+ /**
+ * @brief Returns the Firestore instance associated with this document
+ * reference.
+ *
+ * The pointer will remain valid indefinitely.
+ *
+ * @returns Firebase Firestore instance that this DocumentReference refers to.
+ */
+ virtual const Firestore* firestore() const;
+
+ /**
+ * @brief Returns the Firestore instance associated with this document
+ * reference.
+ *
+ * The pointer will remain valid indefinitely.
+ *
+ * @returns Firebase Firestore instance that this DocumentReference refers to.
+ */
+ virtual Firestore* firestore();
+
+ /**
+ * @brief Returns the string id of this document location.
+ *
+ * The pointer is only valid while the DocumentReference remains in memory.
+ *
+ * @returns String id of this document location, which will remain valid in
+ * memory until the DocumentReference itself goes away.
+ */
+ virtual const char* id() const;
+
+ /**
+ * @brief Returns the string id of this document location.
+ *
+ * @returns String id of this document location.
+ */
+ virtual std::string id_string() const;
+
+ /**
+ * @brief Returns the path of this document (relative to the root of the
+ * database) as a slash-separated string.
+ *
+ * The pointer is only valid while the DocumentReference remains in memory.
+ *
+ * @returns String path of this document location, which will remain valid in
+ * memory until the DocumentReference itself goes away.
+ */
+ virtual const char* path() const;
+
+ /**
+ * @brief Returns the path of this document (relative to the root of the
+ * database) as a slash-separated string.
+ *
+ * @returns String path of this document location.
+ */
+ virtual std::string path_string() const;
+
+ /**
+ * @brief Returns a CollectionReference to the collection that contains this
+ * document.
+ */
+ virtual CollectionReference get_parent() const;
+
+ /**
+ * @brief Returns a CollectionReference instance that refers to the
+ * subcollection at the specified path relative to this document.
+ *
+ * @param[in] collection_path A slash-separated relative path to a
+ * subcollection. The pointer only needs to be valid during this call.
+ *
+ * @return The CollectionReference instance.
+ */
+ virtual CollectionReference Collection(const char* collection_path) const;
+
+ /**
+ * @brief Returns a CollectionReference instance that refers to the
+ * subcollection at the specified path relative to this document.
+ *
+ * @param[in] collection_path A slash-separated relative path to a
+ * subcollection.
+ *
+ * @return The CollectionReference instance.
+ */
+ virtual CollectionReference Collection(
+ const std::string& collection_path) const;
+
+ /**
+ * @brief Reads the document referenced by this DocumentReference.
+ *
+ * @return A Future that will be resolved with the contents of the Document at
+ * this DocumentReference.
+ */
+ virtual Future<DocumentSnapshot> Get() const;
+
+ /**
+ * @brief Writes to the document referred to by this DocumentReference.
+ *
+ * If the document does not yet exist, it will be created. If you pass
+ * SetOptions, the provided data can be merged into an existing document.
+ *
+ * @param[in] data A map of the fields and values for the document.
+ * @param[in] options An object to configure the set behavior.
+ *
+ * @return A Future that will be resolved when the write finishes.
+ */
+ virtual Future<void> Set(const MapFieldValue& data,
+ const SetOptions& options = SetOptions());
+
+ /**
+ * @brief Updates fields in the document referred to by this
+ * DocumentReference.
+ *
+ * If no document exists yet, the update will fail.
+ *
+ * @param[in] data A map of field / value pairs to update. Fields can contain
+ * dots to reference nested fields within the document.
+ *
+ * @return A Future that will be resolved when the write finishes.
+ */
+ virtual Future<void> Update(const MapFieldValue& data);
+
+ /**
+ * @brief Removes the document referred to by this DocumentReference.
+ *
+ * @return A Task that will be resolved when the delete completes.
+ */
+ virtual Future<void> Delete();
+
+ /**
+ * @brief Starts listening to the document referenced by this
+ * DocumentReference.
+ *
+ * @param[in] listener The event listener that will be called with the
+ * snapshots, which must remain in memory until you remove the listener from
+ * this DocumentReference. (Ownership is not transferred; you are responsible
+ * for making sure that listener is valid as long as this DocumentReference is
+ * valid and the listener is registered.)
+ *
+ * @return A registration object that can be used to remove the listener.
+ */
+ virtual ListenerRegistration AddSnapshotListener(
+ EventListener<DocumentSnapshot>* listener);
+
+ /**
+ * @brief Starts listening to the document referenced by this
+ * DocumentReference.
+ *
+ * @param[in] options The options to use for this listen.
+ * @param[in] listener The event listener that will be called with the
+ * snapshots, which must remain in memory until you remove the listener from
+ * this DocumentReference. (Ownership is not transferred; you are responsible
+ * for making sure that listener is valid as long as this DocumentReference is
+ * valid and the listener is registered.)
+ *
+ * @return A registration object that can be used to remove the listener.
+ */
+ virtual ListenerRegistration AddSnapshotListener(
+ const DocumentListenOptions& options,
+ EventListener<DocumentSnapshot>* listener);
+
+#if defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN)
+ /**
+ * @brief Starts listening to the document referenced by this
+ * DocumentReference.
+ *
+ * @param[in] callback function or lambda to call. When this function is
+ * called, exactly one of the parameters will be non-null.
+ *
+ * @return A registration object that can be used to remove the listener.
+ *
+ * @note This method is not available when using STLPort on Android, as
+ * std::function is not supported on STLPort.
+ */
+ virtual ListenerRegistration AddSnapshotListener(
+ std::function<void(const DocumentSnapshot*, const Error*)> callback);
+
+ /**
+ * @brief Starts listening to the document referenced by this
+ * DocumentReference.
+ *
+ * @param[in] options The options to use for this listen.
+ * @param[in] callback function or lambda to call. When this function is
+ * called, exactly one of the parameters will be non-null.
+ *
+ * @return A registration object that can be used to remove the listener.
+ *
+ * @note This method is not available when using STLPort on Android, as
+ * std::function is not supported on STLPort.
+ */
+ virtual ListenerRegistration AddSnapshotListener(
+ const DocumentListenOptions& options,
+ std::function<void(const DocumentSnapshot*, const Error*)> callback);
+#endif // defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN)
+};
+
+// TODO(rsgowman): probably define and inline here.
+bool operator==(const DocumentReference& lhs, const DocumentReference& rhs);
+
+inline bool operator!=(const DocumentReference& lhs,
+ const DocumentReference& rhs) {
+ return !(lhs == rhs);
+}
+
+// TODO(rsgowman): probably define and inline here.
+bool operator<(const DocumentReference& lhs, const DocumentReference& rhs);
+
+inline bool operator>(const DocumentReference& lhs,
+ const DocumentReference& rhs) {
+ return rhs < lhs;
+}
+
+inline bool operator<=(const DocumentReference& lhs,
+ const DocumentReference& rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator>=(const DocumentReference& lhs,
+ const DocumentReference& rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace firestore
+} // namespace firebase
+
+namespace std {
+// TODO(rsgowman): NB that specialization of std::hash deviates from the Google
+// C++ style guide. But we think this is probably ok in this case since:
+// a) It's the standard way of doing this outside of Google (as the style guide
+// itself points out), and
+// b) This has a straightfoward hash function anyway (just hash the path) so I
+// don't think the concerns in the style guide are going to bite us.
+//
+// Raise this concern during the API review.
+template <>
+struct hash<firebase::firestore::DocumentReference> {
+ std::size_t operator()(
+ const firebase::firestore::DocumentReference& doc_ref) const;
+};
+} // namespace std
+
+#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_
diff --git a/Firestore/core/include/firebase/firestore/event_listener.h b/Firestore/core/include/firebase/firestore/event_listener.h
new file mode 100644
index 0000000..6c94428
--- /dev/null
+++ b/Firestore/core/include/firebase/firestore/event_listener.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+// TODO(rsgowman): This file isn't intended to be used just yet. It's just an
+// outline of what the API might eventually look like. Most of this was
+// shamelessly stolen and modified from rtdb's header file, melded with the
+// (java) firestore api.
+
+#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_
+#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_
+
+namespace firebase {
+namespace firestore {
+
+// TODO(rsgowman): replace these forward decl's with appropriate includes (once
+// they exist)
+class Error;
+
+/**
+ * @brief An interface for event listeners.
+ */
+template <typename T>
+class EventListener {
+ public:
+ /**
+ * @brief OnEvent will be called with the new value or the error if an error
+ * occurred.
+ *
+ * It's guaranteed that exactly one of value or error will be non-null.
+ *
+ * @param value The value of the event. null if there was an error.
+ * @param error The error if there was error. null otherwise.
+ */
+ void OnEvent(const T* value, const Error* error);
+};
+
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_
diff --git a/Firestore/core/include/firebase/firestore/firestore.h b/Firestore/core/include/firebase/firestore/firestore.h
new file mode 100644
index 0000000..793fdd0
--- /dev/null
+++ b/Firestore/core/include/firebase/firestore/firestore.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+// TODO(rsgowman): This file isn't intended to be used just yet. It's just an
+// outline of what the API might eventually look like. Most of this was
+// shamelessly stolen and modified from rtdb's header file, melded with the
+// firestore api.
+
+#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_
+#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_
+
+#include <string>
+
+// TODO(rsgowman): replace these forward decl's with appropriate includes (once
+// they exist)
+namespace firebase {
+class App;
+class InitResult;
+} // namespace firebase
+
+namespace firebase {
+namespace firestore {
+
+// TODO(rsgowman): replace these forward decl's with appropriate includes (once
+// they exist)
+class DocumentReference;
+class CollectionReference;
+class Settings;
+
+/**
+ * @brief Entry point for the Firebase Firestore C++ SDK.
+ *
+ * To use the SDK, call firebase::firestore::Firestore::GetInstance() to obtain
+ * an instance of Firestore, then use Collection() or Document() to obtain
+ * references to child paths within the database. From there you can set data
+ * via CollectionReference::Add() and DocumentReference::Set(), or get data via
+ * CollectionReference::Get() and DocumentReference::Get(), attach listeners,
+ * and more.
+ *
+ * Subclassing Note: Firestore classes are not meant to be subclassed except for
+ * use in test mocks. Subclassing is not supported in production code and new
+ * SDK releases may break code that does so.
+ */
+class Firestore {
+ public:
+ /**
+ * @brief Returns an instance of Firestore corresponding to the given App.
+ *
+ * Firebase Firestore uses firebase::App to communicate with Firebase
+ * Authentication to authenticate users to the Firestore server backend.
+ *
+ * If you call GetInstance() multiple times with the same App, you will get
+ * the same instance of App.
+ *
+ * @param[in] app Your instance of firebase::App. Firebase Firestore will use
+ * this to communicate with Firebase Authentication.
+ * @param[out] init_result_out Optional: If provided, write the init result
+ * here. Will be set to kInitResultSuccess if initialization succeeded, or
+ * kInitResultFailedMissingDependency on Android if Google Play services is
+ * not available on the current device.
+ *
+ * @returns An instance of Firestore corresponding to the given App.
+ */
+ static Firestore* GetInstance(::firebase::App* app,
+ InitResult* init_result_out = nullptr);
+
+ static Firestore* GetInstance(InitResult* init_result_out = nullptr);
+
+ /**
+ * @brief Destructor for the Firestore object.
+ *
+ * When deleted, this instance will be removed from the cache of Firestore
+ * objects. If you call GetInstance() in the future with the same App, a new
+ * Firestore instance will be created.
+ */
+ virtual ~Firestore();
+
+ /**
+ * @brief Returns the firebase::App that this Firestore was created with.
+ *
+ * @returns The firebase::App this Firestore was created with.
+ */
+ virtual const App* app() const;
+
+ /**
+ * @brief Returns the firebase::App that this Firestore was created with.
+ *
+ * @returns The firebase::App this Firestore was created with.
+ */
+ virtual App* app();
+
+ /**
+ * @brief Returns a CollectionReference instance that refers to the
+ * collection at the specified path within the database.
+ *
+ * @param[in] collection_path A slash-separated path to a collection.
+ *
+ * @return The CollectionReference instance.
+ */
+ virtual CollectionReference Collection(const char* collection_path) const;
+
+ /**
+ * @brief Returns a CollectionReference instance that refers to the
+ * collection at the specified path within the database.
+ *
+ * @param[in] collection_path A slash-separated path to a collection.
+ *
+ * @return The CollectionReference instance.
+ */
+ virtual CollectionReference Collection(
+ const std::string& collection_path) const;
+
+ /**
+ * @brief Returns a DocumentReference instance that refers to the document at
+ * the specified path within the database.
+ *
+ * @param[in] document_path A slash-separated path to a document.
+ * @return The DocumentReference instance.
+ */
+ virtual DocumentReference Document(const char* document_path) const;
+
+ /**
+ * @brief Returns a DocumentReference instance that refers to the document at
+ * the specified path within the database.
+ *
+ * @param[in] document_path A slash-separated path to a document.
+ *
+ * @return The DocumentReference instance.
+ */
+ virtual DocumentReference Document(const std::string& document_path) const;
+
+ /** Returns the settings used by this Firestore object. */
+ virtual Settings settings() const;
+
+ /** Sets any custom settings used to configure this Firestore object. */
+ virtual void set_settings(const Settings& settings);
+
+ // TODO(rsgowman): batch(), runTransaction()
+
+ /** Globally enables / disables Firestore logging for the SDK. */
+ static void set_logging_enabled(bool logging_enabled);
+};
+
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_
diff --git a/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt
new file mode 100644
index 0000000..2241fae
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Copyright 2018 Google
+#
+# 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.
+
+cc_library(
+ firebase_firestore_auth_base
+ SOURCES
+ credentials_provider.cc
+ credentials_provider.h
+ token.cc
+ token.h
+ user.cc
+ user.h
+ DEPENDS
+ absl_strings
+ firebase_firestore_util
+)
+
+cc_library(
+ firebase_firestore_auth_apple
+ SOURCES
+ firebase_credentials_provider_apple.h
+ firebase_credentials_provider_apple.mm
+ DEPENDS
+ FirebaseCore
+ firebase_firestore_auth_base
+ EXCLUDE_FROM_ALL
+)
+
+if(APPLE)
+ list(APPEND AUTH_DEPENDS firebase_firestore_auth_apple)
+endif()
+
+cc_library(
+ firebase_firestore_auth
+ SOURCES
+ empty_credentials_provider.cc
+ empty_credentials_provider.h
+ DEPENDS
+ ${AUTH_DEPENDS}
+ firebase_firestore_auth_base
+)
diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc b/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc
new file mode 100644
index 0000000..0301944
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+CredentialsProvider::CredentialsProvider() : user_change_listener_(nullptr) {
+}
+
+CredentialsProvider::~CredentialsProvider() {
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h
new file mode 100644
index 0000000..2a52c99
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_
+
+#include <functional>
+#include <string>
+
+#include "Firestore/core/src/firebase/firestore/auth/token.h"
+#include "Firestore/core/src/firebase/firestore/auth/user.h"
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+// `TokenErrorListener` is a listener that gets a token or an error.
+// token: An auth token as a string, or nullptr if error occurred.
+// error: The error if one occurred, or else nullptr.
+typedef std::function<void(const Token& token, const absl::string_view error)>
+ TokenListener;
+
+// Listener notified with a User change.
+typedef std::function<void(const User& user)> UserChangeListener;
+
+/**
+ * Provides methods for getting the uid and token for the current user and
+ * listen for changes.
+ */
+class CredentialsProvider {
+ public:
+ CredentialsProvider();
+
+ virtual ~CredentialsProvider();
+
+ /**
+ * Requests token for the current user, optionally forcing a refreshed token
+ * to be fetched.
+ */
+ virtual void GetToken(bool force_refresh, TokenListener completion) = 0;
+
+ /**
+ * Sets the listener to be notified of user changes (sign-in / sign-out). It
+ * is immediately called once with the initial user.
+ *
+ * Call with nullptr to remove previous listener.
+ */
+ virtual void SetUserChangeListener(UserChangeListener listener) = 0;
+
+ protected:
+ /**
+ * A listener to be notified of user changes (sign-in / sign-out). It is
+ * immediately called once with the initial user.
+ *
+ * Note that this block will be called back on an arbitrary thread that is not
+ * the normal Firestore worker thread.
+ */
+ UserChangeListener user_change_listener_;
+};
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_
diff --git a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc
new file mode 100644
index 0000000..6ee7f61
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#define UNUSED(x) (void)(x)
+
+#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+void EmptyCredentialsProvider::GetToken(bool force_refresh,
+ TokenListener completion) {
+ UNUSED(force_refresh);
+ if (completion) {
+ completion({"", User::Unauthenticated()}, "");
+ }
+}
+
+void EmptyCredentialsProvider::SetUserChangeListener(
+ UserChangeListener listener) {
+ if (listener) {
+ listener(User::Unauthenticated());
+ }
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h
new file mode 100644
index 0000000..55b3cc6
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_
+
+#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+/** `EmptyCredentialsProvider` always yields an empty token. */
+class EmptyCredentialsProvider : public CredentialsProvider {
+ public:
+ void GetToken(bool force_refresh, TokenListener completion) override;
+ void SetUserChangeListener(UserChangeListener listener) override;
+};
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_
diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h
new file mode 100644
index 0000000..65c4c65
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+// Right now, FirebaseCredentialsProvider only support APPLE build.
+#if !defined(__OBJC__)
+#error "This header only supports Objective-C++."
+#endif // !defined(__OBJC__)
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_
+
+#import <Foundation/Foundation.h>
+
+#include <memory>
+#include <mutex> // NOLINT(build/c++11)
+
+#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
+#include "Firestore/core/src/firebase/firestore/auth/user.h"
+#include "absl/strings/string_view.h"
+
+@class FIRApp;
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+/**
+ * `FirebaseCredentialsProvider` uses Firebase Auth via `FIRApp` to get an auth
+ * token.
+ *
+ * NOTE: To simplify the implementation, it requires that you set
+ * `userChangeListener` with a non-`nil` value no more than once and don't call
+ * `getTokenForcingRefresh:` after setting it to `nil`.
+ *
+ * This class must be implemented in a thread-safe manner since it is accessed
+ * from the thread backing our internal worker queue and the callbacks from
+ * FIRAuth will be executed on an arbitrary different thread.
+ *
+ * For non-Apple desktop build, this is right now just a stub.
+ */
+class FirebaseCredentialsProvider : public CredentialsProvider {
+ public:
+ // TODO(zxu123): Provide a ctor to accept the C++ Firebase Games App, which
+ // deals all platforms. Right now, only works for FIRApp*.
+ /**
+ * Initializes a new FirebaseCredentialsProvider.
+ *
+ * @param app The Firebase app from which to get credentials.
+ */
+ explicit FirebaseCredentialsProvider(FIRApp* app);
+
+ ~FirebaseCredentialsProvider() override;
+
+ void GetToken(bool force_refresh, TokenListener completion) override;
+
+ void SetUserChangeListener(UserChangeListener listener) override;
+
+ private:
+ /**
+ * Most contents of the FirebaseCredentialProvider are kept in this
+ * Contents object and pointed to with a shared pointer. Callbacks
+ * registered with FirebaseAuth use weak pointers to the Contents to
+ * avoid races between notifications arriving and C++ object destruction.
+ */
+ struct Contents {
+ Contents(FIRApp* app, const absl::string_view uid)
+ : app(app), current_user(uid), mutex() {
+ }
+
+ const FIRApp* app;
+
+ /**
+ * The current user as reported to us via our AuthStateDidChangeListener.
+ */
+ User current_user;
+
+ /**
+ * Counter used to detect if the user changed while a
+ * -getTokenForcingRefresh: request was outstanding.
+ */
+ int user_counter = 0;
+
+ std::mutex mutex;
+ };
+
+ /**
+ * Handle used to stop receiving auth changes once userChangeListener is
+ * removed.
+ */
+ id<NSObject> auth_listener_handle_;
+
+ std::shared_ptr<Contents> contents_;
+};
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_
diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm
new file mode 100644
index 0000000..f463958
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h"
+
+#import <FirebaseCore/FIRApp.h>
+#import <FirebaseCore/FIRAppInternal.h>
+#import <FirebaseCore/FIROptionsInternal.h>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+FirebaseCredentialsProvider::FirebaseCredentialsProvider(FIRApp* app)
+ : contents_(
+ std::make_shared<Contents>(app, util::MakeStringView([app getUID]))) {
+ std::weak_ptr<Contents> weak_contents = contents_;
+
+ auth_listener_handle_ = [[NSNotificationCenter defaultCenter]
+ addObserverForName:FIRAuthStateDidChangeInternalNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification* notification) {
+ std::shared_ptr<Contents> contents = weak_contents.lock();
+ if (!contents) {
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(contents->mutex);
+ NSDictionary<NSString*, id>* user_info = notification.userInfo;
+
+ // ensure we're only notifiying for the current app.
+ FIRApp* notified_app =
+ user_info[FIRAuthStateDidChangeInternalNotificationAppKey];
+ if (![contents->app isEqual:notified_app]) {
+ return;
+ }
+
+ NSString* user_id =
+ user_info[FIRAuthStateDidChangeInternalNotificationUIDKey];
+ User new_user(util::MakeStringView(user_id));
+ if (new_user != contents->current_user) {
+ contents->current_user = new_user;
+ contents->user_counter++;
+ UserChangeListener listener = user_change_listener_;
+ if (listener) {
+ listener(contents->current_user);
+ }
+ }
+ }];
+}
+
+FirebaseCredentialsProvider::~FirebaseCredentialsProvider() {
+ if (auth_listener_handle_) {
+ // Even though iOS 9 (and later) and macOS 10.11 (and later) keep a weak
+ // reference to the observer so we could avoid this removeObserver call, we
+ // still support iOS 8 which requires it.
+ [[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_];
+ }
+}
+
+void FirebaseCredentialsProvider::GetToken(bool force_refresh,
+ TokenListener completion) {
+ FIREBASE_ASSERT_MESSAGE(auth_listener_handle_,
+ "GetToken cannot be called after listener removed.");
+
+ // Take note of the current value of the userCounter so that this method can
+ // fail if there is a user change while the request is outstanding.
+ int initial_user_counter = contents_->user_counter;
+
+ std::weak_ptr<Contents> weak_contents = contents_;
+ void (^get_token_callback)(NSString*, NSError*) = ^(
+ NSString* _Nullable token, NSError* _Nullable error) {
+ std::shared_ptr<Contents> contents = weak_contents.lock();
+ if (!contents) {
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(contents->mutex);
+ if (initial_user_counter != contents->user_counter) {
+ // Cancel the request since the user changed while the request was
+ // outstanding so the response is likely for a previous user (which
+ // user, we can't be sure).
+ completion({"", User::Unauthenticated()},
+ "getToken aborted due to user change.");
+ } else {
+ completion(
+ {util::MakeStringView(token), contents->current_user},
+ error == nil ? "" : util::MakeStringView(error.localizedDescription));
+ }
+ };
+
+ [contents_->app getTokenForcingRefresh:force_refresh
+ withCallback:get_token_callback];
+}
+
+void FirebaseCredentialsProvider::SetUserChangeListener(
+ UserChangeListener listener) {
+ std::unique_lock<std::mutex> lock(contents_->mutex);
+ if (listener) {
+ FIREBASE_ASSERT_MESSAGE(!user_change_listener_,
+ "set user_change_listener twice!");
+ // Fire initial event.
+ listener(contents_->current_user);
+ } else {
+ FIREBASE_ASSERT_MESSAGE(auth_listener_handle_,
+ "removed user_change_listener twice!");
+ FIREBASE_ASSERT_MESSAGE(user_change_listener_,
+ "user_change_listener removed without being set!");
+ [[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_];
+ auth_listener_handle_ = nil;
+ }
+ user_change_listener_ = listener;
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/auth/token.cc b/Firestore/core/src/firebase/firestore/auth/token.cc
new file mode 100644
index 0000000..0618ddb
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/token.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/token.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+Token::Token(const absl::string_view token, const User& user)
+ : token_(token), user_(user) {
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/auth/token.h b/Firestore/core/src/firebase/firestore/auth/token.h
new file mode 100644
index 0000000..f3b7363
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/token.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_
+
+#include <string>
+
+#include "Firestore/core/src/firebase/firestore/auth/user.h"
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+/**
+ * The current User and the authentication token provided by the underlying
+ * authentication mechanism. This is the result of calling
+ * CredentialsProvider::GetToken().
+ *
+ * ## Portability notes: no TokenType on iOS
+ *
+ * The TypeScript client supports 1st party Oauth tokens (for the Firebase
+ * Console to auth as the developer) and OAuth2 tokens for the node.js sdk to
+ * auth with a service account. We don't have plans to support either case on
+ * mobile so there's no TokenType here.
+ */
+// TODO(zxu123): Make this support token-type for desktop workflow.
+class Token {
+ public:
+ Token(const absl::string_view token, const User& user);
+
+ /** The actual raw token. */
+ const std::string& token() const {
+ return token_;
+ }
+
+ /**
+ * The user with which the token is associated (used for persisting user
+ * state on disk, etc.).
+ */
+ const User& user() const {
+ return user_;
+ }
+
+ private:
+ const std::string token_;
+ const User user_;
+};
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_
diff --git a/Firestore/core/src/firebase/firestore/auth/user.cc b/Firestore/core/src/firebase/firestore/auth/user.cc
new file mode 100644
index 0000000..f442d7b
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/user.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/user.h"
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+User::User() : uid_(), is_authenticated_(false) {
+}
+
+User::User(const absl::string_view uid) : uid_(uid), is_authenticated_(true) {
+ FIREBASE_ASSERT(!uid.empty());
+}
+
+const User& User::Unauthenticated() {
+ static const User kUnauthenticated;
+ return kUnauthenticated;
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/auth/user.h b/Firestore/core/src/firebase/firestore/auth/user.h
new file mode 100644
index 0000000..58b8b55
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/auth/user.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+/**
+ * Simple wrapper around a nullable UID. Mostly exists to make code more
+ * readable and for compatibility with other clients where map keys cannot be
+ * null.
+ */
+class User {
+ public:
+ /** Construct an unauthenticated user. */
+ User();
+
+ /** Construct an authenticated user with the given UID. */
+ explicit User(const absl::string_view uid);
+
+ const std::string& uid() const {
+ return uid_;
+ }
+
+ // PORTING NOTE: Here use more clear naming is_authenticated() instead of
+ // is_unauthenticated().
+ bool is_authenticated() const {
+ return is_authenticated_;
+ }
+
+ /** Returns an unauthenticated instance. */
+ static const User& Unauthenticated();
+
+ User& operator=(const User& other) = default;
+
+ friend bool operator==(const User& lhs, const User& rhs);
+
+ private:
+ std::string uid_;
+ bool is_authenticated_;
+};
+
+inline bool operator==(const User& lhs, const User& rhs) {
+ return lhs.is_authenticated_ == rhs.is_authenticated_ &&
+ (!lhs.is_authenticated_ || lhs.uid_ == rhs.uid_);
+}
+
+inline bool operator!=(const User& lhs, const User& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_
diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
index 8bdbe18..1b0e6a4 100644
--- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
@@ -18,10 +18,22 @@ cc_library(
base_path.h
database_id.cc
database_id.h
+ document.cc
+ document.h
+ document_key.cc
+ document_key.h
field_path.cc
field_path.h
field_value.cc
field_value.h
+ maybe_document.cc
+ maybe_document.h
+ no_document.cc
+ no_document.h
+ resource_path.cc
+ resource_path.h
+ snapshot_version.cc
+ snapshot_version.h
timestamp.cc
timestamp.h
DEPENDS
diff --git a/Firestore/core/src/firebase/firestore/model/base_path.h b/Firestore/core/src/firebase/firestore/model/base_path.h
index f5a8ab7..accce27 100644
--- a/Firestore/core/src/firebase/firestore/model/base_path.h
+++ b/Firestore/core/src/firebase/firestore/model/base_path.h
@@ -166,7 +166,7 @@ class BasePath {
}
BasePath(std::initializer_list<std::string> list) : segments_{list} {
}
- BasePath(SegmentsT&& segments) : segments_{std::move(segments)} {
+ explicit BasePath(SegmentsT&& segments) : segments_{std::move(segments)} {
}
private:
diff --git a/Firestore/core/src/firebase/firestore/model/document.cc b/Firestore/core/src/firebase/firestore/model/document.cc
new file mode 100644
index 0000000..16548cd
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/document.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/document.h"
+
+#include <utility>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+Document::Document(FieldValue&& data,
+ DocumentKey key,
+ SnapshotVersion version,
+ bool has_local_mutations)
+ : MaybeDocument(std::move(key), std::move(version)),
+ data_(std::move(data)),
+ has_local_mutations_(has_local_mutations) {
+ set_type(Type::Document);
+ FIREBASE_ASSERT(FieldValue::Type::Object == data.type());
+}
+
+bool Document::Equals(const MaybeDocument& other) const {
+ if (other.type() != Type::Document) {
+ return false;
+ }
+ const Document& other_doc = static_cast<const Document&>(other);
+ return MaybeDocument::Equals(other) &&
+ has_local_mutations_ == other_doc.has_local_mutations_ &&
+ data_ == other_doc.data_;
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/document.h b/Firestore/core/src/firebase/firestore/model/document.h
new file mode 100644
index 0000000..50a7b90
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/document.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_
+
+#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * Represents a document in Firestore with a key, version, data and whether the
+ * data has local mutations applied to it.
+ */
+class Document : public MaybeDocument {
+ public:
+ /**
+ * Construct a document. FieldValue must be passed by rvalue.
+ */
+ Document(FieldValue&& data,
+ DocumentKey key,
+ SnapshotVersion version,
+ bool has_local_mutations);
+
+ const FieldValue& data() const {
+ return data_;
+ }
+
+ bool has_local_mutations() const {
+ return has_local_mutations_;
+ }
+
+ protected:
+ bool Equals(const MaybeDocument& other) const override;
+
+ private:
+ FieldValue data_; // This is of type Object.
+ bool has_local_mutations_;
+};
+
+/** Compares against another Document. */
+inline bool operator==(const Document& lhs, const Document& rhs) {
+ return lhs.version() == rhs.version() && lhs.key() == rhs.key() &&
+ lhs.has_local_mutations() == rhs.has_local_mutations() &&
+ lhs.data() == rhs.data();
+}
+
+inline bool operator!=(const Document& lhs, const Document& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_
diff --git a/Firestore/core/src/firebase/firestore/model/document_key.cc b/Firestore/core/src/firebase/firestore/model/document_key.cc
new file mode 100644
index 0000000..ddda4c9
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/document_key.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+
+#include <utility>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+namespace {
+
+void AssertValidPath(const ResourcePath& path) {
+ FIREBASE_ASSERT_MESSAGE(DocumentKey::IsDocumentKey(path),
+ "invalid document key path: %s",
+ path.CanonicalString().c_str());
+}
+
+} // namespace
+
+DocumentKey::DocumentKey(const ResourcePath& path)
+ : path_{std::make_shared<ResourcePath>(path)} {
+ AssertValidPath(*path_);
+}
+
+DocumentKey::DocumentKey(ResourcePath&& path)
+ : path_{std::make_shared<ResourcePath>(std::move(path))} {
+ AssertValidPath(*path_);
+}
+
+const DocumentKey& DocumentKey::Empty() {
+ static const DocumentKey empty;
+ return empty;
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/document_key.h b/Firestore/core/src/firebase/firestore/model/document_key.h
new file mode 100644
index 0000000..6eb1e18
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/document_key.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_
+
+#include <initializer_list>
+#include <memory>
+#include <string>
+
+#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * DocumentKey represents the location of a document in the Firestore database.
+ */
+class DocumentKey {
+ public:
+ /** Creates a "blank" document key not associated with any document. */
+ DocumentKey() : path_{std::make_shared<ResourcePath>()} {
+ }
+
+ /** Creates a new document key containing a copy of the given path. */
+ explicit DocumentKey(const ResourcePath& path);
+
+ /** Creates a new document key, taking ownership of the given path. */
+ explicit DocumentKey(ResourcePath&& path);
+
+ /**
+ * Creates and returns a new document key using '/' to split the string into
+ * segments.
+ */
+ static DocumentKey FromPathString(const absl::string_view path) {
+ return DocumentKey{ResourcePath::FromString(path)};
+ }
+
+ /** Creates and returns a new document key with the given segments. */
+ static DocumentKey FromSegments(std::initializer_list<std::string> list) {
+ return DocumentKey{ResourcePath{list}};
+ }
+
+ /** Returns a shared instance of an empty document key. */
+ static const DocumentKey& Empty();
+
+ /** Returns true iff the given path is a path to a document. */
+ static bool IsDocumentKey(const ResourcePath& path) {
+ return path.size() % 2 == 0;
+ }
+
+ /** The path to the document. */
+ const ResourcePath& path() const {
+ return path_ ? *path_ : Empty().path();
+ }
+
+ private:
+ // This is an optimization to make passing DocumentKey around cheaper (it's
+ // copied often).
+ std::shared_ptr<const ResourcePath> path_;
+};
+
+inline bool operator==(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() == rhs.path();
+}
+inline bool operator!=(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() != rhs.path();
+}
+inline bool operator<(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() < rhs.path();
+}
+inline bool operator<=(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() <= rhs.path();
+}
+inline bool operator>(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() > rhs.path();
+}
+inline bool operator>=(const DocumentKey& lhs, const DocumentKey& rhs) {
+ return lhs.path() >= rhs.path();
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_
diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc
index 6c40600..bc0e97c 100644
--- a/Firestore/core/src/firebase/firestore/model/field_path.cc
+++ b/Firestore/core/src/firebase/firestore/model/field_path.cc
@@ -30,9 +30,6 @@ namespace model {
namespace {
-// TODO(varconst): move to C++ equivalent of FSTDocumentKey.{h,cc}
-const char* const kDocumentKeyPath = "__name__";
-
/**
* True if the string could be used as a segment in a field path without
* escaping. Valid identifies follow the regex [a-zA-Z_][a-zA-Z0-9_]*
@@ -51,7 +48,7 @@ bool IsValidIdentifier(const std::string& segment) {
(first < 'A' || first > 'Z')) {
return false;
}
- for (int i = 1; i != segment.size(); ++i) {
+ for (size_t i = 1; i != segment.size(); ++i) {
const unsigned char c = segment[i];
if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') &&
(c < '0' || c > '9')) {
@@ -93,7 +90,7 @@ FieldPath FieldPath::FromServerFormat(const absl::string_view path) {
// Inside backticks, dots are treated literally.
bool inside_backticks = false;
- int i = 0;
+ size_t i = 0;
while (i < path.size()) {
const char c = path[i];
// std::string (and string_view) may contain embedded nulls. For full
@@ -146,12 +143,12 @@ const FieldPath& FieldPath::EmptyPath() {
}
const FieldPath& FieldPath::KeyFieldPath() {
- static const FieldPath key_field_path{kDocumentKeyPath};
+ static const FieldPath key_field_path{FieldPath::kDocumentKeyPath};
return key_field_path;
}
bool FieldPath::IsKeyFieldPath() const {
- return size() == 1 && first_segment() == kDocumentKeyPath;
+ return size() == 1 && first_segment() == FieldPath::kDocumentKeyPath;
}
std::string FieldPath::CanonicalString() const {
diff --git a/Firestore/core/src/firebase/firestore/model/field_path.h b/Firestore/core/src/firebase/firestore/model/field_path.h
index 00b658a..fdf4918 100644
--- a/Firestore/core/src/firebase/firestore/model/field_path.h
+++ b/Firestore/core/src/firebase/firestore/model/field_path.h
@@ -35,6 +35,9 @@ namespace model {
*/
class FieldPath : public impl::BasePath<FieldPath> {
public:
+ /** The field path string that represents the document's key. */
+ static constexpr const char* kDocumentKeyPath = "__name__";
+
// Note: Xcode 8.2 requires explicit specification of the constructor.
FieldPath() : impl::BasePath<FieldPath>() {
}
@@ -82,7 +85,7 @@ class FieldPath : public impl::BasePath<FieldPath> {
}
private:
- FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} {
+ explicit FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} {
}
// So that methods of base can construct FieldPath using the private
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc
index 570226e..012a90d 100644
--- a/Firestore/core/src/firebase/firestore/model/field_value.cc
+++ b/Firestore/core/src/firebase/firestore/model/field_value.cc
@@ -99,6 +99,9 @@ FieldValue& FieldValue::operator=(const FieldValue& value) {
std::swap(blob_value_, tmp);
break;
}
+ case Type::Reference:
+ reference_value_ = value.reference_value_;
+ break;
case Type::GeoPoint:
geo_point_value_ = value.geo_point_value_;
break;
@@ -131,6 +134,11 @@ FieldValue& FieldValue::operator=(FieldValue&& value) {
SwitchTo(Type::Blob);
std::swap(blob_value_, value.blob_value_);
return *this;
+ case Type::Reference:
+ SwitchTo(Type::Reference);
+ std::swap(reference_value_.reference, value.reference_value_.reference);
+ reference_value_.database_id = value.reference_value_.database_id;
+ return *this;
case Type::Array:
SwitchTo(Type::Array);
std::swap(array_value_, value.array_value_);
@@ -233,6 +241,26 @@ FieldValue FieldValue::BlobValue(const uint8_t* source, size_t size) {
return result;
}
+// Does NOT pass ownership of database_id.
+FieldValue FieldValue::ReferenceValue(const DocumentKey& value,
+ const DatabaseId* database_id) {
+ FieldValue result;
+ result.SwitchTo(Type::Reference);
+ result.reference_value_.reference = value;
+ result.reference_value_.database_id = database_id;
+ return result;
+}
+
+// Does NOT pass ownership of database_id.
+FieldValue FieldValue::ReferenceValue(DocumentKey&& value,
+ const DatabaseId* database_id) {
+ FieldValue result;
+ result.SwitchTo(Type::Reference);
+ std::swap(result.reference_value_.reference, value);
+ result.reference_value_.database_id = database_id;
+ return result;
+}
+
FieldValue FieldValue::GeoPointValue(const GeoPoint& value) {
FieldValue result;
result.SwitchTo(Type::GeoPoint);
@@ -309,6 +337,12 @@ bool operator<(const FieldValue& lhs, const FieldValue& rhs) {
return lhs.string_value_.compare(rhs.string_value_) < 0;
case Type::Blob:
return lhs.blob_value_ < rhs.blob_value_;
+ case Type::Reference:
+ return *lhs.reference_value_.database_id <
+ *rhs.reference_value_.database_id ||
+ (*lhs.reference_value_.database_id ==
+ *rhs.reference_value_.database_id &&
+ lhs.reference_value_.reference < rhs.reference_value_.reference);
case Type::GeoPoint:
return lhs.geo_point_value_ < rhs.geo_point_value_;
case Type::Array:
@@ -343,6 +377,9 @@ void FieldValue::SwitchTo(const Type type) {
case Type::Blob:
blob_value_.~vector();
break;
+ case Type::Reference:
+ reference_value_.~ReferenceValue();
+ break;
case Type::GeoPoint:
geo_point_value_.~GeoPoint();
break;
@@ -370,6 +407,10 @@ void FieldValue::SwitchTo(const Type type) {
// Do not even bother to allocate a new array of size 0.
new (&blob_value_) std::vector<uint8_t>();
break;
+ case Type::Reference:
+ // Qualified name to avoid conflict with the member function of same name.
+ new (&reference_value_) firebase::firestore::model::ReferenceValue();
+ break;
case Type::GeoPoint:
new (&geo_point_value_) GeoPoint();
break;
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h
index 4cd0b3d..e547be3 100644
--- a/Firestore/core/src/firebase/firestore/model/field_value.h
+++ b/Firestore/core/src/firebase/firestore/model/field_value.h
@@ -25,6 +25,8 @@
#include <vector>
#include "Firestore/core/include/firebase/firestore/geo_point.h"
+#include "Firestore/core/src/firebase/firestore/model/database_id.h"
+#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
namespace firebase {
@@ -38,6 +40,12 @@ struct ServerTimestamp {
bool has_previous_value_;
};
+struct ReferenceValue {
+ DocumentKey reference;
+ // Does not own the DatabaseId instance.
+ const DatabaseId* database_id;
+};
+
/**
* tagged-union class representing an immutable data value as stored in
* Firestore. FieldValue represents all the different kinds of values
@@ -69,7 +77,7 @@ class FieldValue {
// position instead, see the doc comment above.
};
- FieldValue() : tag_(Type::Null) {
+ FieldValue() {
}
// Do not inline these ctor/dtor below, which contain call to non-trivial
@@ -103,7 +111,10 @@ class FieldValue {
static FieldValue StringValue(const std::string& value);
static FieldValue StringValue(std::string&& value);
static FieldValue BlobValue(const uint8_t* source, size_t size);
- // static FieldValue ReferenceValue();
+ static FieldValue ReferenceValue(const DocumentKey& value,
+ const DatabaseId* database_id);
+ static FieldValue ReferenceValue(DocumentKey&& value,
+ const DatabaseId* database_id);
static FieldValue GeoPointValue(const GeoPoint& value);
static FieldValue ArrayValue(const std::vector<FieldValue>& value);
static FieldValue ArrayValue(std::vector<FieldValue>&& value);
@@ -123,7 +134,7 @@ class FieldValue {
*/
void SwitchTo(const Type type);
- Type tag_;
+ Type tag_ = Type::Null;
union {
// There is no null type as tag_ alone is enough for Null FieldValue.
bool boolean_value_;
@@ -133,6 +144,8 @@ class FieldValue {
ServerTimestamp server_timestamp_value_;
std::string string_value_;
std::vector<uint8_t> blob_value_;
+ // Qualified name to avoid conflict with the member function of same name.
+ firebase::firestore::model::ReferenceValue reference_value_;
GeoPoint geo_point_value_;
std::vector<FieldValue> array_value_;
std::map<const std::string, const FieldValue> object_value_;
diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.cc b/Firestore/core/src/firebase/firestore/model/maybe_document.cc
new file mode 100644
index 0000000..4f3be1d
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/maybe_document.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+
+#include <utility>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+MaybeDocument::MaybeDocument(DocumentKey key, SnapshotVersion version)
+ : key_(std::move(key)), version_(std::move(version)) {
+}
+
+bool MaybeDocument::Equals(const MaybeDocument& other) const {
+ return type_ == other.type_ && version_ == other.version_ &&
+ key_ == other.key_;
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.h b/Firestore/core/src/firebase/firestore/model/maybe_document.h
new file mode 100644
index 0000000..71bd3ef
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/maybe_document.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_
+
+#include <functional>
+
+#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * The result of a lookup for a given path may be an existing document or a
+ * tombstone that marks the path deleted.
+ */
+class MaybeDocument {
+ public:
+ /**
+ * All the different kinds of documents, including MaybeDocument and its
+ * subclasses. This is used to provide RTTI for documents.
+ */
+ enum class Type {
+ Unknown,
+ Document,
+ NoDocument,
+ };
+
+ MaybeDocument(DocumentKey key, SnapshotVersion version);
+
+ /** The runtime type of this document. */
+ Type type() const {
+ return type_;
+ }
+
+ /** The key for this document. */
+ const DocumentKey& key() const {
+ return key_;
+ }
+
+ /**
+ * Returns the version of this document if it exists or a version at which
+ * this document was guaranteed to not exist.
+ */
+ const SnapshotVersion& version() const {
+ return version_;
+ }
+
+ protected:
+ // Only allow subclass to set their types.
+ void set_type(Type type) {
+ type_ = type;
+ }
+
+ virtual bool Equals(const MaybeDocument& other) const;
+
+ friend bool operator==(const MaybeDocument& lhs, const MaybeDocument& rhs);
+
+ private:
+ Type type_ = Type::Unknown;
+ DocumentKey key_;
+ SnapshotVersion version_;
+};
+
+inline bool operator==(const MaybeDocument& lhs, const MaybeDocument& rhs) {
+ return lhs.Equals(rhs);
+}
+
+inline bool operator!=(const MaybeDocument& lhs, const MaybeDocument& rhs) {
+ return !(lhs == rhs);
+}
+
+/** Compares against another MaybeDocument by keys only. */
+struct DocumentKeyComparator : public std::less<MaybeDocument> {
+ bool operator()(const MaybeDocument& lhs, const MaybeDocument& rhs) const {
+ return lhs.key() < rhs.key();
+ }
+};
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_
diff --git a/Firestore/core/src/firebase/firestore/model/no_document.cc b/Firestore/core/src/firebase/firestore/model/no_document.cc
new file mode 100644
index 0000000..98cb428
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/no_document.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/no_document.h"
+
+#include <utility>
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+NoDocument::NoDocument(DocumentKey key, SnapshotVersion version)
+ : MaybeDocument(std::move(key), std::move(version)) {
+ set_type(Type::NoDocument);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/no_document.h b/Firestore/core/src/firebase/firestore/model/no_document.h
new file mode 100644
index 0000000..7cfd47c
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/no_document.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_
+
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/** Represents that no documents exists for the key at the given version. */
+class NoDocument : public MaybeDocument {
+ public:
+ NoDocument(DocumentKey key, SnapshotVersion version);
+};
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_
diff --git a/Firestore/core/src/firebase/firestore/model/resource_path.cc b/Firestore/core/src/firebase/firestore/model/resource_path.cc
index 36218e9..c95aa63 100644
--- a/Firestore/core/src/firebase/firestore/model/resource_path.cc
+++ b/Firestore/core/src/firebase/firestore/model/resource_path.cc
@@ -18,6 +18,7 @@
#include <algorithm>
#include <utility>
+#include <vector>
#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
#include "absl/strings/str_join.h"
@@ -27,7 +28,7 @@ namespace firebase {
namespace firestore {
namespace model {
-ResourcePath ResourcePath::Parse(const absl::string_view path) {
+ResourcePath ResourcePath::FromString(const absl::string_view path) {
// NOTE: The client is ignorant of any path segments containing escape
// sequences (e.g. __id123__) and just passes them through raw (they exist
// for legacy reasons and should not be used frequently).
diff --git a/Firestore/core/src/firebase/firestore/model/resource_path.h b/Firestore/core/src/firebase/firestore/model/resource_path.h
index 481d32f..53c1951 100644
--- a/Firestore/core/src/firebase/firestore/model/resource_path.h
+++ b/Firestore/core/src/firebase/firestore/model/resource_path.h
@@ -19,6 +19,7 @@
#include <initializer_list>
#include <string>
+#include <utility>
#include "Firestore/core/src/firebase/firestore/model/base_path.h"
#include "absl/strings/string_view.h"
@@ -44,7 +45,7 @@ class ResourcePath : public impl::BasePath<ResourcePath> {
* Creates and returns a new path from the given resource-path string, where
* the path segments are separated by a slash "/".
*/
- static ResourcePath Parse(absl::string_view path);
+ static ResourcePath FromString(absl::string_view path);
/** Returns a standardized string representation of this path. */
std::string CanonicalString() const;
@@ -69,7 +70,7 @@ class ResourcePath : public impl::BasePath<ResourcePath> {
}
private:
- ResourcePath(SegmentsT&& segments) : BasePath{std::move(segments)} {
+ explicit ResourcePath(SegmentsT&& segments) : BasePath{std::move(segments)} {
}
// So that methods of base can construct ResourcePath using the private
@@ -81,4 +82,4 @@ class ResourcePath : public impl::BasePath<ResourcePath> {
} // namespace firestore
} // namespace firebase
-#endif
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_RESOURCE_PATH_H_
diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.cc b/Firestore/core/src/firebase/firestore/model/snapshot_version.cc
new file mode 100644
index 0000000..114e313
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+SnapshotVersion::SnapshotVersion(const Timestamp& timestamp)
+ : timestamp_(timestamp) {
+}
+
+const SnapshotVersion& SnapshotVersion::None() {
+ static const SnapshotVersion kNone(Timestamp{});
+ return kNone;
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.h b/Firestore/core/src/firebase/firestore/model/snapshot_version.h
new file mode 100644
index 0000000..70f6f4a
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_
+
+#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * A version of a document in Firestore. This corresponds to the version
+ * timestamp, such as update_time or read_time.
+ */
+class SnapshotVersion {
+ public:
+ explicit SnapshotVersion(const Timestamp& timestamp);
+
+ const Timestamp& timestamp() const {
+ return timestamp_;
+ }
+
+ /** Creates a new version that is smaller than all other versions. */
+ static const SnapshotVersion& None();
+
+ private:
+ Timestamp timestamp_;
+};
+
+/** Compares against another SnapshotVersion. */
+inline bool operator<(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() < rhs.timestamp();
+}
+
+inline bool operator>(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() > rhs.timestamp();
+}
+
+inline bool operator>=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() >= rhs.timestamp();
+}
+
+inline bool operator<=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() <= rhs.timestamp();
+}
+
+inline bool operator!=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() != rhs.timestamp();
+}
+
+inline bool operator==(const SnapshotVersion& lhs, const SnapshotVersion& rhs) {
+ return lhs.timestamp() == rhs.timestamp();
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_
diff --git a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt
index a218e3b..7f528fb 100644
--- a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt
@@ -20,6 +20,8 @@ cc_library(
serializer.h
serializer.cc
DEPENDS
+ firebase_firestore_model
+ firebase_firestore_protos_nanopb
grpc::grpc
nanopb
)
diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc
index d3cdd3f..e503d09 100644
--- a/Firestore/core/src/firebase/firestore/remote/serializer.cc
+++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc
@@ -16,16 +16,94 @@
#include "Firestore/core/src/firebase/firestore/remote/serializer.h"
-// TODO(rsgowman): These are (currently!) unnecessary includes. Adding for now
-// to ensure we can find nanopb's generated header files.
-#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h"
-#include "Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h"
+#include <pb_decode.h>
+#include <pb_encode.h>
namespace firebase {
namespace firestore {
namespace remote {
-Serializer::Serializer() {
+using firebase::firestore::model::FieldValue;
+
+Serializer::TypedValue Serializer::EncodeFieldValue(
+ const FieldValue& field_value) {
+ Serializer::TypedValue proto_value{
+ field_value.type(), google_firestore_v1beta1_Value_init_default};
+ switch (field_value.type()) {
+ case FieldValue::Type::Null:
+ proto_value.value.null_value = google_protobuf_NullValue_NULL_VALUE;
+ break;
+ default:
+ // TODO(rsgowman): implement the other types
+ abort();
+ }
+ return proto_value;
+}
+
+void Serializer::EncodeTypedValue(const TypedValue& value,
+ std::vector<uint8_t>* out_bytes) {
+ bool status;
+ // TODO(rsgowman): how large should the output buffer be? Do some
+ // investigation to see if we can get nanopb to tell us how much space it's
+ // going to need.
+ uint8_t buf[1024];
+ pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
+ switch (value.type) {
+ case FieldValue::Type::Null:
+ status = pb_encode_tag(&stream, PB_WT_VARINT,
+ google_firestore_v1beta1_Value_null_value_tag);
+ if (!status) {
+ // TODO(rsgowman): figure out error handling
+ abort();
+ }
+
+ status = pb_encode_varint(&stream, value.value.null_value);
+ if (!status) {
+ // TODO(rsgowman): figure out error handling
+ abort();
+ }
+
+ out_bytes->insert(out_bytes->end(), buf, buf + stream.bytes_written);
+
+ break;
+
+ default:
+ // TODO(rsgowman): implement the other types
+ abort();
+ }
+}
+
+FieldValue Serializer::DecodeFieldValue(
+ const Serializer::TypedValue& value_proto) {
+ switch (value_proto.type) {
+ case FieldValue::Type::Null:
+ return FieldValue::NullValue();
+ default:
+ // TODO(rsgowman): implement the other types
+ abort();
+ }
+}
+
+Serializer::TypedValue Serializer::DecodeTypedValue(const uint8_t* bytes,
+ size_t length) {
+ pb_istream_t stream = pb_istream_from_buffer(bytes, length);
+ pb_wire_type_t wire_type;
+ uint32_t tag;
+ bool eof;
+ bool status = pb_decode_tag(&stream, &wire_type, &tag, &eof);
+ if (!status || wire_type != PB_WT_VARINT) {
+ // TODO(rsgowman): figure out error handling
+ abort();
+ }
+
+ switch (tag) {
+ case google_firestore_v1beta1_Value_null_value_tag:
+ return Serializer::TypedValue{
+ FieldValue::Type::Null, google_firestore_v1beta1_Value_init_default};
+ default:
+ // TODO(rsgowman): figure out error handling
+ abort();
+ }
}
} // namespace remote
diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h
index 4dc6b9e..518cff4 100644
--- a/Firestore/core/src/firebase/firestore/remote/serializer.h
+++ b/Firestore/core/src/firebase/firestore/remote/serializer.h
@@ -17,16 +17,26 @@
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_
+#include <stdint.h>
+#include <stdlib.h>
+#include <vector>
+
+#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h"
+#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
namespace firebase {
namespace firestore {
namespace remote {
/**
* @brief Converts internal model objects to their equivalent protocol buffer
- * form.
+ * form, and protocol buffer objects to their equivalent bytes.
*
- * Methods starting with "Encode" convert to a protocol buffer and methods
- * starting with "Decode" convert from a protocol buffer.
+ * Methods starting with "Encode" either convert from a model object to a
+ * protocol buffer or from a protocol buffer to bytes, and methods starting with
+ * "Decode" either convert from a protocol buffer to a model object or from
+ * bytes to a protocol buffer.
*
*/
// TODO(rsgowman): Original docs also has this: "Throws an exception if a
@@ -34,10 +44,102 @@ namespace remote {
// interpret." Adjust for C++.
class Serializer {
public:
- // TODO(rsgowman): Adjust ctor to accept a DatabaseID... once that exists.
- Serializer(/*DatabaseID databaseId*/);
+ /**
+ * @brief Wraps (nanopb) google_firestore_v1beta1_Value with type information.
+ */
+ struct TypedValue {
+ firebase::firestore::model::FieldValue::Type type;
+ google_firestore_v1beta1_Value value;
+ };
+
+ Serializer() {
+ }
+ // TODO(rsgowman): We eventually need the DatabaseId, but can't add it just
+ // yet since it's not used yet (which travis complains about). So for now,
+ // we'll create a parameterless ctor (above) that likely won't exist in the
+ // final version of this class.
+ ///**
+ // * @param database_id Must remain valid for the lifetime of this Serializer
+ // * object.
+ // */
+ // explicit Serializer(const firebase::firestore::model::DatabaseId&
+ // database_id)
+ // : database_id_(database_id) {
+ //}
+
+ /**
+ * Converts the FieldValue model passed into the Value proto equivalent.
+ *
+ * @param field_value the model to convert.
+ * @return the proto representation of the model.
+ */
+ static Serializer::TypedValue EncodeFieldValue(
+ const firebase::firestore::model::FieldValue& field_value);
+
+ /**
+ * @brief Converts the value proto passed into bytes.
+ *
+ * @param[out] out_bytes A buffer to place the output. The bytes will be
+ * appended to this vector.
+ */
+ // TODO(rsgowman): error handling, incl return code.
+ static void EncodeTypedValue(const TypedValue& value,
+ std::vector<uint8_t>* out_bytes);
+
+ /**
+ * Converts from the proto Value format to the model FieldValue format
+ *
+ * @return The model equivalent of the proto data.
+ */
+ static firebase::firestore::model::FieldValue DecodeFieldValue(
+ const Serializer::TypedValue& value_proto);
+
+ /**
+ * @brief Converts from bytes to the nanopb proto.
+ *
+ * @param bytes The bytes to convert. It's assumed that exactly all of the
+ * bytes will be used by this conversion.
+ * @return The (nanopb) proto equivalent of the bytes.
+ */
+ // TODO(rsgowman): error handling.
+ static TypedValue DecodeTypedValue(const uint8_t* bytes, size_t length);
+
+ /**
+ * @brief Converts from bytes to the nanopb proto.
+ *
+ * @param bytes The bytes to convert. It's assumed that exactly all of the
+ * bytes will be used by this conversion.
+ * @return The (nanopb) proto equivalent of the bytes.
+ */
+ // TODO(rsgowman): error handling.
+ static TypedValue DecodeTypedValue(const std::vector<uint8_t>& bytes) {
+ return DecodeTypedValue(bytes.data(), bytes.size());
+ }
+
+ private:
+ // TODO(rsgowman): We don't need the database_id_ yet (but will eventually).
+ // const firebase::firestore::model::DatabaseId& database_id_;
};
+inline bool operator==(const Serializer::TypedValue& lhs,
+ const Serializer::TypedValue& rhs) {
+ if (lhs.type != rhs.type) {
+ return false;
+ }
+
+ switch (lhs.type) {
+ case firebase::firestore::model::FieldValue::Type::Null:
+ FIREBASE_DEV_ASSERT(lhs.value.null_value ==
+ google_protobuf_NullValue_NULL_VALUE);
+ FIREBASE_DEV_ASSERT(rhs.value.null_value ==
+ google_protobuf_NullValue_NULL_VALUE);
+ return true;
+ default:
+ // TODO(rsgowman): implement the other types
+ abort();
+ }
+}
+
} // namespace remote
} // namespace firestore
} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h
index 993f27a..76768e6 100644
--- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h
+++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.h
@@ -91,6 +91,17 @@
FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, __VA_ARGS__)
#endif // defined(NDEBUG)
+// Assert expression is true otherwise display the specified message and
+// abort.
+#define FIREBASE_ASSERT_MESSAGE(expression, ...) \
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, __VA_ARGS__)
+
+// Assert expression is true otherwise display the specified message and
+// abort. Compiled out of release builds.
+#define FIREBASE_DEV_ASSERT_MESSAGE(expression, ...) \
+ FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, \
+ __VA_ARGS__)
+
namespace firebase {
namespace firestore {
namespace util {
diff --git a/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt b/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt
new file mode 100644
index 0000000..f470bd7
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Copyright 2018 Google
+#
+# 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.
+
+cc_test(
+ firebase_firestore_auth_test
+ SOURCES
+ credentials_provider_test.cc
+ empty_credentials_provider_test.cc
+ token_test.cc
+ user_test.cc
+ DEPENDS
+ firebase_firestore_auth
+)
+
+if(APPLE)
+ cc_test(
+ firebase_firestore_auth_apple_test
+ SOURCES
+ firebase_credentials_provider_test.mm
+ DEPENDS
+ firebase_firestore_auth_apple
+ )
+endif(APPLE)
diff --git a/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc
new file mode 100644
index 0000000..1748422
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+#define UNUSED(x) (void)(x)
+
+TEST(CredentialsProvider, Typedef) {
+ TokenListener token_listener = [](const Token& token,
+ const absl::string_view error) {
+ UNUSED(token);
+ UNUSED(error);
+ };
+ EXPECT_NE(nullptr, token_listener);
+ EXPECT_TRUE(token_listener);
+
+ token_listener = nullptr;
+ EXPECT_EQ(nullptr, token_listener);
+ EXPECT_FALSE(token_listener);
+
+ UserChangeListener user_change_listener = [](const User& user) {
+ UNUSED(user);
+ };
+ EXPECT_NE(nullptr, user_change_listener);
+ EXPECT_TRUE(user_change_listener);
+
+ user_change_listener = nullptr;
+ EXPECT_EQ(nullptr, user_change_listener);
+ EXPECT_FALSE(user_change_listener);
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc
new file mode 100644
index 0000000..123f952
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+TEST(EmptyCredentialsProvider, GetToken) {
+ EmptyCredentialsProvider credentials_provider;
+ credentials_provider.GetToken(
+ /*force_refresh=*/true,
+ [](const Token& token, const absl::string_view error) {
+ EXPECT_EQ("", token.token());
+ const User& user = token.user();
+ EXPECT_EQ("", user.uid());
+ EXPECT_FALSE(user.is_authenticated());
+ EXPECT_EQ("", error);
+ });
+}
+
+TEST(EmptyCredentialsProvider, SetListener) {
+ EmptyCredentialsProvider credentials_provider;
+ credentials_provider.SetUserChangeListener([](const User& user) {
+ EXPECT_EQ("", user.uid());
+ EXPECT_FALSE(user.is_authenticated());
+ });
+
+ credentials_provider.SetUserChangeListener(nullptr);
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm
new file mode 100644
index 0000000..8d2b361
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h"
+
+#import <FirebaseCore/FIRApp.h>
+#import <FirebaseCore/FIRAppInternal.h>
+#import <FirebaseCore/FIROptionsInternal.h>
+
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+// TODO(zxu123): Make this an integration test and get infos from environment.
+// Set a .plist file here to enable the test-case.
+static NSString* const kPlist = @"";
+
+class FirebaseCredentialsProviderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ app_ready_ = false;
+ if (![kPlist hasSuffix:@".plist"]) {
+ return;
+ }
+
+ static dispatch_once_t once_token;
+ dispatch_once(&once_token, ^{
+ FIROptions* options = [[FIROptions alloc] initWithContentsOfFile:kPlist];
+ [FIRApp configureWithOptions:options];
+ });
+
+ // Set getUID implementation.
+ FIRApp* default_app = [FIRApp defaultApp];
+ default_app.getUIDImplementation = ^NSString* {
+ return @"I'm a fake uid.";
+ };
+ app_ready_ = true;
+ }
+
+ bool app_ready_;
+};
+
+// Set kPlist above before enable.
+TEST_F(FirebaseCredentialsProviderTest, GetToken) {
+ if (!app_ready_) {
+ return;
+ }
+
+ FirebaseCredentialsProvider credentials_provider([FIRApp defaultApp]);
+ credentials_provider.GetToken(
+ /*force_refresh=*/true,
+ [](const Token& token, const absl::string_view error) {
+ EXPECT_EQ("", token.token());
+ const User& user = token.user();
+ EXPECT_EQ("I'm a fake uid.", user.uid());
+ EXPECT_TRUE(user.is_authenticated());
+ EXPECT_EQ("", error) << error;
+ });
+}
+
+// Set kPlist above before enable.
+TEST_F(FirebaseCredentialsProviderTest, SetListener) {
+ if (!app_ready_) {
+ return;
+ }
+
+ FirebaseCredentialsProvider credentials_provider([FIRApp defaultApp]);
+ credentials_provider.SetUserChangeListener([](const User& user) {
+ EXPECT_EQ("I'm a fake uid.", user.uid());
+ EXPECT_TRUE(user.is_authenticated());
+ });
+
+ // TODO(wilhuff): We should wait for the above expectations to actually happen
+ // before continuing.
+
+ credentials_provider.SetUserChangeListener(nullptr);
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/auth/token_test.cc b/Firestore/core/test/firebase/firestore/auth/token_test.cc
new file mode 100644
index 0000000..a0f2c48
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/token_test.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/token.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+TEST(Token, Getter) {
+ Token token("token", User("abc"));
+ EXPECT_EQ("token", token.token());
+ EXPECT_EQ(User("abc"), token.user());
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/auth/user_test.cc b/Firestore/core/test/firebase/firestore/auth/user_test.cc
new file mode 100644
index 0000000..a9f764d
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/auth/user_test.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/auth/user.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace auth {
+
+TEST(User, Getter) {
+ User anonymous;
+ EXPECT_EQ("", anonymous.uid());
+ EXPECT_FALSE(anonymous.is_authenticated());
+
+ User signin("abc");
+ EXPECT_EQ("abc", signin.uid());
+ EXPECT_TRUE(signin.is_authenticated());
+
+ User copy;
+ copy = signin;
+ EXPECT_EQ(signin, copy);
+}
+
+TEST(User, Unauthenticated) {
+ User unauthenticated = User::Unauthenticated();
+ EXPECT_EQ("", unauthenticated.uid());
+ EXPECT_FALSE(unauthenticated.is_authenticated());
+}
+
+TEST(User, Comparison) {
+ EXPECT_EQ(User(), User());
+ EXPECT_EQ(User("abc"), User("abc"));
+ EXPECT_NE(User(), User("abc"));
+ EXPECT_NE(User("abc"), User("xyz"));
+}
+
+} // namespace auth
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
index 63ed813..0d581bc 100644
--- a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
@@ -16,10 +16,15 @@ cc_test(
firebase_firestore_model_test
SOURCES
database_id_test.cc
+ document_key_test.cc
+ document_test.cc
field_path_test.cc
field_value_test.cc
- timestamp_test.cc
+ maybe_document_test.cc
+ no_document_test.cc
resource_path_test.cc
+ snapshot_version_test.cc
+ timestamp_test.cc
DEPENDS
firebase_firestore_model
)
diff --git a/Firestore/core/test/firebase/firestore/model/document_key_test.cc b/Firestore/core/test/firebase/firestore/model/document_key_test.cc
new file mode 100644
index 0000000..0e0df2d
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/document_key_test.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+TEST(DocumentKey, Constructor_Empty) {
+ const DocumentKey default_key;
+ EXPECT_TRUE(default_key.path().empty());
+
+ const auto& empty_key = DocumentKey::Empty();
+ const auto& another_empty_key = DocumentKey::Empty();
+ EXPECT_EQ(default_key, empty_key);
+ EXPECT_EQ(empty_key, another_empty_key);
+ EXPECT_EQ(&empty_key, &another_empty_key);
+}
+
+TEST(DocumentKey, Constructor_FromPath) {
+ ResourcePath path{"rooms", "firestore", "messages", "1"};
+ const DocumentKey key_from_path_copy{path};
+ // path shouldn't have been moved from.
+ EXPECT_FALSE(path.empty());
+ EXPECT_EQ(key_from_path_copy.path(), path);
+
+ const DocumentKey key_from_moved_path{std::move(path)};
+ EXPECT_TRUE(path.empty());
+ EXPECT_FALSE(key_from_moved_path.path().empty());
+ EXPECT_EQ(key_from_path_copy.path(), key_from_moved_path.path());
+}
+
+TEST(DocumentKey, CopyAndMove) {
+ DocumentKey key({"rooms", "firestore", "messages", "1"});
+ const std::string path_string = "rooms/firestore/messages/1";
+ EXPECT_EQ(path_string, key.path().CanonicalString());
+
+ DocumentKey copied = key;
+ EXPECT_EQ(path_string, copied.path().CanonicalString());
+ EXPECT_EQ(key, copied);
+
+ const DocumentKey moved = std::move(key);
+ EXPECT_EQ(path_string, moved.path().CanonicalString());
+ EXPECT_NE(key, moved);
+ EXPECT_TRUE(key.path().empty());
+
+ // Reassignment.
+
+ key = copied;
+ EXPECT_EQ(copied, key);
+ EXPECT_EQ(path_string, key.path().CanonicalString());
+
+ key = {};
+ EXPECT_TRUE(key.path().empty());
+ key = std::move(copied);
+ EXPECT_NE(copied, key);
+ EXPECT_TRUE(copied.path().empty());
+ EXPECT_EQ(path_string, key.path().CanonicalString());
+}
+
+TEST(DocumentKey, Constructor_StaticFactory) {
+ const auto key_from_segments =
+ DocumentKey::FromSegments({"rooms", "firestore", "messages", "1"});
+ const std::string path_string = "rooms/firestore/messages/1";
+ const auto key_from_string = DocumentKey::FromPathString(path_string);
+ EXPECT_EQ(path_string, key_from_string.path().CanonicalString());
+ EXPECT_EQ(path_string, key_from_segments.path().CanonicalString());
+ EXPECT_EQ(key_from_segments, key_from_string);
+
+ const auto from_empty_path = DocumentKey::FromPathString("");
+ EXPECT_EQ(from_empty_path, DocumentKey{});
+}
+
+TEST(DocumentKey, Constructor_BadArguments) {
+ ASSERT_ANY_THROW(DocumentKey(ResourcePath{"foo"}));
+ ASSERT_ANY_THROW(DocumentKey(ResourcePath{"foo", "bar", "baz"}));
+
+ ASSERT_ANY_THROW(DocumentKey::FromSegments({"foo"}));
+ ASSERT_ANY_THROW(DocumentKey::FromSegments({"foo", "bar", "baz"}));
+
+ ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid"));
+ ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid//string"));
+ ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid/key/path"));
+}
+
+TEST(DocumentKey, IsDocumentKey) {
+ EXPECT_TRUE(DocumentKey::IsDocumentKey({}));
+ EXPECT_FALSE(DocumentKey::IsDocumentKey({"foo"}));
+ EXPECT_TRUE(DocumentKey::IsDocumentKey({"foo", "bar"}));
+ EXPECT_FALSE(DocumentKey::IsDocumentKey({"foo", "bar", "baz"}));
+}
+
+TEST(DocumentKey, Comparison) {
+ const DocumentKey abcd({"a", "b", "c", "d"});
+ const DocumentKey abcd_too({"a", "b", "c", "d"});
+ const DocumentKey xyzw({"x", "y", "z", "w"});
+ EXPECT_EQ(abcd, abcd_too);
+ EXPECT_NE(abcd, xyzw);
+
+ const DocumentKey empty;
+ const DocumentKey a({"a", "a"});
+ const DocumentKey b({"b", "b"});
+ const DocumentKey ab({"a", "a", "b", "b"});
+
+ EXPECT_FALSE(empty < empty);
+ EXPECT_TRUE(empty <= empty);
+ EXPECT_TRUE(empty < a);
+ EXPECT_TRUE(empty <= a);
+ EXPECT_TRUE(a > empty);
+ EXPECT_TRUE(a >= empty);
+
+ EXPECT_FALSE(a < a);
+ EXPECT_TRUE(a <= a);
+ EXPECT_FALSE(a > a);
+ EXPECT_TRUE(a >= a);
+ EXPECT_TRUE(a == a);
+ EXPECT_FALSE(a != a);
+
+ EXPECT_TRUE(a < b);
+ EXPECT_TRUE(a <= b);
+ EXPECT_TRUE(b > a);
+ EXPECT_TRUE(b >= a);
+
+ EXPECT_TRUE(a < ab);
+ EXPECT_TRUE(a <= ab);
+ EXPECT_TRUE(ab > a);
+ EXPECT_TRUE(ab >= a);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/document_test.cc b/Firestore/core/test/firebase/firestore/model/document_test.cc
new file mode 100644
index 0000000..6b72360
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/document_test.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/document.h"
+
+#include "absl/strings/string_view.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+namespace {
+
+inline Document MakeDocument(const absl::string_view data,
+ const absl::string_view path,
+ const Timestamp& timestamp,
+ bool has_local_mutations) {
+ return Document(FieldValue::ObjectValue(
+ {{"field", FieldValue::StringValue(data.data())}}),
+ DocumentKey::FromPathString(path.data()),
+ SnapshotVersion(timestamp), has_local_mutations);
+}
+
+} // anonymous namespace
+
+TEST(Document, Getter) {
+ const Document& doc =
+ MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true);
+ EXPECT_EQ(MaybeDocument::Type::Document, doc.type());
+ EXPECT_EQ(
+ FieldValue::ObjectValue({{"field", FieldValue::StringValue("foo")}}),
+ doc.data());
+ EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key());
+ EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version());
+ EXPECT_TRUE(doc.has_local_mutations());
+}
+
+TEST(Document, Comparison) {
+ EXPECT_EQ(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true),
+ MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true));
+ EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true),
+ MakeDocument("bar", "i/am/a/path", Timestamp(123, 456), true));
+ EXPECT_NE(
+ MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true),
+ MakeDocument("foo", "i/am/another/path", Timestamp(123, 456), true));
+ EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true),
+ MakeDocument("foo", "i/am/a/path", Timestamp(456, 123), true));
+ EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true),
+ MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), false));
+
+ // Document and MaybeDocument will not equal. In particular, Document and
+ // NoDocument will not equal, which I won't test here.
+ EXPECT_NE(Document(FieldValue::ObjectValue({}),
+ DocumentKey::FromPathString("same/path"),
+ SnapshotVersion(Timestamp()), false),
+ MaybeDocument(DocumentKey::FromPathString("same/path"),
+ SnapshotVersion(Timestamp())));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/field_path_test.cc b/Firestore/core/test/firebase/firestore/model/field_path_test.cc
index 7c7e0a3..a5ae3b2 100644
--- a/Firestore/core/test/firebase/firestore/model/field_path_test.cc
+++ b/Firestore/core/test/firebase/firestore/model/field_path_test.cc
@@ -29,18 +29,18 @@ namespace model {
TEST(FieldPath, Constructors) {
const FieldPath empty_path;
EXPECT_TRUE(empty_path.empty());
- EXPECT_EQ(0, empty_path.size());
+ EXPECT_EQ(0u, empty_path.size());
EXPECT_TRUE(empty_path.begin() == empty_path.end());
const FieldPath path_from_list = {"rooms", "Eros", "messages"};
EXPECT_FALSE(path_from_list.empty());
- EXPECT_EQ(3, path_from_list.size());
+ EXPECT_EQ(3u, path_from_list.size());
EXPECT_TRUE(path_from_list.begin() + 3 == path_from_list.end());
std::vector<std::string> segments{"rooms", "Eros", "messages"};
const FieldPath path_from_segments{segments.begin(), segments.end()};
EXPECT_FALSE(path_from_segments.empty());
- EXPECT_EQ(3, path_from_segments.size());
+ EXPECT_EQ(3u, path_from_segments.size());
EXPECT_TRUE(path_from_segments.begin() + 3 == path_from_segments.end());
FieldPath copied = path_from_list;
@@ -168,13 +168,13 @@ TEST(FieldPath, IsPrefixOf) {
TEST(FieldPath, AccessFailures) {
const FieldPath path;
- ASSERT_DEATH_IF_SUPPORTED(path.first_segment(), "");
- ASSERT_DEATH_IF_SUPPORTED(path.last_segment(), "");
- ASSERT_DEATH_IF_SUPPORTED(path[0], "");
- ASSERT_DEATH_IF_SUPPORTED(path[1], "");
- ASSERT_DEATH_IF_SUPPORTED(path.PopFirst(), "");
- ASSERT_DEATH_IF_SUPPORTED(path.PopFirst(2), "");
- ASSERT_DEATH_IF_SUPPORTED(path.PopLast(), "");
+ ASSERT_ANY_THROW(path.first_segment());
+ ASSERT_ANY_THROW(path.last_segment());
+ ASSERT_ANY_THROW(path[0]);
+ ASSERT_ANY_THROW(path[1]);
+ ASSERT_ANY_THROW(path.PopFirst());
+ ASSERT_ANY_THROW(path.PopFirst(2));
+ ASSERT_ANY_THROW(path.PopLast());
}
TEST(FieldPath, Parsing) {
@@ -201,7 +201,7 @@ TEST(FieldPath, Parsing) {
const auto path_with_dot = FieldPath::FromServerFormat(R"(foo\.bar)");
EXPECT_EQ(path_with_dot.CanonicalString(), "`foo.bar`");
- EXPECT_EQ(path_with_dot.size(), 1);
+ EXPECT_EQ(path_with_dot.size(), 1u);
}
// This is a special case in C++: std::string may contain embedded nulls. To
@@ -213,22 +213,22 @@ TEST(FieldPath, ParseEmbeddedNull) {
str += ".bar";
const auto path = FieldPath::FromServerFormat(str);
- EXPECT_EQ(path.size(), 1);
+ EXPECT_EQ(path.size(), 1u);
EXPECT_EQ(path.CanonicalString(), "foo");
}
TEST(FieldPath, ParseFailures) {
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat(""), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("."), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat(".."), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("foo."), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat(".bar"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("foo..bar"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat(R"(foo\)"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat(R"(foo.\)"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("foo`"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("foo```"), "");
- ASSERT_DEATH_IF_SUPPORTED(FieldPath::FromServerFormat("`foo"), "");
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat(""));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("."));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat(".."));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo."));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat(".bar"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo..bar"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat(R"(foo\)"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat(R"(foo.\)"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo`"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo```"));
+ ASSERT_ANY_THROW(FieldPath::FromServerFormat("`foo"));
}
TEST(FieldPath, CanonicalStringOfSubstring) {
diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
index 702c0f6..86eb804 100644
--- a/Firestore/core/test/firebase/firestore/model/field_value_test.cc
+++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
@@ -144,6 +144,20 @@ TEST(FieldValue, BlobType) {
EXPECT_FALSE(a < a);
}
+TEST(FieldValue, ReferenceType) {
+ const DatabaseId id("project", "database");
+ const FieldValue a =
+ FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), &id);
+ DocumentKey key = DocumentKey::FromPathString("root/def");
+ const FieldValue b = FieldValue::ReferenceValue(key, &id);
+ const FieldValue c = FieldValue::ReferenceValue(std::move(key), &id);
+ EXPECT_EQ(Type::Reference, a.type());
+ EXPECT_EQ(Type::Reference, b.type());
+ EXPECT_EQ(Type::Reference, c.type());
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(a < a);
+}
+
TEST(FieldValue, GeoPointType) {
const FieldValue a = FieldValue::GeoPointValue({1, 2});
const FieldValue b = FieldValue::GeoPointValue({3, 4});
@@ -280,6 +294,23 @@ TEST(FieldValue, Copy) {
clone = null_value;
EXPECT_EQ(FieldValue::NullValue(), clone);
+ const DatabaseId database_id("project", "database");
+ const FieldValue reference_value = FieldValue::ReferenceValue(
+ DocumentKey::FromPathString("root/abc"), &database_id);
+ clone = reference_value;
+ EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"),
+ &database_id),
+ clone);
+ EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"),
+ &database_id),
+ reference_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"),
+ &database_id),
+ clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
clone = geo_point_value;
EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone);
@@ -361,7 +392,7 @@ TEST(FieldValue, Move) {
clone = FieldValue::NullValue();
EXPECT_EQ(FieldValue::NullValue(), clone);
- const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
+ FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
clone = std::move(timestamp_value);
EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone);
clone = FieldValue::NullValue();
@@ -373,13 +404,23 @@ TEST(FieldValue, Move) {
clone = FieldValue::NullValue();
EXPECT_EQ(FieldValue::NullValue(), clone);
- const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
+ FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
clone = std::move(blob_value);
EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone);
clone = FieldValue::NullValue();
EXPECT_EQ(FieldValue::NullValue(), clone);
- const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
+ const DatabaseId database_id("project", "database");
+ FieldValue reference_value = FieldValue::ReferenceValue(
+ DocumentKey::FromPathString("root/abc"), &database_id);
+ clone = std::move(reference_value);
+ EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"),
+ &database_id),
+ clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
clone = std::move(geo_point_value);
EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone);
clone = null_value;
@@ -415,6 +456,9 @@ TEST(FieldValue, CompareMixedType) {
const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
const FieldValue string_value = FieldValue::StringValue("abc");
const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
+ const DatabaseId database_id("project", "database");
+ const FieldValue reference_value = FieldValue::ReferenceValue(
+ DocumentKey::FromPathString("root/abc"), &database_id);
const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
const FieldValue array_value =
FieldValue::ArrayValue(std::vector<FieldValue>());
@@ -425,7 +469,8 @@ TEST(FieldValue, CompareMixedType) {
EXPECT_TRUE(number_value < timestamp_value);
EXPECT_TRUE(timestamp_value < string_value);
EXPECT_TRUE(string_value < blob_value);
- EXPECT_TRUE(blob_value < geo_point_value);
+ EXPECT_TRUE(blob_value < reference_value);
+ EXPECT_TRUE(reference_value < geo_point_value);
EXPECT_TRUE(geo_point_value < array_value);
EXPECT_TRUE(array_value < object_value);
}
diff --git a/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc b/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc
new file mode 100644
index 0000000..005798a
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+
+#include "absl/strings/string_view.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+namespace {
+
+inline MaybeDocument MakeMaybeDocument(const absl::string_view path,
+ const Timestamp& timestamp) {
+ return MaybeDocument(DocumentKey::FromPathString(path.data()),
+ SnapshotVersion(timestamp));
+}
+
+inline bool operator<(const MaybeDocument& lhs, const MaybeDocument& rhs) {
+ static const DocumentKeyComparator less;
+ return less(lhs, rhs);
+}
+
+} // anonymous namespace
+
+TEST(MaybeDocument, Getter) {
+ const MaybeDocument& doc =
+ MakeMaybeDocument("i/am/a/path", Timestamp(123, 456));
+ EXPECT_EQ(MaybeDocument::Type::Unknown, doc.type());
+ EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key());
+ EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version());
+}
+
+TEST(MaybeDocument, Comparison) {
+ EXPECT_TRUE(MakeMaybeDocument("root/123", Timestamp(456, 123)) <
+ MakeMaybeDocument("root/456", Timestamp(123, 456)));
+ // MaybeDocument comparision is purely key-based.
+ EXPECT_FALSE(MakeMaybeDocument("root/123", Timestamp(111, 111)) <
+ MakeMaybeDocument("root/123", Timestamp(222, 222)));
+
+ EXPECT_EQ(MakeMaybeDocument("root/123", Timestamp(456, 123)),
+ MakeMaybeDocument("root/123", Timestamp(456, 123)));
+ EXPECT_NE(MakeMaybeDocument("root/123", Timestamp(456, 123)),
+ MakeMaybeDocument("root/456", Timestamp(456, 123)));
+ EXPECT_NE(MakeMaybeDocument("root/123", Timestamp(456, 123)),
+ MakeMaybeDocument("root/123", Timestamp(123, 456)));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/no_document_test.cc b/Firestore/core/test/firebase/firestore/model/no_document_test.cc
new file mode 100644
index 0000000..825820f
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/no_document_test.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/no_document.h"
+
+#include "absl/strings/string_view.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+namespace {
+
+inline NoDocument MakeNoDocument(const absl::string_view path,
+ const Timestamp& timestamp) {
+ return NoDocument(DocumentKey::FromPathString(path.data()),
+ SnapshotVersion(timestamp));
+}
+
+} // anonymous namespace
+
+TEST(NoDocument, Getter) {
+ const NoDocument& doc = MakeNoDocument("i/am/a/path", Timestamp(123, 456));
+ EXPECT_EQ(MaybeDocument::Type::NoDocument, doc.type());
+ EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key());
+ EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version());
+
+ // NoDocument and MaybeDocument will not equal.
+ EXPECT_NE(NoDocument(DocumentKey::FromPathString("same/path"),
+ SnapshotVersion(Timestamp())),
+ MaybeDocument(DocumentKey::FromPathString("same/path"),
+ SnapshotVersion(Timestamp())));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/resource_path_test.cc b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc
index 317a1db..637e78e 100644
--- a/Firestore/core/test/firebase/firestore/model/resource_path_test.cc
+++ b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc
@@ -29,18 +29,18 @@ namespace model {
TEST(ResourcePath, Constructor) {
const ResourcePath empty_path;
EXPECT_TRUE(empty_path.empty());
- EXPECT_EQ(0, empty_path.size());
+ EXPECT_EQ(0u, empty_path.size());
EXPECT_TRUE(empty_path.begin() == empty_path.end());
const ResourcePath path_from_list{{"rooms", "Eros", "messages"}};
EXPECT_FALSE(path_from_list.empty());
- EXPECT_EQ(3, path_from_list.size());
+ EXPECT_EQ(3u, path_from_list.size());
EXPECT_TRUE(path_from_list.begin() + 3 == path_from_list.end());
std::vector<std::string> segments{"rooms", "Eros", "messages"};
const ResourcePath path_from_segments{segments.begin(), segments.end()};
EXPECT_FALSE(path_from_segments.empty());
- EXPECT_EQ(3, path_from_segments.size());
+ EXPECT_EQ(3u, path_from_segments.size());
EXPECT_TRUE(path_from_segments.begin() + 3 == path_from_segments.end());
ResourcePath copied = path_from_list;
@@ -74,7 +74,7 @@ TEST(ResourcePath, Comparison) {
TEST(ResourcePath, Parsing) {
const auto parse = [](const std::pair<std::string, size_t> expected) {
- const auto path = ResourcePath::Parse(expected.first);
+ const auto path = ResourcePath::FromString(expected.first);
return std::make_pair(path.CanonicalString(), path.size());
};
const auto make_expected = [](const std::string& str, const size_t size) {
@@ -92,12 +92,12 @@ TEST(ResourcePath, Parsing) {
expected = make_expected(R"(foo/__!?#@..`..\`/baz)", 3);
EXPECT_EQ(expected, parse(expected));
- EXPECT_EQ(ResourcePath::Parse("/foo/").CanonicalString(), "foo");
+ EXPECT_EQ(ResourcePath::FromString("/foo/").CanonicalString(), "foo");
}
TEST(ResourcePath, ParseFailures) {
- ASSERT_DEATH_IF_SUPPORTED(ResourcePath::Parse("//"), "");
- ASSERT_DEATH_IF_SUPPORTED(ResourcePath::Parse("foo//bar"), "");
+ ASSERT_ANY_THROW(ResourcePath::FromString("//"));
+ ASSERT_ANY_THROW(ResourcePath::FromString("foo//bar"));
}
} // namespace model
diff --git a/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc b/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc
new file mode 100644
index 0000000..e359f84
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 Google
+ *
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+TEST(SnapshotVersion, Getter) {
+ SnapshotVersion version(Timestamp(123, 456));
+ EXPECT_EQ(Timestamp(123, 456), version.timestamp());
+
+ const SnapshotVersion& no_version = SnapshotVersion::None();
+ EXPECT_EQ(Timestamp(), no_version.timestamp());
+}
+
+TEST(SnapshotVersion, Comparison) {
+ EXPECT_LT(SnapshotVersion::None(), SnapshotVersion(Timestamp(123, 456)));
+
+ EXPECT_LT(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(456, 123)));
+ EXPECT_GT(SnapshotVersion(Timestamp(456, 123)),
+ SnapshotVersion(Timestamp(123, 456)));
+ EXPECT_LE(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(456, 123)));
+ EXPECT_LE(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(123, 456)));
+ EXPECT_GE(SnapshotVersion(Timestamp(456, 123)),
+ SnapshotVersion(Timestamp(123, 456)));
+ EXPECT_GE(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(123, 456)));
+ EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(123, 456)));
+ EXPECT_NE(SnapshotVersion(Timestamp(123, 456)),
+ SnapshotVersion(Timestamp(456, 123)));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
index 1be5a87..35f417e 100644
--- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
+++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc
@@ -16,8 +16,15 @@
#include "Firestore/core/src/firebase/firestore/remote/serializer.h"
-#include <gtest/gtest.h>
+#include <pb.h>
#include <pb_encode.h>
+#include <vector>
+
+#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+#include "gtest/gtest.h"
+
+using firebase::firestore::model::FieldValue;
+using firebase::firestore::remote::Serializer;
TEST(Serializer, CanLinkToNanopb) {
// This test doesn't actually do anything interesting as far as actually using
@@ -26,3 +33,65 @@ TEST(Serializer, CanLinkToNanopb) {
// the test.
pb_ostream_from_buffer(NULL, 0);
}
+
+// Fixture for running serializer tests.
+class SerializerTest : public ::testing::Test {
+ public:
+ SerializerTest() : serializer(/*DatabaseId("p", "d")*/) {
+ }
+ Serializer serializer;
+
+ void ExpectRoundTrip(const FieldValue& model,
+ const Serializer::TypedValue& proto,
+ FieldValue::Type type) {
+ EXPECT_EQ(type, model.type());
+ EXPECT_EQ(type, proto.type);
+ Serializer::TypedValue actual_proto = serializer.EncodeFieldValue(model);
+ EXPECT_EQ(type, actual_proto.type);
+ EXPECT_EQ(proto, actual_proto);
+ EXPECT_EQ(model, serializer.DecodeFieldValue(proto));
+ }
+
+ void ExpectRoundTrip(const Serializer::TypedValue& proto,
+ std::vector<uint8_t> bytes,
+ FieldValue::Type type) {
+ EXPECT_EQ(type, proto.type);
+ std::vector<uint8_t> actual_bytes;
+ Serializer::EncodeTypedValue(proto, &actual_bytes);
+ EXPECT_EQ(bytes, actual_bytes);
+ Serializer::TypedValue actual_proto = Serializer::DecodeTypedValue(bytes);
+ EXPECT_EQ(type, actual_proto.type);
+ EXPECT_EQ(proto, actual_proto);
+ }
+};
+
+TEST_F(SerializerTest, EncodesNullModelToProto) {
+ FieldValue model = FieldValue::NullValue();
+ Serializer::TypedValue proto{FieldValue::Type::Null,
+ google_firestore_v1beta1_Value_init_default};
+ // sanity check (the _init_default above should set this to _NULL_VALUE)
+ EXPECT_EQ(google_protobuf_NullValue_NULL_VALUE, proto.value.null_value);
+ ExpectRoundTrip(model, proto, FieldValue::Type::Null);
+}
+
+TEST_F(SerializerTest, EncodesNullProtoToBytes) {
+ Serializer::TypedValue proto{FieldValue::Type::Null,
+ google_firestore_v1beta1_Value_init_default};
+ // sanity check (the _init_default above should set this to _NULL_VALUE)
+ EXPECT_EQ(google_protobuf_NullValue_NULL_VALUE, proto.value.null_value);
+
+ /* NB: proto bytes were created via:
+ echo 'null_value: NULL_VALUE' \
+ | ./build/external/protobuf/src/protobuf-build/src/protoc \
+ -I./Firestore/Protos/protos \
+ -I./build/external/protobuf/src/protobuf/src/ \
+ --encode=google.firestore.v1beta1.Value \
+ google/firestore/v1beta1/document.proto \
+ > output.bin
+ */
+ std::vector<uint8_t> bytes{0x58, 0x00};
+ ExpectRoundTrip(proto, bytes, FieldValue::Type::Null);
+}
+
+// TODO(rsgowman): Test [en|de]coding multiple protos into the same output
+// vector.
diff --git a/Firestore/test.sh b/Firestore/test.sh
index 7e26e3f..b211f46 100755
--- a/Firestore/test.sh
+++ b/Firestore/test.sh
@@ -38,6 +38,23 @@ test_iOS() {
| xcpretty
}
+test_CMake() {
+ echo "cpu core: $(sysctl -n hw.ncpu)"
+ echo "set cmake build" && \
+ mkdir build && \
+ cd build && \
+ cmake .. || \
+ exit 1
+
+ echo "initial cmake build" && \
+ make -j $(sysctl -n hw.ncpu) all || \
+ exit 2
+
+ echo "test Firestore cmake build" && \
+ cd Firestore && \
+ make test
+}
+
test_iOS; RESULT=$?
if [[ $RESULT == 65 ]]; then
echo "xcodebuild exited with 65, retrying"
@@ -46,4 +63,8 @@ if [[ $RESULT == 65 ]]; then
test_iOS; RESULT=$?
fi
-exit $RESULT
+if [ $RESULT != 0 ]; then exit $RESULT; fi
+
+test_CMake; RESULT=$?
+
+if [ $RESULT != 0 ]; then exit $RESULT; fi
diff --git a/README.md b/README.md
index 848e202..499d136 100644
--- a/README.md
+++ b/README.md
@@ -54,10 +54,10 @@ pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git',
If your Podfile does not include *use_frameworks!*, you need to workaround
a build issue with the FirebaseAnalytics umbrella header. Delete the first four lines
-of Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h
+of `Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h`
or copy [patch/FirebaseAnalytics.h](patch/FirebaseAnalytics.h) to
-Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h.
-See the post_install phase of [Example/Podfile](Example/Podfile) for an example
+`Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h`.
+See the `post_install` phase of [Example/Podfile](Example/Podfile) for an example
of applying the workaround automatically - make sure you correct the path of
`patch/FirebaseAnalytics.h`.
diff --git a/cmake/FindNanopb.cmake b/cmake/FindNanopb.cmake
index fb22aef..43c2886 100644
--- a/cmake/FindNanopb.cmake
+++ b/cmake/FindNanopb.cmake
@@ -10,7 +10,7 @@ find_path(
find_library(
NANOPB_LIBRARY
NAMES protobuf-nanopb protobuf-nanopbd
- HINTS ${BINARY_DIR}/src/nanopb-build
+ HINTS ${BINARY_DIR}/src/nanopb
)
find_package_handle_standard_args(
diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake
index 0af6b31..d545087 100644
--- a/cmake/external/grpc.cmake
+++ b/cmake/external/grpc.cmake
@@ -68,7 +68,15 @@ else()
PREFIX ${PROJECT_BINARY_DIR}/external/grpc
- CMAKE_ARGS ${CMAKE_ARGS}
+ # TODO(rsgowman): We're currently building nanopb twice; once via grpc, and
+ # once via nanopb. The version from grpc is the one that actually ends up
+ # being used. We need to fix this such that either:
+ # a) we instruct grpc to use our nanopb
+ # b) we rely on grpc's nanopb instead of using our own.
+ # For now, we'll pass in the necessary nanopb cflags into grpc. (We require
+ # 16 bit fields. Without explicitly requesting this, nanopb uses 8 bit
+ # fields.)
+ CMAKE_ARGS ${CMAKE_ARGS};-DCMAKE_C_FLAGS=-DPB_FIELD_16BIT;DCMAKE_CXX_FLAGS=-DPB_FIELD_16BIT
BUILD_COMMAND
${CMAKE_COMMAND} --build . --target grpc
diff --git a/cmake/external/nanopb.cmake b/cmake/external/nanopb.cmake
index 5df0cf5..d09c668 100644
--- a/cmake/external/nanopb.cmake
+++ b/cmake/external/nanopb.cmake
@@ -55,9 +55,14 @@ ExternalProject_Add(
# nanopb relies on $PATH for the location of protoc. cmake makes it difficult
# to adjust the path, so we'll just patch the build files with the exact
# location of protoc.
+ #
+ # NB: cmake sometimes runs the patch command multiple times in the same src
+ # dir, so we need to make sure this is idempotent. (eg 'make && make clean &&
+ # make')
PATCH_COMMAND
- perl -i -pe s,protoc,${NANOPB_PROTOC_BIN},g
- ./CMakeLists.txt ./generator/proto/Makefile
+ grep ${NANOPB_PROTOC_BIN} ./generator/proto/Makefile
+ || perl -i -pe s,protoc,${NANOPB_PROTOC_BIN},g
+ ./CMakeLists.txt ./generator/proto/Makefile
UPDATE_COMMAND ""
INSTALL_COMMAND ""