From e12b997c6147663c692233a7e29a7433f4aee6f6 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Fri, 4 May 2018 15:21:22 -0400 Subject: [De]serialize DocumentKeys via the remote Serializer (#1212) --- .../src/firebase/firestore/remote/serializer.cc | 75 ++++++++++++++++++++++ .../src/firebase/firestore/remote/serializer.h | 39 ++++++----- 2 files changed, 99 insertions(+), 15 deletions(-) (limited to 'Firestore/core/src') diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index 2a89515..e81ea2d 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -25,14 +25,18 @@ #include #include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" namespace firebase { namespace firestore { namespace remote { +using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldValue; using firebase::firestore::model::ObjectValue; +using firebase::firestore::model::ResourcePath; using firebase::firestore::util::Status; using firebase::firestore::util::StatusOr; @@ -719,6 +723,64 @@ ObjectValue::Map DecodeObject(Reader* reader) { }); } +/** + * Creates the prefix for a fully qualified resource path, without a local path + * on the end. + */ +ResourcePath EncodeDatabaseId(const DatabaseId& database_id) { + return ResourcePath{"projects", database_id.project_id(), "databases", + database_id.database_id()}; +} + +/** + * Encodes a databaseId and resource path into the following form: + * /projects/$projectId/database/$databaseId/documents/$path + */ +std::string EncodeResourceName(const DatabaseId& database_id, + const ResourcePath& path) { + return EncodeDatabaseId(database_id) + .Append("documents") + .Append(path) + .CanonicalString(); +} + +/** + * Validates that a path has a prefix that looks like a valid encoded + * databaseId. + */ +bool IsValidResourceName(const ResourcePath& path) { + // Resource names have at least 4 components (project ID, database ID) + // and commonly the (root) resource type, e.g. documents + return path.size() >= 4 && path[0] == "projects" && path[2] == "databases"; +} + +/** + * Decodes a fully qualified resource name into a resource path and validates + * that there is a project and database encoded in the path. There are no + * guarantees that a local path is also encoded in this resource name. + */ +ResourcePath DecodeResourceName(absl::string_view encoded) { + ResourcePath resource = ResourcePath::FromString(encoded); + FIREBASE_ASSERT_MESSAGE(IsValidResourceName(resource), + "Tried to deserialize invalid key %s", + resource.CanonicalString().c_str()); + return resource; +} + +/** + * Decodes a fully qualified resource name into a resource path and validates + * that there is a project and database encoded in the path along with a local + * path. + */ +ResourcePath ExtractLocalPathFromResourceName( + const ResourcePath& resource_name) { + FIREBASE_ASSERT_MESSAGE( + resource_name.size() > 4 && resource_name[4] == "documents", + "Tried to deserialize invalid key %s", + resource_name.CanonicalString().c_str()); + return resource_name.PopFirst(5); +} + } // namespace Status Serializer::EncodeFieldValue(const FieldValue& field_value, @@ -739,6 +801,19 @@ StatusOr Serializer::DecodeFieldValue(const uint8_t* bytes, } } +std::string Serializer::EncodeKey(const DocumentKey& key) const { + return EncodeResourceName(database_id_, key.path()); +} + +DocumentKey Serializer::DecodeKey(absl::string_view name) const { + ResourcePath resource = DecodeResourceName(name); + FIREBASE_ASSERT_MESSAGE(resource[1] == database_id_.project_id(), + "Tried to deserialize key from different project."); + FIREBASE_ASSERT_MESSAGE(resource[3] == database_id_.database_id(), + "Tried to deserialize key from different database."); + return DocumentKey{ExtractLocalPathFromResourceName(resource)}; +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 7d13583..86aa6e2 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -19,13 +19,17 @@ #include #include +#include #include +#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/field_value.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" namespace firebase { namespace firestore { @@ -46,20 +50,13 @@ namespace remote { // interpret." Adjust for C++. class Serializer { public: - Serializer() { + /** + * @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) { } - // 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 bytes. @@ -101,9 +98,21 @@ class Serializer { return DecodeFieldValue(bytes.data(), bytes.size()); } + /** + * Encodes the given document key as a fully qualified name. This includes the + * databaseId associated with this Serializer and the key path. + */ + std::string EncodeKey( + const firebase::firestore::model::DocumentKey& key) const; + + /** + * Decodes the given document key from a fully qualified name. + */ + firebase::firestore::model::DocumentKey DecodeKey( + absl::string_view name) const; + private: - // TODO(rsgowman): We don't need the database_id_ yet (but will eventually). - // model::DatabaseId* database_id_; + const firebase::firestore::model::DatabaseId& database_id_; }; } // namespace remote -- cgit v1.2.3