Skip to content

BundleSerializer changes for Protobuf migration #7999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 77 additions & 74 deletions Firestore/core/src/bundle/bundle_serializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
#include "Firestore/core/src/core/order_by.h"
#include "Firestore/core/src/core/query.h"
#include "Firestore/core/src/core/target.h"
#include "Firestore/core/src/model/document.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/model/field_value.h"
#include "Firestore/core/src/model/mutable_document.h"
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/model/value_util.h"
#include "Firestore/core/src/nanopb/byte_string.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "Firestore/core/src/timestamp_internal.h"
#include "Firestore/core/src/util/statusor.h"
#include "Firestore/core/src/util/string_util.h"
Expand All @@ -55,15 +56,17 @@ using core::OrderByList;
using core::Query;
using core::Target;
using immutable::AppendOnlyList;
using model::Document;
using model::DocumentKey;
using model::FieldPath;
using model::FieldValue;
using model::MutableDocument;
using model::NaNValue;
using model::NullValue;
using model::ObjectValue;
using model::ResourcePath;
using model::SnapshotVersion;
using nanopb::ByteString;
using nanopb::Reader;
using nanopb::SetRepeatedField;
using nlohmann::json;
using util::ReadContext;
using util::StatusOr;
Expand Down Expand Up @@ -221,16 +224,16 @@ Filter DecodeUnaryFilter(JsonReader& reader, const json& filter) {

if (op == "IS_NAN") {
return FieldFilter::Create(std::move(path), Filter::Operator::Equal,
FieldValue::Nan());
NaNValue());
} else if (op == "IS_NULL") {
return FieldFilter::Create(std::move(path), Filter::Operator::Equal,
FieldValue::Null());
NullValue());
} else if (op == "IS_NOT_NAN") {
return FieldFilter::Create(std::move(path), Filter::Operator::NotEqual,
FieldValue::Nan());
NaNValue());
} else if (op == "IS_NOT_NULL") {
return FieldFilter::Create(std::move(path), Filter::Operator::NotEqual,
FieldValue::Null());
NullValue());
}

reader.Fail("Unexpected unary filter operator: " + op);
Expand Down Expand Up @@ -294,21 +297,22 @@ LimitType DecodeLimitType(JsonReader& reader, const json& query) {
}
}

FieldValue DecodeGeoPointValue(JsonReader& reader, const json& geo_json) {
double latitude = reader.OptionalDouble("latitude", geo_json, 0.0);
double longitude = reader.OptionalDouble("longitude", geo_json, 0.0);

return FieldValue::FromGeoPoint(GeoPoint(latitude, longitude));
google_type_LatLng DecodeGeoPointValue(JsonReader& reader,
const json& geo_json) {
google_type_LatLng result{};
result.latitude = reader.OptionalDouble("latitude", geo_json, 0.0);
result.longitude = reader.OptionalDouble("longitude", geo_json, 0.0);
return result;
}

FieldValue DecodeBytesValue(JsonReader& reader,
const std::string& bytes_string) {
pb_bytes_array_t* DecodeBytesValue(JsonReader& reader,
const std::string& bytes_string) {
std::string decoded;
if (!absl::Base64Unescape(bytes_string, &decoded)) {
reader.Fail("Failed to decode bytesValue string into binary form");
return {};
}
return FieldValue::FromBlob(ByteString((decoded)));
return nanopb::MakeBytesArray(decoded);
}

} // namespace
Expand Down Expand Up @@ -522,13 +526,13 @@ BundledQuery BundleSerializer::DecodeBundledQuery(

auto start_at_bound = DecodeBound(reader, structured_query, "startAt");
std::shared_ptr<Bound> start_at;
if (!start_at_bound.position().empty()) {
if (start_at_bound.position().values_count > 0) {
start_at = std::make_shared<Bound>(std::move(start_at_bound));
}

auto end_at_bound = DecodeBound(reader, structured_query, "endAt");
std::shared_ptr<Bound> end_at;
if (!end_at_bound.position().empty()) {
if (end_at_bound.position().values_count > 0) {
end_at = std::make_shared<Bound>(std::move(end_at_bound));
}

Expand Down Expand Up @@ -591,7 +595,7 @@ Filter BundleSerializer::DecodeFieldFilter(JsonReader& reader,
const auto& op_string = reader.RequiredString("op", filter);
auto op = DecodeFieldFilterOperator(reader, op_string);

FieldValue value =
google_firestore_v1_Value value =
DecodeValue(reader, reader.RequiredObject("value", filter));

// Return early if !ok(), because `FieldFilter::Create` will abort with
Expand Down Expand Up @@ -632,63 +636,75 @@ Bound BundleSerializer::DecodeBound(JsonReader& reader,
}

const json& bound_json = reader.RequiredObject(bound_name, query);
std::vector<json> values = reader.RequiredArray("values", bound_json);
bool before = reader.OptionalBool("before", bound_json);

std::vector<FieldValue> positions;

for (const auto& value : reader.RequiredArray("values", bound_json)) {
positions.push_back(DecodeValue(reader, value));
}

return Bound(std::move(positions), before);
google_firestore_v1_ArrayValue positions{};
SetRepeatedField(&positions.values, &positions.values_count, values,
[&](const json& j) { return DecodeValue(reader, j); });
return Bound(positions, before);
}

FieldValue BundleSerializer::DecodeValue(JsonReader& reader,
const json& value) const {
google_firestore_v1_Value BundleSerializer::DecodeValue(
JsonReader& reader, const json& value) const {
if (!value.is_object()) {
reader.Fail("'value' is not encoded as JSON object");
return {};
}

google_firestore_v1_Value result{};
if (value.contains("nullValue")) {
return FieldValue::Null();
result.which_value_type = google_firestore_v1_Value_null_value_tag;
} else if (value.contains("booleanValue")) {
result.which_value_type = google_firestore_v1_Value_boolean_value_tag;
auto val = value.at("booleanValue");
if (!val.is_boolean()) {
reader.Fail("'booleanValue' is not encoded as a valid boolean");
return {};
}
return FieldValue::FromBoolean(val.get<bool>());
result.boolean_value = val.get<bool>();
} else if (value.contains("integerValue")) {
return FieldValue::FromInteger(
reader.RequiredInt<int64_t>("integerValue", value));
result.which_value_type = google_firestore_v1_Value_integer_value_tag;
result.integer_value = reader.RequiredInt<int64_t>("integerValue", value);
} else if (value.contains("doubleValue")) {
return FieldValue::FromDouble(reader.RequiredDouble("doubleValue", value));
result.which_value_type = google_firestore_v1_Value_double_value_tag;
result.double_value = reader.RequiredDouble("doubleValue", value);
} else if (value.contains("timestampValue")) {
auto val = DecodeTimestamp(reader, value.at("timestampValue"));
return FieldValue::FromTimestamp(val);
result.which_value_type = google_firestore_v1_Value_timestamp_value_tag;
result.timestamp_value.seconds = val.seconds();
result.timestamp_value.nanos = val.nanoseconds();
} else if (value.contains("stringValue")) {
auto val = reader.RequiredString("stringValue", value);
return FieldValue::FromString(std::move(val));
result.which_value_type = google_firestore_v1_Value_string_value_tag;
result.string_value =
nanopb::MakeBytesArray(reader.RequiredString("stringValue", value));
} else if (value.contains("bytesValue")) {
return DecodeBytesValue(reader, reader.RequiredString("bytesValue", value));
result.which_value_type = google_firestore_v1_Value_bytes_value_tag;
result.bytes_value =
DecodeBytesValue(reader, reader.RequiredString("bytesValue", value));
} else if (value.contains("referenceValue")) {
return DecodeReferenceValue(reader,
reader.RequiredString("referenceValue", value));
result.which_value_type = google_firestore_v1_Value_reference_value_tag;
result.reference_value =
nanopb::MakeBytesArray(reader.RequiredString("referenceValue", value));
} else if (value.contains("geoPointValue")) {
return DecodeGeoPointValue(reader, value.at("geoPointValue"));
result.which_value_type = google_firestore_v1_Value_geo_point_value_tag;
result.geo_point_value =
DecodeGeoPointValue(reader, value.at("geoPointValue"));
} else if (value.contains("arrayValue")) {
return DecodeArrayValue(reader, value.at("arrayValue"));
result.which_value_type = google_firestore_v1_Value_array_value_tag;
result.array_value = DecodeArrayValue(reader, value.at("arrayValue"));
} else if (value.contains("mapValue")) {
return DecodeMapValue(reader, value.at("mapValue"));
result.which_value_type = google_firestore_v1_Value_map_value_tag;
result.map_value = DecodeMapValue(reader, value.at("mapValue"));
} else {
reader.Fail("Failed to decode value, no type is recognized");
return {};
}
return result;
}

FieldValue BundleSerializer::DecodeMapValue(JsonReader& reader,
const json& map_json) const {
google_firestore_v1_MapValue BundleSerializer::DecodeMapValue(
JsonReader& reader, const json& map_json) const {
if (!map_json.is_object() || !map_json.contains("fields")) {
reader.Fail("mapValue is not a valid map");
return {};
Expand All @@ -699,37 +715,25 @@ FieldValue BundleSerializer::DecodeMapValue(JsonReader& reader,
return {};
}

immutable::SortedMap<std::string, FieldValue> field_values;
for (auto it = fields.begin(); it != fields.end(); ++it) {
field_values =
field_values.insert(it.key(), DecodeValue(reader, it.value()));
}

return FieldValue::FromMap(std::move(field_values));
google_firestore_v1_MapValue map_value{};
SetRepeatedField(&map_value.fields, &map_value.fields_count, fields,
[&](const std::pair<std::string, json>& entry) {
google_firestore_v1_MapValue_FieldsEntry result{};
result.key = nanopb::MakeBytesArray(entry.first);
result.value = DecodeValue(reader, entry.second);
return result;
});
return map_value;
}

FieldValue BundleSerializer::DecodeArrayValue(JsonReader& reader,
const json& array_json) const {
google_firestore_v1_ArrayValue BundleSerializer::DecodeArrayValue(
JsonReader& reader, const json& array_json) const {
const auto& values = reader.RequiredArray("values", array_json);
std::vector<FieldValue> field_values;
for (const json& json_value : values) {
field_values.push_back(DecodeValue(reader, json_value));
}
if (!reader.ok()) {
return {};
}

return FieldValue::FromArray(std::move(field_values));
}

FieldValue BundleSerializer::DecodeReferenceValue(
JsonReader& reader, const std::string& ref_string) const {
// Check if ref_string is indeed a valid string passed in.
if (!reader.ok()) {
return {};
}

return rpc_serializer_.DecodeReference(&reader, ref_string);
google_firestore_v1_ArrayValue array_value{};
SetRepeatedField(&array_value.values, &array_value.values_count, values,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the behavior changed..there was an early exit if decoding a value failed. But maybe it does not matter..if there is no easy way to bring that back, I am OK to leave it as it is now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It not as easy anymore to do the validation since I am generally just passing through the protos. I could handle bundles differently, but for now I prefer to keep the current handling (and show a warning in DocumentSnapshot.data())

[&](const json& j) { return DecodeValue(reader, j); });
return array_value;
}

BundledDocumentMetadata BundleSerializer::DecodeDocumentMetadata(
Expand Down Expand Up @@ -778,9 +782,8 @@ BundleDocument BundleSerializer::DecodeDocument(JsonReader& reader,

auto map_value = DecodeMapValue(reader, document);

return BundleDocument(Document(ObjectValue::FromMap(map_value.object_value()),
std::move(key), update_time,
model::DocumentState::kSynced));
return BundleDocument(MutableDocument::FoundDocument(
key, update_time, ObjectValue::FromMapValue(map_value)));
}

} // namespace bundle
Expand Down