From f2ec9e11fba82d4a76d989293d270a37ad1373a2 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Tue, 17 Apr 2018 15:07:25 -0700 Subject: Serialize array transform mutations. (#1107) * Serialize array transform mutations. * Improve ArrayTransform constructor to avoid extra copying. --- .../Example/Tests/Remote/FSTSerializerBetaTests.mm | 34 ++++++++- Firestore/Source/API/FSTUserDataConverter.mm | 8 +- Firestore/Source/Remote/FSTSerializerBeta.mm | 89 +++++++++++++++++++--- .../firestore/model/transform_operations.h | 5 +- 4 files changed, 118 insertions(+), 18 deletions(-) diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm index a648cd8..8ddddb0 100644 --- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm +++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm @@ -367,7 +367,7 @@ NS_ASSUME_NONNULL_BEGIN [self assertRoundTripForMutation:mutation proto:proto]; } -- (void)testEncodesTransformMutation { +- (void)testEncodesServerTimestampTransformMutation { FSTTransformMutation *mutation = FSTTestTransformMutation(@"docs/1", @{ @"a" : [FIRFieldValue fieldValueForServerTimestamp], @"bar.baz" : [FIRFieldValue fieldValueForServerTimestamp] @@ -382,6 +382,38 @@ NS_ASSUME_NONNULL_BEGIN [self assertRoundTripForMutation:mutation proto:proto]; } +- (void)testEncodesArrayTransformMutations { + FSTTransformMutation *mutation = FSTTestTransformMutation(@"docs/1", @{ + @"a" : [FIRFieldValue fieldValueForArrayUnion:@[ @"a", @2 ]], + @"bar.baz" : [FIRFieldValue fieldValueForArrayRemove:@[ + @{ @"x" : @1 } + ]] + }); + GCFSWrite *proto = [GCFSWrite message]; + proto.transform = [GCFSDocumentTransform message]; + proto.transform.document = [self.serializer encodedDocumentKey:mutation.key]; + + GCFSDocumentTransform_FieldTransform *arrayUnion = [GCFSDocumentTransform_FieldTransform message]; + arrayUnion.fieldPath = @"a"; + arrayUnion.appendMissingElements = [GCFSArrayValue message]; + NSMutableArray *unionElements = arrayUnion.appendMissingElements.valuesArray; + [unionElements addObject:[self.serializer encodedFieldValue:FSTTestFieldValue(@"a")]]; + [unionElements addObject:[self.serializer encodedFieldValue:FSTTestFieldValue(@2)]]; + [proto.transform.fieldTransformsArray addObject:arrayUnion]; + + GCFSDocumentTransform_FieldTransform *arrayRemove = + [GCFSDocumentTransform_FieldTransform message]; + arrayRemove.fieldPath = @"bar.baz"; + arrayRemove.removeAllFromArray_p = [GCFSArrayValue message]; + NSMutableArray *removeElements = arrayRemove.removeAllFromArray_p.valuesArray; + [removeElements addObject:[self.serializer encodedFieldValue:FSTTestFieldValue(@{ @"x" : @1 })]]; + [proto.transform.fieldTransformsArray addObject:arrayRemove]; + + proto.currentDocument.exists = YES; + + [self assertRoundTripForMutation:mutation proto:proto]; +} + - (void)testEncodesSetMutationWithPrecondition { FSTSetMutation *mutation = [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(@"foo/bar") diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 719f6f5..cd32cef 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -606,16 +606,16 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } else if ([fieldValue isKindOfClass:[FSTArrayUnionFieldValue class]]) { std::vector parsedElements = [self parseArrayTransformElements:((FSTArrayUnionFieldValue *)fieldValue).elements]; - auto array_union = - absl::make_unique(TransformOperation::Type::ArrayUnion, parsedElements); + auto array_union = absl::make_unique(TransformOperation::Type::ArrayUnion, + std::move(parsedElements)); [context appendToFieldTransformsWithFieldPath:*context.path transformOperation:std::move(array_union)]; } else if ([fieldValue isKindOfClass:[FSTArrayRemoveFieldValue class]]) { std::vector parsedElements = [self parseArrayTransformElements:((FSTArrayRemoveFieldValue *)fieldValue).elements]; - auto array_remove = - absl::make_unique(TransformOperation::Type::ArrayRemove, parsedElements); + auto array_remove = absl::make_unique(TransformOperation::Type::ArrayRemove, + std::move(parsedElements)); [context appendToFieldTransformsWithFieldPath:*context.path transformOperation:std::move(array_remove)]; diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index c8b0fa4..ebb49a7 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -57,6 +57,7 @@ #include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::model::ArrayTransform; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldMask; @@ -572,31 +573,97 @@ NS_ASSUME_NONNULL_BEGIN (const std::vector &)fieldTransforms { NSMutableArray *protos = [NSMutableArray array]; for (const FieldTransform &fieldTransform : fieldTransforms) { - FSTAssert(fieldTransform.transformation().type() == TransformOperation::Type::ServerTimestamp, - @"Unknown transform: %d type", fieldTransform.transformation().type()); - GCFSDocumentTransform_FieldTransform *proto = [GCFSDocumentTransform_FieldTransform message]; - proto.fieldPath = util::WrapNSString(fieldTransform.path().CanonicalString()); - proto.setToServerValue = GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime; + GCFSDocumentTransform_FieldTransform *proto = [self encodedFieldTransform:fieldTransform]; [protos addObject:proto]; } return protos; } +- (GCFSDocumentTransform_FieldTransform *)encodedFieldTransform: + (const FieldTransform &)fieldTransform { + GCFSDocumentTransform_FieldTransform *proto = [GCFSDocumentTransform_FieldTransform message]; + proto.fieldPath = util::WrapNSString(fieldTransform.path().CanonicalString()); + if (fieldTransform.transformation().type() == TransformOperation::Type::ServerTimestamp) { + proto.setToServerValue = GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime; + + } else if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayUnion) { + proto.appendMissingElements = [self + encodedArrayTransformElements:ArrayTransform::Elements(fieldTransform.transformation())]; + + } else if (fieldTransform.transformation().type() == TransformOperation::Type::ArrayRemove) { + proto.removeAllFromArray_p = [self + encodedArrayTransformElements:ArrayTransform::Elements(fieldTransform.transformation())]; + + } else { + FSTFail(@"Unknown transform: %d type", fieldTransform.transformation().type()); + } + return proto; +} + +- (GCFSArrayValue *)encodedArrayTransformElements:(const std::vector &)elements { + GCFSArrayValue *proto = [GCFSArrayValue message]; + NSMutableArray *protoContents = [proto valuesArray]; + + for (FSTFieldValue *element : elements) { + GCFSValue *converted = [self encodedFieldValue:element]; + [protoContents addObject:converted]; + } + return proto; +} + - (std::vector)decodedFieldTransforms: (NSArray *)protos { std::vector fieldTransforms; fieldTransforms.reserve(protos.count); + for (GCFSDocumentTransform_FieldTransform *proto in protos) { - FSTAssert( - proto.setToServerValue == GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime, - @"Unknown transform setToServerValue: %d", proto.setToServerValue); - fieldTransforms.emplace_back( - FieldPath::FromServerFormat(util::MakeStringView(proto.fieldPath)), - absl::make_unique(ServerTimestampTransform::Get())); + switch (proto.transformTypeOneOfCase) { + case GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_SetToServerValue: { + FSTAssert( + proto.setToServerValue == GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime, + @"Unknown transform setToServerValue: %d", proto.setToServerValue); + fieldTransforms.emplace_back( + FieldPath::FromServerFormat(util::MakeStringView(proto.fieldPath)), + absl::make_unique(ServerTimestampTransform::Get())); + break; + } + + case GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_AppendMissingElements: { + std::vector elements = + [self decodedArrayTransformElements:proto.appendMissingElements]; + fieldTransforms.emplace_back( + FieldPath::FromServerFormat(util::MakeStringView(proto.fieldPath)), + absl::make_unique(TransformOperation::Type::ArrayUnion, + std::move(elements))); + break; + } + + case GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_RemoveAllFromArray_p: { + std::vector elements = + [self decodedArrayTransformElements:proto.removeAllFromArray_p]; + fieldTransforms.emplace_back( + FieldPath::FromServerFormat(util::MakeStringView(proto.fieldPath)), + absl::make_unique(TransformOperation::Type::ArrayRemove, + std::move(elements))); + break; + } + + default: + FSTFail(@"Unknown transform: %@", proto); + } } + return fieldTransforms; } +- (std::vector)decodedArrayTransformElements:(GCFSArrayValue *)proto { + __block std::vector elements; + [proto.valuesArray enumerateObjectsUsingBlock:^(GCFSValue *value, NSUInteger idx, BOOL *stop) { + elements.push_back([self decodedFieldValue:value]); + }]; + return elements; +} + #pragma mark - FSTMutationResult <= GCFSWriteResult proto - (FSTMutationResult *)decodedMutationResult:(GCFSWriteResult *)mutation { diff --git a/Firestore/core/src/firebase/firestore/model/transform_operations.h b/Firestore/core/src/firebase/firestore/model/transform_operations.h index 2943ea0..a1c2de0 100644 --- a/Firestore/core/src/firebase/firestore/model/transform_operations.h +++ b/Firestore/core/src/firebase/firestore/model/transform_operations.h @@ -17,6 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ +#include #include #if defined(__OBJC__) @@ -107,8 +108,8 @@ class ServerTimestampTransform : public TransformOperation { */ class ArrayTransform : public TransformOperation { public: - ArrayTransform(const Type& type, const std::vector elements) - : type_(type), elements_(elements) { + ArrayTransform(Type type, std::vector elements) + : type_(type), elements_(std::move(elements)) { } ~ArrayTransform() override { -- cgit v1.2.3