aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Firestore/Example/Tests/Integration/FSTDatastoreTests.mm4
-rw-r--r--Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm15
-rw-r--r--Firestore/Example/Tests/Model/FSTMutationTests.mm4
-rw-r--r--Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm14
-rw-r--r--Firestore/Example/Tests/Util/FSTHelpers.mm10
-rw-r--r--Firestore/Source/API/FIRDocumentReference.mm9
-rw-r--r--Firestore/Source/API/FIRWriteBatch.mm15
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.h8
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.mm6
-rw-r--r--Firestore/Source/Core/FSTTransaction.mm32
-rw-r--r--Firestore/Source/Model/FSTMutation.h64
-rw-r--r--Firestore/Source/Model/FSTMutation.mm154
-rw-r--r--Firestore/Source/Remote/FSTSerializerBeta.mm37
-rw-r--r--Firestore/core/src/firebase/firestore/model/CMakeLists.txt2
-rw-r--r--Firestore/core/src/firebase/firestore/model/precondition.cc67
-rw-r--r--Firestore/core/src/firebase/firestore/model/precondition.h160
-rw-r--r--Firestore/core/src/firebase/firestore/model/snapshot_version.h23
-rw-r--r--Firestore/core/test/firebase/firestore/model/CMakeLists.txt1
-rw-r--r--Firestore/core/test/firebase/firestore/model/precondition_test.cc77
-rw-r--r--Firestore/core/test/firebase/firestore/testutil/testutil.h29
20 files changed, 489 insertions, 242 deletions
diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
index 430366f..ad911ce 100644
--- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
+++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
@@ -42,12 +42,14 @@
#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
using firebase::firestore::auth::EmptyCredentialsProvider;
using firebase::firestore::core::DatabaseInfo;
using firebase::firestore::model::DatabaseId;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::TargetId;
NS_ASSUME_NONNULL_BEGIN
@@ -238,7 +240,7 @@ NS_ASSUME_NONNULL_BEGIN
initWithKey:[FSTDocumentKey keyWithPathString:@"rooms/eros"]
value:[[FSTObjectValue alloc]
initWithDictionary:@{@"name" : [FSTStringValue stringValue:@"Eros"]}]
- precondition:[FSTPrecondition none]];
+ precondition:Precondition::None()];
}
@end
diff --git a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm
index d810aa6..362f46f 100644
--- a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm
+++ b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm
@@ -42,12 +42,14 @@
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
namespace testutil = firebase::firestore::testutil;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::FieldMask;
+using firebase::firestore::model::Precondition;
NS_ASSUME_NONNULL_BEGIN
@@ -78,13 +80,12 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testEncodesMutationBatch {
FSTMutation *set = FSTTestSetMutation(@"foo/bar", @{ @"a" : @"b", @"num" : @1 });
- FSTMutation *patch =
- [[FSTPatchMutation alloc] initWithKey:FSTTestDocKey(@"bar/baz")
- fieldMask:FieldMask{testutil::Field("a")}
- value:FSTTestObjectValue(
- @{ @"a" : @"b",
- @"num" : @1 })
- precondition:[FSTPrecondition preconditionWithExists:YES]];
+ FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:FSTTestDocKey(@"bar/baz")
+ fieldMask:FieldMask{testutil::Field("a")}
+ value:FSTTestObjectValue(
+ @{ @"a" : @"b",
+ @"num" : @1 })
+ precondition:Precondition::Exists(true)];
FSTMutation *del = FSTTestDeleteMutation(@"baz/quux");
FIRTimestamp *writeTime = [FIRTimestamp timestamp];
FSTMutationBatch *model = [[FSTMutationBatch alloc] initWithBatchID:42
diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.mm b/Firestore/Example/Tests/Model/FSTMutationTests.mm
index 9024b22..60cb7b8 100644
--- a/Firestore/Example/Tests/Model/FSTMutationTests.mm
+++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm
@@ -26,11 +26,13 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
namespace testutil = firebase::firestore::testutil;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
+using firebase::firestore::model::Precondition;
@interface FSTMutationTests : XCTestCase
@end
@@ -74,7 +76,7 @@ using firebase::firestore::model::FieldMask;
FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:key
fieldMask:{testutil::Field("foo.bar")}
value:[FSTObjectValue objectValue]
- precondition:[FSTPrecondition none]];
+ precondition:Precondition::None()];
FSTMaybeDocument *patchedDoc =
[patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm
index c0e9cad..454b108 100644
--- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm
+++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm
@@ -50,6 +50,7 @@
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
@@ -58,6 +59,7 @@ namespace util = firebase::firestore::util;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldTransform;
+using firebase::firestore::model::Precondition;
NS_ASSUME_NONNULL_BEGIN
@@ -377,12 +379,12 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesSetMutationWithPrecondition {
- FSTSetMutation *mutation = [[FSTSetMutation alloc]
- initWithKey:FSTTestDocKey(@"foo/bar")
- value:FSTTestObjectValue(
- @{ @"a" : @"b",
- @"num" : @1 })
- precondition:[FSTPrecondition preconditionWithUpdateTime:FSTTestVersion(4)]];
+ FSTSetMutation *mutation =
+ [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(@"foo/bar")
+ value:FSTTestObjectValue(
+ @{ @"a" : @"b",
+ @"num" : @1 })
+ precondition:Precondition::UpdateTime(testutil::Version(4))];
GCFSWrite *proto = [GCFSWrite message];
proto.update = [self.serializer encodedDocumentWithFields:mutation.value key:mutation.key];
proto.currentDocument.updateTime =
diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm
index 2e1082e..78b41a4 100644
--- a/Firestore/Example/Tests/Util/FSTHelpers.mm
+++ b/Firestore/Example/Tests/Util/FSTHelpers.mm
@@ -48,6 +48,7 @@
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
@@ -62,6 +63,7 @@ using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
using firebase::firestore::model::FieldValue;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::model::ServerTimestampTransform;
using firebase::firestore::model::TransformOperation;
@@ -250,7 +252,7 @@ FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray<FSTDocument *> *docs) {
FSTSetMutation *FSTTestSetMutation(NSString *path, NSDictionary<NSString *, id> *values) {
return [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(path)
value:FSTTestObjectValue(values)
- precondition:[FSTPrecondition none]];
+ precondition:Precondition::None()];
}
FSTPatchMutation *FSTTestPatchMutation(const absl::string_view path,
@@ -274,7 +276,7 @@ FSTPatchMutation *FSTTestPatchMutation(const absl::string_view path,
return [[FSTPatchMutation alloc] initWithKey:key
fieldMask:mask
value:objectValue
- precondition:[FSTPrecondition preconditionWithExists:YES]];
+ precondition:Precondition::Exists(true)];
}
// For now this only creates TransformMutations with server timestamps.
@@ -291,8 +293,8 @@ FSTTransformMutation *FSTTestTransformMutation(NSString *path,
}
FSTDeleteMutation *FSTTestDeleteMutation(NSString *path) {
- return [[FSTDeleteMutation alloc] initWithKey:FSTTestDocKey(path)
- precondition:[FSTPrecondition none]];
+ return
+ [[FSTDeleteMutation alloc] initWithKey:FSTTestDocKey(path) precondition:Precondition::None()];
}
FSTMaybeDocumentDictionary *FSTTestDocUpdates(NSArray<FSTMaybeDocument *> *docs) {
diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm
index 67e401d..9fb4541 100644
--- a/Firestore/Source/API/FIRDocumentReference.mm
+++ b/Firestore/Source/API/FIRDocumentReference.mm
@@ -41,11 +41,13 @@
#import "Firestore/Source/Util/FSTUsageValidation.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
namespace util = firebase::firestore::util;
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::ResourcePath;
NS_ASSUME_NONNULL_BEGIN
@@ -168,7 +170,7 @@ NS_ASSUME_NONNULL_BEGIN
? [self.firestore.dataConverter parsedMergeData:documentData]
: [self.firestore.dataConverter parsedSetData:documentData];
return [self.firestore.client
- writeMutations:[parsed mutationsWithKey:self.key precondition:[FSTPrecondition none]]
+ writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::None()]
completion:completion];
}
@@ -180,8 +182,7 @@ NS_ASSUME_NONNULL_BEGIN
completion:(nullable void (^)(NSError *_Nullable error))completion {
FSTParsedUpdateData *parsed = [self.firestore.dataConverter parsedUpdateData:fields];
return [self.firestore.client
- writeMutations:[parsed mutationsWithKey:self.key
- precondition:[FSTPrecondition preconditionWithExists:YES]]
+ writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::Exists(true)]
completion:completion];
}
@@ -191,7 +192,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
FSTDeleteMutation *mutation =
- [[FSTDeleteMutation alloc] initWithKey:self.key precondition:[FSTPrecondition none]];
+ [[FSTDeleteMutation alloc] initWithKey:self.key precondition:Precondition::None()];
return [self.firestore.client writeMutations:@[ mutation ] completion:completion];
}
diff --git a/Firestore/Source/API/FIRWriteBatch.mm b/Firestore/Source/API/FIRWriteBatch.mm
index b1cfa09..df6e5e0 100644
--- a/Firestore/Source/API/FIRWriteBatch.mm
+++ b/Firestore/Source/API/FIRWriteBatch.mm
@@ -24,6 +24,10 @@
#import "Firestore/Source/Model/FSTMutation.h"
#import "Firestore/Source/Util/FSTUsageValidation.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
+
+using firebase::firestore::model::Precondition;
+
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRWriteBatch
@@ -69,8 +73,8 @@ NS_ASSUME_NONNULL_BEGIN
[self validateReference:document];
FSTParsedSetData *parsed = options.isMerge ? [self.firestore.dataConverter parsedMergeData:data]
: [self.firestore.dataConverter parsedSetData:data];
- [self.mutations addObjectsFromArray:[parsed mutationsWithKey:document.key
- precondition:[FSTPrecondition none]]];
+ [self.mutations
+ addObjectsFromArray:[parsed mutationsWithKey:document.key precondition:Precondition::None()]];
return self;
}
@@ -79,9 +83,8 @@ NS_ASSUME_NONNULL_BEGIN
[self verifyNotCommitted];
[self validateReference:document];
FSTParsedUpdateData *parsed = [self.firestore.dataConverter parsedUpdateData:fields];
- [self.mutations
- addObjectsFromArray:[parsed mutationsWithKey:document.key
- precondition:[FSTPrecondition preconditionWithExists:YES]]];
+ [self.mutations addObjectsFromArray:[parsed mutationsWithKey:document.key
+ precondition:Precondition::Exists(true)]];
return self;
}
@@ -89,7 +92,7 @@ NS_ASSUME_NONNULL_BEGIN
[self verifyNotCommitted];
[self validateReference:document];
[self.mutations addObject:[[FSTDeleteMutation alloc] initWithKey:document.key
- precondition:[FSTPrecondition none]]];
+ precondition:Precondition::None()]];
return self;
}
diff --git a/Firestore/Source/API/FSTUserDataConverter.h b/Firestore/Source/API/FSTUserDataConverter.h
index a2f947a..ea20b3e 100644
--- a/Firestore/Source/API/FSTUserDataConverter.h
+++ b/Firestore/Source/API/FSTUserDataConverter.h
@@ -22,12 +22,12 @@
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
@class FIRSetOptions;
@class FSTObjectValue;
@class FSTFieldValue;
@class FSTMutation;
-@class FSTPrecondition;
@class FSTSnapshotVersion;
NS_ASSUME_NONNULL_BEGIN
@@ -58,7 +58,8 @@ NS_ASSUME_NONNULL_BEGIN
* field transforms) using the specified document key and precondition.
*/
- (NSArray<FSTMutation *> *)mutationsWithKey:(const firebase::firestore::model::DocumentKey &)key
- precondition:(FSTPrecondition *)precondition;
+ precondition:
+ (const firebase::firestore::model::Precondition &)precondition;
@end
@@ -83,7 +84,8 @@ NS_ASSUME_NONNULL_BEGIN
* field transforms) using the specified document key and precondition.
*/
- (NSArray<FSTMutation *> *)mutationsWithKey:(const firebase::firestore::model::DocumentKey &)key
- precondition:(FSTPrecondition *)precondition;
+ precondition:
+ (const firebase::firestore::model::Precondition &)precondition;
@end
diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm
index 6320e83..dff9235 100644
--- a/Firestore/Source/API/FSTUserDataConverter.mm
+++ b/Firestore/Source/API/FSTUserDataConverter.mm
@@ -38,6 +38,7 @@
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
#include "absl/memory/memory.h"
@@ -48,6 +49,7 @@ using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::ServerTimestampTransform;
using firebase::firestore::model::TransformOperation;
@@ -91,7 +93,7 @@ static NSString *const RESERVED_FIELD_DESIGNATOR = @"__";
}
- (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key
- precondition:(FSTPrecondition *)precondition {
+ precondition:(const Precondition &)precondition {
NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array];
if (self.isPatch) {
[mutations addObject:[[FSTPatchMutation alloc] initWithKey:key
@@ -132,7 +134,7 @@ static NSString *const RESERVED_FIELD_DESIGNATOR = @"__";
}
- (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key
- precondition:(FSTPrecondition *)precondition {
+ precondition:(const Precondition &)precondition {
NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array];
[mutations addObject:[[FSTPatchMutation alloc] initWithKey:key
fieldMask:self.fieldMask
diff --git a/Firestore/Source/Core/FSTTransaction.mm b/Firestore/Source/Core/FSTTransaction.mm
index 71c1f35..681f9ca 100644
--- a/Firestore/Source/Core/FSTTransaction.mm
+++ b/Firestore/Source/Core/FSTTransaction.mm
@@ -33,8 +33,10 @@
#import "Firestore/Source/Util/FSTUsageValidation.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
using firebase::firestore::model::DocumentKey;
+using firebase::firestore::model::Precondition;
NS_ASSUME_NONNULL_BEGIN
@@ -136,25 +138,26 @@ NS_ASSUME_NONNULL_BEGIN
* Returns version of this doc when it was read in this transaction as a precondition, or no
* precondition if it was not read.
*/
-- (FSTPrecondition *)preconditionForDocumentKey:(const DocumentKey &)key {
+- (Precondition)preconditionForDocumentKey:(const DocumentKey &)key {
const auto iter = _readVersions.find(key);
if (iter == _readVersions.end()) {
- return [FSTPrecondition none];
+ return Precondition::None();
} else {
- return [FSTPrecondition preconditionWithUpdateTime:iter->second];
+ return Precondition::UpdateTime(iter->second);
}
}
/**
* Returns the precondition for a document if the operation is an update, based on the provided
- * UpdateOptions. Will return nil if an error occurred, in which case it sets the error parameter.
+ * UpdateOptions. Will return none precondition if an error occurred, in which case it sets the
+ * error parameter.
*/
-- (nullable FSTPrecondition *)preconditionForUpdateWithDocumentKey:(const DocumentKey &)key
- error:(NSError **)error {
+- (Precondition)preconditionForUpdateWithDocumentKey:(const DocumentKey &)key
+ error:(NSError **)error {
const auto iter = _readVersions.find(key);
if (iter == _readVersions.end()) {
// Document was not read, so we just use the preconditions for an update.
- return [FSTPrecondition preconditionWithExists:YES];
+ return Precondition::Exists(true);
}
FSTSnapshotVersion *version = iter->second;
@@ -169,10 +172,10 @@ NS_ASSUME_NONNULL_BEGIN
NSLocalizedDescriptionKey : @"Can't update a document that doesn't exist."
}];
}
- return nil;
+ return Precondition::None();
} else {
// Document exists, just base precondition on document update time.
- return [FSTPrecondition preconditionWithUpdateTime:version];
+ return Precondition::UpdateTime(version);
}
}
@@ -183,13 +186,12 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateData:(FSTParsedUpdateData *)data forDocument:(const DocumentKey &)key {
NSError *error = nil;
- FSTPrecondition *_Nullable precondition =
- [self preconditionForUpdateWithDocumentKey:key error:&error];
- if (precondition) {
- [self writeMutations:[data mutationsWithKey:key precondition:precondition]];
- } else {
+ const Precondition precondition = [self preconditionForUpdateWithDocumentKey:key error:&error];
+ if (precondition.IsNone()) {
FSTAssert(error, @"Got nil precondition, but error was not set");
self.lastWriteError = error;
+ } else {
+ [self writeMutations:[data mutationsWithKey:key precondition:precondition]];
}
}
@@ -198,7 +200,7 @@ NS_ASSUME_NONNULL_BEGIN
initWithKey:key
precondition:[self preconditionForDocumentKey:key]] ]];
// Since the delete will be applied before all following writes, we need to ensure that the
- // precondition for the next write will be exists: false.
+ // precondition for the next write will be exists without timestamp.
_readVersions[key] = [FSTSnapshotVersion noVersion];
}
diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h
index 38c35bf..7261f30 100644
--- a/Firestore/Source/Model/FSTMutation.h
+++ b/Firestore/Source/Model/FSTMutation.h
@@ -23,6 +23,7 @@
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
@class FSTDocument;
@@ -34,49 +35,6 @@
NS_ASSUME_NONNULL_BEGIN
-#pragma mark - FSTPrecondition
-
-typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
- FSTPreconditionExistsNotSet,
- FSTPreconditionExistsYes,
- FSTPreconditionExistsNo,
-};
-
-/**
- * Encodes a precondition for a mutation. This follows the model that the backend accepts with the
- * special case of an explicit "empty" precondition (meaning no precondition).
- */
-@interface FSTPrecondition : NSObject
-
-/** Creates a new FSTPrecondition with an exists flag. */
-+ (FSTPrecondition *)preconditionWithExists:(BOOL)exists;
-
-/** Creates a new FSTPrecondition based on a time the document exists at. */
-+ (FSTPrecondition *)preconditionWithUpdateTime:(FSTSnapshotVersion *)updateTime;
-
-/** Returns a precondition representing no precondition. */
-+ (FSTPrecondition *)none;
-
-/**
- * Returns true if the preconditions is valid for the given document (or null if no document is
- * available).
- */
-- (BOOL)isValidForDocument:(FSTMaybeDocument *_Nullable)maybeDoc;
-
-/** Returns whether this Precondition represents no precondition. */
-- (BOOL)isNone;
-
-/** If set, preconditions a mutation based on the last updateTime. */
-@property(nonatomic, strong, readonly, nullable) FSTSnapshotVersion *updateTime;
-
-/**
- * If set, preconditions a mutation based on whether the document exists.
- * Uses FSTPreconditionExistsNotSet to mark as unset.
- */
-@property(nonatomic, assign, readonly) FSTPreconditionExists exists;
-
-@end
-
#pragma mark - FSTMutationResult
@interface FSTMutationResult : NSObject
@@ -115,7 +73,8 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
- (id)init NS_UNAVAILABLE;
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
- precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER;
+ precondition:(firebase::firestore::model::Precondition)precondition
+ NS_DESIGNATED_INITIALIZER;
/**
* Applies this mutation to the given FSTDocument, FSTDeletedDocument or nil, if we don't have
@@ -176,8 +135,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
- (const firebase::firestore::model::DocumentKey &)key;
-/** The precondition for this mutation. */
-@property(nonatomic, strong, readonly) FSTPrecondition *precondition;
+- (const firebase::firestore::model::Precondition &)precondition;
@end
@@ -190,7 +148,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
@interface FSTSetMutation : FSTMutation
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
- precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE;
+ precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE;
/**
* Initializes the set mutation.
@@ -202,7 +160,8 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
*/
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
value:(FSTObjectValue *)value
- precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER;
+ precondition:(firebase::firestore::model::Precondition)precondition
+ NS_DESIGNATED_INITIALIZER;
/** The object value to use when setting the document. */
@property(nonatomic, strong, readonly) FSTObjectValue *value;
@@ -221,9 +180,9 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
*/
@interface FSTPatchMutation : FSTMutation
-/** Returns the precondition for the given FSTPrecondition. */
+/** Returns the precondition for the given Precondition. */
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
- precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE;
+ precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE;
/**
* Initializes a new patch mutation with an explicit FieldMask and FSTObjectValue representing
@@ -239,7 +198,8 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
fieldMask:(firebase::firestore::model::FieldMask)fieldMask
value:(FSTObjectValue *)value
- precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER;
+ precondition:(firebase::firestore::model::Precondition)precondition
+ NS_DESIGNATED_INITIALIZER;
/**
* A mask to apply to |value|, where only fields that are in both the fieldMask and the value
@@ -266,7 +226,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
@interface FSTTransformMutation : FSTMutation
- (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key
- precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE;
+ precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE;
/**
* Initializes a new transform mutation with the specified field transforms.
diff --git a/Firestore/Source/Model/FSTMutation.mm b/Firestore/Source/Model/FSTMutation.mm
index dd649a0..99d2e51 100644
--- a/Firestore/Source/Model/FSTMutation.mm
+++ b/Firestore/Source/Model/FSTMutation.mm
@@ -33,115 +33,19 @@
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::ServerTimestampTransform;
using firebase::firestore::model::TransformOperation;
NS_ASSUME_NONNULL_BEGIN
-#pragma mark - FSTPrecondition
-
-@implementation FSTPrecondition
-
-+ (FSTPrecondition *)preconditionWithExists:(BOOL)exists {
- FSTPreconditionExists existsEnum = exists ? FSTPreconditionExistsYes : FSTPreconditionExistsNo;
- return [[FSTPrecondition alloc] initWithUpdateTime:nil exists:existsEnum];
-}
-
-+ (FSTPrecondition *)preconditionWithUpdateTime:(FSTSnapshotVersion *)updateTime {
- return [[FSTPrecondition alloc] initWithUpdateTime:updateTime exists:FSTPreconditionExistsNotSet];
-}
-
-+ (FSTPrecondition *)none {
- static dispatch_once_t onceToken;
- static FSTPrecondition *noPrecondition;
- dispatch_once(&onceToken, ^{
- noPrecondition =
- [[FSTPrecondition alloc] initWithUpdateTime:nil exists:FSTPreconditionExistsNotSet];
- });
- return noPrecondition;
-}
-
-- (instancetype)initWithUpdateTime:(FSTSnapshotVersion *_Nullable)updateTime
- exists:(FSTPreconditionExists)exists {
- if (self = [super init]) {
- _updateTime = updateTime;
- _exists = exists;
- }
- return self;
-}
-
-- (BOOL)isValidForDocument:(FSTMaybeDocument *_Nullable)maybeDoc {
- if (self.updateTime) {
- return
- [maybeDoc isKindOfClass:[FSTDocument class]] && [maybeDoc.version isEqual:self.updateTime];
- } else if (self.exists != FSTPreconditionExistsNotSet) {
- if (self.exists == FSTPreconditionExistsYes) {
- return [maybeDoc isKindOfClass:[FSTDocument class]];
- } else {
- FSTAssert(self.exists == FSTPreconditionExistsNo, @"Invalid precondition");
- return maybeDoc == nil || [maybeDoc isKindOfClass:[FSTDeletedDocument class]];
- }
- } else {
- FSTAssert(self.isNone, @"Precondition should be empty");
- return YES;
- }
-}
-
-- (BOOL)isNone {
- return self.updateTime == nil && self.exists == FSTPreconditionExistsNotSet;
-}
-
-- (BOOL)isEqual:(id)other {
- if (self == other) {
- return YES;
- }
-
- if (![other isKindOfClass:[FSTPrecondition class]]) {
- return NO;
- }
-
- FSTPrecondition *otherPrecondition = (FSTPrecondition *)other;
- // Compare references to cover nil equality
- return (self.updateTime == otherPrecondition.updateTime ||
- [self.updateTime isEqual:otherPrecondition.updateTime]) &&
- self.exists == otherPrecondition.exists;
-}
-
-- (NSUInteger)hash {
- NSUInteger hash = [self.updateTime hash];
- hash = hash * 31 + self.exists;
- return hash;
-}
-
-- (NSString *)description {
- if (self.isNone) {
- return @"<FSTPrecondition <none>>";
- } else {
- NSString *existsString;
- switch (self.exists) {
- case FSTPreconditionExistsYes:
- existsString = @"yes";
- break;
- case FSTPreconditionExistsNo:
- existsString = @"no";
- break;
- default:
- existsString = @"<not-set>";
- break;
- }
- return [NSString stringWithFormat:@"<FSTPrecondition updateTime=%@ exists=%@>", self.updateTime,
- existsString];
- }
-}
-
-@end
-
#pragma mark - FSTMutationResult
@implementation FSTMutationResult
@@ -161,12 +65,13 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTMutation {
DocumentKey _key;
+ Precondition _precondition;
}
-- (instancetype)initWithKey:(DocumentKey)key precondition:(FSTPrecondition *)precondition {
+- (instancetype)initWithKey:(DocumentKey)key precondition:(Precondition)precondition {
if (self = [super init]) {
_key = std::move(key);
- _precondition = precondition;
+ _precondition = std::move(precondition);
}
return self;
}
@@ -189,6 +94,10 @@ NS_ASSUME_NONNULL_BEGIN
return _key;
}
+- (const firebase::firestore::model::Precondition &)precondition {
+ return _precondition;
+}
+
@end
#pragma mark - FSTSetMutation
@@ -197,8 +106,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithKey:(DocumentKey)key
value:(FSTObjectValue *)value
- precondition:(FSTPrecondition *)precondition {
- if (self = [super initWithKey:std::move(key) precondition:precondition]) {
+ precondition:(Precondition)precondition {
+ if (self = [super initWithKey:std::move(key) precondition:std::move(precondition)]) {
_value = value;
}
return self;
@@ -206,7 +115,8 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)description {
return [NSString stringWithFormat:@"<FSTSetMutation key=%s value=%@ precondition=%@>",
- self.key.ToString().c_str(), self.value, self.precondition];
+ self.key.ToString().c_str(), self.value,
+ self.precondition.description()];
}
- (BOOL)isEqual:(id)other {
@@ -219,12 +129,12 @@ NS_ASSUME_NONNULL_BEGIN
FSTSetMutation *otherMutation = (FSTSetMutation *)other;
return [self.key isEqual:otherMutation.key] && [self.value isEqual:otherMutation.value] &&
- [self.precondition isEqual:otherMutation.precondition];
+ self.precondition == otherMutation.precondition;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = 31 * result + [self.precondition hash];
+ result = 31 * result + self.precondition.Hash();
result = 31 * result + [self.value hash];
return result;
}
@@ -237,7 +147,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTSetMutation.");
}
- if (![self.precondition isValidForDocument:maybeDoc]) {
+ if (!self.precondition.IsValidFor(maybeDoc)) {
return maybeDoc;
}
@@ -271,8 +181,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithKey:(DocumentKey)key
fieldMask:(FieldMask)fieldMask
value:(FSTObjectValue *)value
- precondition:(FSTPrecondition *)precondition {
- self = [super initWithKey:std::move(key) precondition:precondition];
+ precondition:(Precondition)precondition {
+ self = [super initWithKey:std::move(key) precondition:std::move(precondition)];
if (self) {
_fieldMask = std::move(fieldMask);
_value = value;
@@ -295,12 +205,12 @@ NS_ASSUME_NONNULL_BEGIN
FSTPatchMutation *otherMutation = (FSTPatchMutation *)other;
return [self.key isEqual:otherMutation.key] && self.fieldMask == otherMutation.fieldMask &&
[self.value isEqual:otherMutation.value] &&
- [self.precondition isEqual:otherMutation.precondition];
+ self.precondition == otherMutation.precondition;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = 31 * result + [self.precondition hash];
+ result = 31 * result + self.precondition.Hash();
result = 31 * result + self.fieldMask.Hash();
result = 31 * result + [self.value hash];
return result;
@@ -309,7 +219,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)description {
return [NSString stringWithFormat:@"<FSTPatchMutation key=%s mask=%s value=%@ precondition=%@>",
self.key.ToString().c_str(), self.fieldMask.ToString().c_str(),
- self.value, self.precondition];
+ self.value, self.precondition.description()];
}
- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
@@ -320,7 +230,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTPatchMutation.");
}
- if (![self.precondition isValidForDocument:maybeDoc]) {
+ if (!self.precondition.IsValidFor(maybeDoc)) {
return maybeDoc;
}
@@ -373,8 +283,7 @@ NS_ASSUME_NONNULL_BEGIN
// NOTE: We set a precondition of exists: true as a safety-check, since we always combine
// FSTTransformMutations with a FSTSetMutation or FSTPatchMutation which (if successful) should
// end up with an existing document.
- if (self = [super initWithKey:std::move(key)
- precondition:[FSTPrecondition preconditionWithExists:YES]]) {
+ if (self = [super initWithKey:std::move(key) precondition:Precondition::Exists(true)]) {
_fieldTransforms = std::move(fieldTransforms);
}
return self;
@@ -395,12 +304,12 @@ NS_ASSUME_NONNULL_BEGIN
FSTTransformMutation *otherMutation = (FSTTransformMutation *)other;
return [self.key isEqual:otherMutation.key] &&
self.fieldTransforms == otherMutation.fieldTransforms &&
- [self.precondition isEqual:otherMutation.precondition];
+ self.precondition == otherMutation.precondition;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = 31 * result + [self.precondition hash];
+ result = 31 * result + self.precondition.Hash();
for (const auto &transform : self.fieldTransforms) {
result = 31 * result + transform.Hash();
}
@@ -414,7 +323,7 @@ NS_ASSUME_NONNULL_BEGIN
}
return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%s precondition=%@>",
self.key.ToString().c_str(), fieldTransforms.c_str(),
- self.precondition];
+ self.precondition.description()];
}
- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
@@ -426,7 +335,7 @@ NS_ASSUME_NONNULL_BEGIN
@"Transform results missing for FSTTransformMutation.");
}
- if (![self.precondition isValidForDocument:maybeDoc]) {
+ if (!self.precondition.IsValidFor(maybeDoc)) {
return maybeDoc;
}
@@ -514,19 +423,18 @@ NS_ASSUME_NONNULL_BEGIN
}
FSTDeleteMutation *otherMutation = (FSTDeleteMutation *)other;
- return [self.key isEqual:otherMutation.key] &&
- [self.precondition isEqual:otherMutation.precondition];
+ return [self.key isEqual:otherMutation.key] && self.precondition == otherMutation.precondition;
}
- (NSUInteger)hash {
NSUInteger result = [self.key hash];
- result = 31 * result + [self.precondition hash];
+ result = 31 * result + self.precondition.Hash();
return result;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<FSTDeleteMutation key=%s precondition=%@>",
- self.key.ToString().c_str(), self.precondition];
+ self.key.ToString().c_str(), self.precondition.description()];
}
- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
@@ -538,7 +446,7 @@ NS_ASSUME_NONNULL_BEGIN
@"Transform results received by FSTDeleteMutation.");
}
- if (![self.precondition isValidForDocument:maybeDoc]) {
+ if (!self.precondition.IsValidFor(maybeDoc)) {
return maybeDoc;
}
diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm
index 1d9ad30..c8b0fa4 100644
--- a/Firestore/Source/Remote/FSTSerializerBeta.mm
+++ b/Firestore/Source/Remote/FSTSerializerBeta.mm
@@ -50,6 +50,7 @@
#include "Firestore/core/src/firebase/firestore/model/field_mask.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/field_transform.h"
+#include "Firestore/core/src/firebase/firestore/model/precondition.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
#include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
@@ -61,8 +62,10 @@ using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
+using firebase::firestore::model::Precondition;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::model::ServerTimestampTransform;
+using firebase::firestore::model::SnapshotVersion;
using firebase::firestore::model::TransformOperation;
NS_ASSUME_NONNULL_BEGIN
@@ -475,7 +478,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTFail(@"Unknown mutation type %@", NSStringFromClass(mutationClass));
}
- if (!mutation.precondition.isNone) {
+ if (!mutation.precondition.IsNone()) {
proto.currentDocument = [self encodedPrecondition:mutation.precondition];
}
@@ -483,9 +486,9 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMutation *)decodedMutation:(GCFSWrite *)mutation {
- FSTPrecondition *precondition = [mutation hasCurrentDocument]
- ? [self decodedPrecondition:mutation.currentDocument]
- : [FSTPrecondition none];
+ Precondition precondition = [mutation hasCurrentDocument]
+ ? [self decodedPrecondition:mutation.currentDocument]
+ : Precondition::None();
switch (mutation.operationOneOfCase) {
case GCFSWrite_Operation_OneOfCase_Update:
@@ -505,8 +508,7 @@ NS_ASSUME_NONNULL_BEGIN
precondition:precondition];
case GCFSWrite_Operation_OneOfCase_Transform: {
- FSTPreconditionExists exists = precondition.exists;
- FSTAssert(exists == FSTPreconditionExistsYes,
+ FSTAssert(precondition == Precondition::Exists(true),
@"Transforms must have precondition \"exists == true\"");
return [[FSTTransformMutation alloc]
@@ -520,30 +522,29 @@ NS_ASSUME_NONNULL_BEGIN
}
}
-- (GCFSPrecondition *)encodedPrecondition:(FSTPrecondition *)precondition {
- FSTAssert(!precondition.isNone, @"Can't serialize an empty precondition");
+- (GCFSPrecondition *)encodedPrecondition:(const Precondition &)precondition {
+ FSTAssert(!precondition.IsNone(), @"Can't serialize an empty precondition");
GCFSPrecondition *message = [GCFSPrecondition message];
- if (precondition.updateTime) {
- message.updateTime = [self encodedVersion:precondition.updateTime];
- } else if (precondition.exists != FSTPreconditionExistsNotSet) {
- message.exists = precondition.exists == FSTPreconditionExistsYes;
+ if (precondition.type() == Precondition::Type::UpdateTime) {
+ message.updateTime = [self encodedVersion:precondition.update_time()];
+ } else if (precondition.type() == Precondition::Type::Exists) {
+ message.exists = precondition == Precondition::Exists(true);
} else {
- FSTFail(@"Unknown precondition: %@", precondition);
+ FSTFail(@"Unknown precondition: %@", precondition.description());
}
return message;
}
-- (FSTPrecondition *)decodedPrecondition:(GCFSPrecondition *)precondition {
+- (Precondition)decodedPrecondition:(GCFSPrecondition *)precondition {
switch (precondition.conditionTypeOneOfCase) {
case GCFSPrecondition_ConditionType_OneOfCase_GPBUnsetOneOfCase:
- return [FSTPrecondition none];
+ return Precondition::None();
case GCFSPrecondition_ConditionType_OneOfCase_Exists:
- return [FSTPrecondition preconditionWithExists:precondition.exists];
+ return Precondition::Exists(precondition.exists);
case GCFSPrecondition_ConditionType_OneOfCase_UpdateTime:
- return [FSTPrecondition
- preconditionWithUpdateTime:[self decodedVersion:precondition.updateTime]];
+ return Precondition::UpdateTime([self decodedVersion:precondition.updateTime]);
default:
FSTFail(@"Unrecognized Precondition one-of case %@", precondition);
diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
index de783ad..02affdb 100644
--- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
@@ -32,6 +32,8 @@ cc_library(
maybe_document.h
no_document.cc
no_document.h
+ precondition.cc
+ precondition.h
resource_path.cc
resource_path.h
snapshot_version.cc
diff --git a/Firestore/core/src/firebase/firestore/model/precondition.cc b/Firestore/core/src/firebase/firestore/model/precondition.cc
new file mode 100644
index 0000000..423d5a2
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/precondition.cc
@@ -0,0 +1,67 @@
+/*
+ * 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/precondition.h"
+
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+Precondition::Precondition(Type type, SnapshotVersion update_time, bool exists)
+ : type_(type), update_time_(std::move(update_time)), exists_(exists) {
+}
+
+/* static */
+Precondition Precondition::Exists(bool exists) {
+ return Precondition{Type::Exists, SnapshotVersion::None(), exists};
+}
+
+/* static */
+Precondition Precondition::UpdateTime(SnapshotVersion update_time) {
+ // update_time could be SnapshotVersion::None() in particular for locally
+ // deleted documents.
+ return Precondition{Type::UpdateTime, std::move(update_time), false};
+}
+
+/* static */
+Precondition Precondition::None() {
+ return Precondition{Type::None, SnapshotVersion::None(), false};
+}
+
+bool Precondition::IsValidFor(const MaybeDocument& maybe_doc) const {
+ switch (type_) {
+ case Type::UpdateTime:
+ return maybe_doc.type() == MaybeDocument::Type::Document &&
+ maybe_doc.version() == update_time_;
+ case Type::Exists:
+ if (exists_) {
+ return maybe_doc.type() == MaybeDocument::Type::Document;
+ } else {
+ return maybe_doc.type() == MaybeDocument::Type::NoDocument;
+ }
+ case Type::None:
+ return true;
+ }
+ FIREBASE_UNREACHABLE();
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/precondition.h b/Firestore/core/src/firebase/firestore/model/precondition.h
new file mode 100644
index 0000000..4ab03c2
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/precondition.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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_
+
+#include <utility>
+
+#if defined(__OBJC__)
+#import "FIRTimestamp.h"
+#import "Firestore/Source/Core/FSTSnapshotVersion.h"
+#import "Firestore/Source/Model/FSTDocument.h"
+#include "Firestore/core/include/firebase/firestore/timestamp.h"
+#endif // defined(__OBJC__)
+
+#include "Firestore/core/src/firebase/firestore/model/maybe_document.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * Encodes a precondition for a mutation. This follows the model that the
+ * backend accepts with the special case of an explicit "empty" precondition
+ * (meaning no precondition).
+ */
+class Precondition {
+ public:
+ enum class Type {
+ None,
+ Exists,
+ UpdateTime,
+ };
+
+ /** Creates a new Precondition with an exists flag. */
+ static Precondition Exists(bool exists);
+
+ /** Creates a new Precondition based on a time the document exists at. */
+ static Precondition UpdateTime(SnapshotVersion update_time);
+
+ /** Returns a precondition representing no precondition. */
+ static Precondition None();
+
+ /**
+ * Returns true if the precondition is valid for the given document (and the
+ * document is available).
+ */
+ bool IsValidFor(const MaybeDocument& maybe_doc) const;
+
+ /** Returns whether this Precondition represents no precondition. */
+ bool IsNone() const {
+ return type_ == Type::None;
+ }
+
+ Type type() const {
+ return type_;
+ }
+
+ const SnapshotVersion& update_time() const {
+ return update_time_;
+ }
+
+ bool operator==(const Precondition& other) const {
+ return type_ == other.type_ && update_time_ == other.update_time_ &&
+ exists_ == other.exists_;
+ }
+
+#if defined(__OBJC__)
+ // Objective-C requires a default constructor.
+ Precondition()
+ : type_(Type::None),
+ update_time_(SnapshotVersion::None()),
+ exists_(false) {
+ }
+
+ // MaybeDocument is not fully ported yet. So we suppose this addition helper.
+ bool IsValidFor(FSTMaybeDocument* maybe_doc) const {
+ switch (type_) {
+ case Type::UpdateTime:
+ return [maybe_doc isKindOfClass:[FSTDocument class]] &&
+ firebase::firestore::model::SnapshotVersion(maybe_doc.version) ==
+ update_time_;
+ case Type::Exists:
+ if (exists_) {
+ return [maybe_doc isKindOfClass:[FSTDocument class]];
+ } else {
+ return maybe_doc == nil ||
+ [maybe_doc isKindOfClass:[FSTDeletedDocument class]];
+ }
+ case Type::None:
+ return true;
+ }
+ FIREBASE_UNREACHABLE();
+ }
+
+ // For Objective-C++ hash; to be removed after migration.
+ // Do NOT use in C++ code.
+ NSUInteger Hash() const {
+ NSUInteger hash = std::hash<Timestamp>()(update_time_.timestamp());
+ hash = hash * 31 + exists_;
+ hash = hash * 31 + static_cast<NSUInteger>(type_);
+ return hash;
+ }
+
+ NSString* description() const {
+ switch (type_) {
+ case Type::None:
+ return @"<Precondition <none>>";
+ case Type::Exists:
+ if (exists_) {
+ return @"<Precondition exists=yes>";
+ } else {
+ return @"<Precondition exists=no>";
+ }
+ case Type::UpdateTime:
+ return [NSString
+ stringWithFormat:@"<Precondition update_time=%s>",
+ update_time_.timestamp().ToString().c_str()];
+ }
+ // We only raise dev assertion here. This function is mainly used in
+ // logging.
+ FIREBASE_DEV_ASSERT_MESSAGE(false, "precondition invalid");
+ return @"<Precondition invalid>";
+ }
+#endif // defined(__OBJC__)
+
+ private:
+ Precondition(Type type, SnapshotVersion update_time, bool exists);
+
+ // The actual time of this precondition.
+ Type type_ = Type::None;
+
+ // For UpdateTime type, preconditions a mutation based on the last updateTime.
+ SnapshotVersion update_time_;
+
+ // For Exists type, preconditions a mutation based on whether the document
+ // exists.
+ bool exists_;
+};
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_
diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.h b/Firestore/core/src/firebase/firestore/model/snapshot_version.h
index 56e8c50..1fbba1c 100644
--- a/Firestore/core/src/firebase/firestore/model/snapshot_version.h
+++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.h
@@ -19,6 +19,11 @@
#include "Firestore/core/include/firebase/firestore/timestamp.h"
+#if defined(__OBJC__)
+#import "FIRTimestamp.h"
+#import "Firestore/Source/Core/FSTSnapshotVersion.h"
+#endif // defined(__OBJC__)
+
namespace firebase {
namespace firestore {
namespace model {
@@ -38,6 +43,24 @@ class SnapshotVersion {
/** Creates a new version that is smaller than all other versions. */
static const SnapshotVersion& None();
+#if defined(__OBJC__)
+ SnapshotVersion(FSTSnapshotVersion* version) // NOLINT(runtime/explicit)
+ : timestamp_{version.timestamp.seconds, version.timestamp.nanoseconds} {
+ }
+
+ operator FSTSnapshotVersion*() const {
+ if (timestamp_ == Timestamp{}) {
+ return [FSTSnapshotVersion noVersion];
+ } else {
+ return [FSTSnapshotVersion
+ versionWithTimestamp:[FIRTimestamp
+ timestampWithSeconds:timestamp_.seconds()
+ nanoseconds:timestamp_
+ .nanoseconds()]];
+ }
+ }
+#endif // defined(__OBJC__)
+
private:
Timestamp timestamp_;
};
diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
index 3bac89d..9c94677 100644
--- a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
@@ -24,6 +24,7 @@ cc_test(
field_value_test.cc
maybe_document_test.cc
no_document_test.cc
+ precondition_test.cc
resource_path_test.cc
snapshot_version_test.cc
transform_operations_test.cc
diff --git a/Firestore/core/test/firebase/firestore/model/precondition_test.cc b/Firestore/core/test/firebase/firestore/model/precondition_test.cc
new file mode 100644
index 0000000..3ddb2ba
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/precondition_test.cc
@@ -0,0 +1,77 @@
+/*
+ * 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/precondition.h"
+
+#include "Firestore/core/src/firebase/firestore/model/document.h"
+#include "Firestore/core/src/firebase/firestore/model/no_document.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
+#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+TEST(Precondition, None) {
+ const Precondition none = Precondition::None();
+ EXPECT_EQ(Precondition::Type::None, none.type());
+ EXPECT_TRUE(none.IsNone());
+ EXPECT_EQ(SnapshotVersion::None(), none.update_time());
+
+ const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567);
+ const Document doc = testutil::Doc("bar/doc", 7654321);
+ EXPECT_TRUE(none.IsValidFor(deleted_doc));
+ EXPECT_TRUE(none.IsValidFor(doc));
+}
+
+TEST(Precondition, Exists) {
+ const Precondition exists = Precondition::Exists(true);
+ const Precondition no_exists = Precondition::Exists(false);
+ EXPECT_EQ(Precondition::Type::Exists, exists.type());
+ EXPECT_EQ(Precondition::Type::Exists, no_exists.type());
+ EXPECT_FALSE(exists.IsNone());
+ EXPECT_FALSE(no_exists.IsNone());
+ EXPECT_EQ(SnapshotVersion::None(), exists.update_time());
+ EXPECT_EQ(SnapshotVersion::None(), no_exists.update_time());
+
+ const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567);
+ const Document doc = testutil::Doc("bar/doc", 7654321);
+ EXPECT_FALSE(exists.IsValidFor(deleted_doc));
+ EXPECT_TRUE(exists.IsValidFor(doc));
+ EXPECT_TRUE(no_exists.IsValidFor(deleted_doc));
+ EXPECT_FALSE(no_exists.IsValidFor(doc));
+}
+
+TEST(Precondition, UpdateTime) {
+ const Precondition update_time =
+ Precondition::UpdateTime(testutil::Version(1234567));
+ EXPECT_EQ(Precondition::Type::UpdateTime, update_time.type());
+ EXPECT_FALSE(update_time.IsNone());
+ EXPECT_EQ(testutil::Version(1234567), update_time.update_time());
+
+ const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567);
+ const Document not_match = testutil::Doc("bar/doc", 7654321);
+ const Document match = testutil::Doc("baz/doc", 1234567);
+ EXPECT_FALSE(update_time.IsValidFor(deleted_doc));
+ EXPECT_FALSE(update_time.IsValidFor(not_match));
+ EXPECT_TRUE(update_time.IsValidFor(match));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.h b/Firestore/core/test/firebase/firestore/testutil/testutil.h
index 9c69784..9a875f4 100644
--- a/Firestore/core/test/firebase/firestore/testutil/testutil.h
+++ b/Firestore/core/test/firebase/firestore/testutil/testutil.h
@@ -17,9 +17,16 @@
#ifndef FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_
#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_
+#include <chrono> // NOLINT(build/c++11)
+#include <cstdint>
+
+#include "Firestore/core/include/firebase/firestore/timestamp.h"
+#include "Firestore/core/src/firebase/firestore/model/document.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
+#include "Firestore/core/src/firebase/firestore/model/no_document.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
+#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
#include "absl/strings/string_view.h"
namespace firebase {
@@ -40,6 +47,28 @@ inline model::ResourcePath Resource(absl::string_view field) {
return model::ResourcePath::FromString(field);
}
+/**
+ * Creates a snapshot version from the given version timestamp.
+ *
+ * @param version a timestamp in microseconds since the epoch.
+ */
+inline model::SnapshotVersion Version(int64_t version) {
+ namespace chr = std::chrono;
+ auto timepoint =
+ chr::time_point<chr::system_clock>(chr::microseconds(version));
+ return model::SnapshotVersion{Timestamp::FromTimePoint(timepoint)};
+}
+
+inline model::Document Doc(absl::string_view key, int64_t version) {
+ return model::Document{model::FieldValue::ObjectValueFromMap({}), Key(key),
+ Version(version),
+ /* has_local_mutations= */ false};
+}
+
+inline model::NoDocument DeletedDoc(absl::string_view key, int64_t version) {
+ return model::NoDocument{Key(key), Version(version)};
+}
+
// Add a non-inline function to make this library buildable.
// TODO(zxu123): remove once there is non-inline function.
void dummy();