Skip to content

Commit 046e299

Browse files
authored
Fix small issues in C++ serializer and port remaining tests (#4012)
These were discovered by running integration tests using the new `Serializer`. Also, port all the missing tests from `FSTSerializerBetaTests.mm` (that I could find).
1 parent 28038e5 commit 046e299

File tree

6 files changed

+352
-59
lines changed

6 files changed

+352
-59
lines changed

Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const pb_field_t google_firestore_v1_Write_fields[6] = {
3333
PB_ANONYMOUS_ONEOF_FIELD(operation, 1, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1_Write, update, update, &google_firestore_v1_Document_fields),
3434
PB_ANONYMOUS_ONEOF_FIELD(operation, 2, BYTES , ONEOF, POINTER , UNION, google_firestore_v1_Write, delete_, delete_, 0),
3535
PB_ANONYMOUS_ONEOF_FIELD(operation, 6, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1_Write, transform, transform, &google_firestore_v1_DocumentTransform_fields),
36-
PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1_Write, update_mask, transform, &google_firestore_v1_DocumentMask_fields),
37-
PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1_Write, current_document, update_mask, &google_firestore_v1_Precondition_fields),
36+
PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_firestore_v1_Write, update_mask, transform, &google_firestore_v1_DocumentMask_fields),
37+
PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, google_firestore_v1_Write, current_document, update_mask, &google_firestore_v1_Precondition_fields),
3838
PB_LAST_FIELD
3939
};
4040

Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ typedef struct _google_firestore_v1_Write {
108108
pb_bytes_array_t *delete_;
109109
google_firestore_v1_DocumentTransform transform;
110110
};
111+
bool has_update_mask;
111112
google_firestore_v1_DocumentMask update_mask;
113+
bool has_current_document;
112114
google_firestore_v1_Precondition current_document;
113115
/* @@protoc_insertion_point(struct:google_firestore_v1_Write) */
114116
} google_firestore_v1_Write;
@@ -124,15 +126,15 @@ typedef struct _google_firestore_v1_WriteResult {
124126
/* Default values for struct fields */
125127

126128
/* Initializer values for message structs */
127-
#define google_firestore_v1_Write_init_default {0, {google_firestore_v1_Document_init_default}, google_firestore_v1_DocumentMask_init_default, google_firestore_v1_Precondition_init_default}
129+
#define google_firestore_v1_Write_init_default {0, {google_firestore_v1_Document_init_default}, false, google_firestore_v1_DocumentMask_init_default, false, google_firestore_v1_Precondition_init_default}
128130
#define google_firestore_v1_DocumentTransform_init_default {NULL, 0, NULL}
129131
#define google_firestore_v1_DocumentTransform_FieldTransform_init_default {NULL, 0, {_google_firestore_v1_DocumentTransform_FieldTransform_ServerValue_MIN}}
130132
#define google_firestore_v1_WriteResult_init_default {false, google_protobuf_Timestamp_init_default, 0, NULL}
131133
#define google_firestore_v1_DocumentChange_init_default {google_firestore_v1_Document_init_default, 0, NULL, 0, NULL}
132134
#define google_firestore_v1_DocumentDelete_init_default {NULL, false, google_protobuf_Timestamp_init_default, 0, NULL}
133135
#define google_firestore_v1_DocumentRemove_init_default {NULL, 0, NULL, google_protobuf_Timestamp_init_default}
134136
#define google_firestore_v1_ExistenceFilter_init_default {0, 0}
135-
#define google_firestore_v1_Write_init_zero {0, {google_firestore_v1_Document_init_zero}, google_firestore_v1_DocumentMask_init_zero, google_firestore_v1_Precondition_init_zero}
137+
#define google_firestore_v1_Write_init_zero {0, {google_firestore_v1_Document_init_zero}, false, google_firestore_v1_DocumentMask_init_zero, false, google_firestore_v1_Precondition_init_zero}
136138
#define google_firestore_v1_DocumentTransform_init_zero {NULL, 0, NULL}
137139
#define google_firestore_v1_DocumentTransform_FieldTransform_init_zero {NULL, 0, {_google_firestore_v1_DocumentTransform_FieldTransform_ServerValue_MIN}}
138140
#define google_firestore_v1_WriteResult_init_zero {false, google_protobuf_Timestamp_init_zero, 0, NULL}

Firestore/Protos/protos/google/firestore/v1/write.options

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@
1717
# workaround suggested in
1818
# https://github.com/nanopb/nanopb/issues/255#issuecomment-291842903
1919

20-
# update_time should not be set for deletes.
21-
google.firestore.v1.WriteResult.update_time proto3:false
2220
# read_time might not be set for deletes.
2321
google.firestore.v1.DocumentDelete.read_time proto3:false
22+
23+
# `current_document` is an optional precondition.
24+
google.firestore.v1.Write.current_document proto3:false
25+
26+
# Whether `update_mask` field is set is crucial for the backend to distinguish
27+
# between "set" and "patch" mutations.
28+
google.firestore.v1.Write.update_mask proto3:false
29+
30+
# update_time should not be set for deletes.
31+
google.firestore.v1.WriteResult.update_time proto3:false

Firestore/core/src/firebase/firestore/remote/serializer.cc

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ namespace {
115115
* Creates the prefix for a fully qualified resource path, without a local path
116116
* on the end.
117117
*/
118-
ResourcePath EncodeDatabaseId(const DatabaseId& database_id) {
118+
ResourcePath DatabaseName(const DatabaseId& database_id) {
119119
return ResourcePath{"projects", database_id.project_id(), "databases",
120120
database_id.database_id()};
121121
}
@@ -126,7 +126,7 @@ ResourcePath EncodeDatabaseId(const DatabaseId& database_id) {
126126
*/
127127
pb_bytes_array_t* EncodeResourceName(const DatabaseId& database_id,
128128
const ResourcePath& path) {
129-
return Serializer::EncodeString(EncodeDatabaseId(database_id)
129+
return Serializer::EncodeString(DatabaseName(database_id)
130130
.Append("documents")
131131
.Append(path)
132132
.CanonicalString());
@@ -197,15 +197,18 @@ Query InvalidQuery() {
197197
}
198198

199199
Serializer::Serializer(DatabaseId database_id)
200-
: database_id_(std::move(database_id)),
201-
database_name_(EncodeDatabaseId(database_id_).CanonicalString()) {
200+
: database_id_(std::move(database_id)) {
202201
}
203202

204203
void Serializer::FreeNanopbMessage(const pb_field_t fields[],
205204
void* dest_struct) {
206205
pb_release(fields, dest_struct);
207206
}
208207

208+
pb_bytes_array_t* Serializer::EncodeDatabaseName() const {
209+
return EncodeString(DatabaseName(database_id_).CanonicalString());
210+
}
211+
209212
google_firestore_v1_Value Serializer::EncodeFieldValue(
210213
const FieldValue& field_value) const {
211214
switch (field_value.type()) {
@@ -596,6 +599,7 @@ google_firestore_v1_Write Serializer::EncodeMutation(
596599
google_firestore_v1_Write result{};
597600

598601
if (!mutation.precondition().is_none()) {
602+
result.has_current_document = true;
599603
result.current_document = EncodePrecondition(mutation.precondition());
600604
}
601605

@@ -611,7 +615,13 @@ google_firestore_v1_Write Serializer::EncodeMutation(
611615
result.which_operation = google_firestore_v1_Write_update_tag;
612616
auto patch_mutation = static_cast<const PatchMutation&>(mutation);
613617
result.update = EncodeDocument(mutation.key(), patch_mutation.value());
614-
result.update_mask = EncodeFieldMask(patch_mutation.mask());
618+
// Note: the fact that this field is set (even if the mask is empty) is
619+
// what makes the backend treat this as a patch mutation, not a set
620+
// mutation.
621+
result.has_update_mask = true;
622+
if (patch_mutation.mask().size() != 0) {
623+
result.update_mask = EncodeFieldMask(patch_mutation.mask());
624+
}
615625
return result;
616626
}
617627

@@ -632,6 +642,13 @@ google_firestore_v1_Write Serializer::EncodeMutation(
632642
EncodeFieldTransform(field_transform);
633643
i++;
634644
}
645+
646+
// NOTE: We set a precondition of exists: true as a safety-check, since we
647+
// always combine TransformMutations with a SetMutation or PatchMutation
648+
// which (if successful) should end up with an existing document.
649+
result.has_current_document = true;
650+
result.current_document = EncodePrecondition(Precondition::Exists(true));
651+
635652
return result;
636653
}
637654

@@ -647,8 +664,10 @@ google_firestore_v1_Write Serializer::EncodeMutation(
647664

648665
Mutation Serializer::DecodeMutation(
649666
nanopb::Reader* reader, const google_firestore_v1_Write& mutation) const {
650-
Precondition precondition =
651-
DecodePrecondition(reader, mutation.current_document);
667+
auto precondition = Precondition::None();
668+
if (mutation.has_current_document) {
669+
precondition = DecodePrecondition(reader, mutation.current_document);
670+
}
652671

653672
switch (mutation.which_operation) {
654673
case google_firestore_v1_Write_update_tag: {
@@ -805,6 +824,8 @@ Serializer::EncodeFieldTransform(const FieldTransform& field_transform) const {
805824
return proto;
806825

807826
case Type::Increment: {
827+
proto.which_transform_type =
828+
google_firestore_v1_DocumentTransform_FieldTransform_increment_tag;
808829
const auto& increment = static_cast<const NumericIncrementTransform&>(
809830
field_transform.transformation());
810831
proto.increment = EncodeFieldValue(increment.operand());

Firestore/core/src/firebase/firestore/remote/serializer.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ class Serializer {
124124
*/
125125
static pb_bytes_array_t* EncodeBytes(const std::vector<uint8_t>& bytes);
126126

127+
/**
128+
* Returns the database ID, such as
129+
* `projects/{project_id}/databases/{database_id}`.
130+
*/
131+
pb_bytes_array_t* EncodeDatabaseName() const;
132+
127133
/**
128134
* Release memory allocated by the Encode* methods that return protos.
129135
*
@@ -254,6 +260,13 @@ class Serializer {
254260
nanopb::Reader* reader,
255261
const google_firestore_v1_ListenResponse& listen_response) const;
256262

263+
// Public for the sake of tests.
264+
google_firestore_v1_StructuredQuery_Filter EncodeFilters(
265+
const core::FilterList& filters) const;
266+
core::FilterList DecodeFilters(
267+
nanopb::Reader* reader,
268+
const google_firestore_v1_StructuredQuery_Filter& proto) const;
269+
257270
private:
258271
google_firestore_v1_Value EncodeNull() const;
259272
google_firestore_v1_Value EncodeBoolean(bool value) const;
@@ -299,12 +312,6 @@ class Serializer {
299312

300313
std::string EncodeLabel(local::QueryPurpose purpose) const;
301314

302-
google_firestore_v1_StructuredQuery_Filter EncodeFilters(
303-
const core::FilterList& filters) const;
304-
core::FilterList DecodeFilters(
305-
nanopb::Reader* reader,
306-
const google_firestore_v1_StructuredQuery_Filter& proto) const;
307-
308315
google_firestore_v1_StructuredQuery_Filter EncodeSingularFilter(
309316
const core::FieldFilter& filter) const;
310317
core::Filter DecodeFieldFilter(
@@ -360,7 +367,8 @@ class Serializer {
360367
const google_firestore_v1_ExistenceFilter& filter) const;
361368

362369
model::DatabaseId database_id_;
363-
const std::string database_name_;
370+
// TODO(varconst): Android caches the result of calling `EncodeDatabaseName`
371+
// as well, consider implementing that.
364372
};
365373

366374
} // namespace remote

0 commit comments

Comments
 (0)