From 0056c2f789ebbc60f060530a7a96a727b8c14899 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Thu, 22 Sep 2022 10:20:22 -0700 Subject: [PATCH] feat(storage): easier mocks with `ObjectMetadata` (#9899) Recapitulates the changes to make `BucketMetadata` easier to mock. This change adds modifiers for all the fields in `ObjectMetadata` and removes the need for `internal::CommonMetadata<>`. Since `internal::CommonMetadata<>` is not needed, I deprecated the class, removed all references to the class or its header file, and refactor any useful code out of its header file. --- google/cloud/storage/bucket_metadata.h | 2 +- .../storage/google_cloud_cpp_storage.bzl | 1 + .../storage/google_cloud_cpp_storage.cmake | 1 + .../storage/internal/access_control_common.h | 1 - .../internal/bucket_metadata_parser.cc | 2 +- .../cloud/storage/internal/common_metadata.h | 49 +-- .../storage/internal/common_metadata_parser.h | 6 +- .../internal/grpc_object_metadata_parser.cc | 84 ++--- .../storage/internal/grpc_owner_parser.h | 2 +- .../storage/internal/lifecycle_rule_parser.cc | 2 +- .../internal/object_access_control_parser.cc | 2 +- .../internal/object_metadata_parser.cc | 298 +++++++++++++----- google/cloud/storage/object_access_control.h | 1 - google/cloud/storage/object_metadata.cc | 52 +-- google/cloud/storage/object_metadata.h | 223 ++++++++++--- google/cloud/storage/object_metadata_test.cc | 28 +- google/cloud/storage/owner.h | 65 ++++ 17 files changed, 592 insertions(+), 227 deletions(-) create mode 100644 google/cloud/storage/owner.h diff --git a/google/cloud/storage/bucket_metadata.h b/google/cloud/storage/bucket_metadata.h index 263cad94b3c45..17aae706a8d33 100644 --- a/google/cloud/storage/bucket_metadata.h +++ b/google/cloud/storage/bucket_metadata.h @@ -16,10 +16,10 @@ #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_BUCKET_METADATA_H #include "google/cloud/storage/bucket_access_control.h" -#include "google/cloud/storage/internal/common_metadata.h" #include "google/cloud/storage/internal/patch_builder.h" #include "google/cloud/storage/lifecycle_rule.h" #include "google/cloud/storage/object_access_control.h" +#include "google/cloud/storage/owner.h" #include "google/cloud/storage/version.h" #include "google/cloud/optional.h" #include "absl/types/optional.h" diff --git a/google/cloud/storage/google_cloud_cpp_storage.bzl b/google/cloud/storage/google_cloud_cpp_storage.bzl index ddb228c6ddcc7..003402b4cb604 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.bzl +++ b/google/cloud/storage/google_cloud_cpp_storage.bzl @@ -122,6 +122,7 @@ google_cloud_cpp_storage_hdrs = [ "object_write_stream.h", "options.h", "override_default_project.h", + "owner.h", "parallel_upload.h", "policy_document.h", "retry_policy.h", diff --git a/google/cloud/storage/google_cloud_cpp_storage.cmake b/google/cloud/storage/google_cloud_cpp_storage.cmake index e8a52804b8e10..285acd7c6f5d1 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.cmake +++ b/google/cloud/storage/google_cloud_cpp_storage.cmake @@ -208,6 +208,7 @@ add_library( object_write_stream.h options.h override_default_project.h + owner.h parallel_upload.cc parallel_upload.h policy_document.cc diff --git a/google/cloud/storage/internal/access_control_common.h b/google/cloud/storage/internal/access_control_common.h index d53d32ee39051..852ee03056f9b 100644 --- a/google/cloud/storage/internal/access_control_common.h +++ b/google/cloud/storage/internal/access_control_common.h @@ -15,7 +15,6 @@ #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ACCESS_CONTROL_COMMON_H #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ACCESS_CONTROL_COMMON_H -#include "google/cloud/storage/internal/common_metadata.h" #include "google/cloud/storage/version.h" #include "google/cloud/status.h" #include "absl/types/optional.h" diff --git a/google/cloud/storage/internal/bucket_metadata_parser.cc b/google/cloud/storage/internal/bucket_metadata_parser.cc index c2b217c202082..52e851a5f0940 100644 --- a/google/cloud/storage/internal/bucket_metadata_parser.cc +++ b/google/cloud/storage/internal/bucket_metadata_parser.cc @@ -14,8 +14,8 @@ #include "google/cloud/storage/internal/bucket_metadata_parser.h" #include "google/cloud/storage/internal/bucket_access_control_parser.h" -#include "google/cloud/storage/internal/common_metadata_parser.h" #include "google/cloud/storage/internal/lifecycle_rule_parser.h" +#include "google/cloud/storage/internal/metadata_parser.h" #include "google/cloud/storage/internal/object_access_control_parser.h" #include "absl/strings/str_format.h" #include diff --git a/google/cloud/storage/internal/common_metadata.h b/google/cloud/storage/internal/common_metadata.h index 2794d050e873b..abd5e12e5c07a 100644 --- a/google/cloud/storage/internal/common_metadata.h +++ b/google/cloud/storage/internal/common_metadata.h @@ -15,6 +15,7 @@ #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_COMMON_METADATA_H #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_COMMON_METADATA_H +#include "google/cloud/storage/owner.h" #include "google/cloud/storage/version.h" #include "google/cloud/status_or.h" #include "absl/types/optional.h" @@ -27,38 +28,6 @@ namespace google { namespace cloud { namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -/// A simple wrapper for the `owner` field in `internal::CommonMetadata`. -struct Owner { - std::string entity; - std::string entity_id; -}; - -inline bool operator==(Owner const& lhs, Owner const& rhs) { - return std::tie(lhs.entity, lhs.entity_id) == - std::tie(rhs.entity, rhs.entity_id); -} - -inline bool operator<(Owner const& lhs, Owner const& rhs) { - return std::tie(lhs.entity, lhs.entity_id) < - std::tie(rhs.entity, rhs.entity_id); -} - -inline bool operator!=(Owner const& lhs, Owner const& rhs) { - return std::rel_ops::operator!=(lhs, rhs); -} - -inline bool operator>(Owner const& lhs, Owner const& rhs) { - return std::rel_ops::operator>(lhs, rhs); -} - -inline bool operator<=(Owner const& lhs, Owner const& rhs) { - return std::rel_ops::operator<=(lhs, rhs); -} - -inline bool operator>=(Owner const& lhs, Owner const& rhs) { - return std::rel_ops::operator>=(lhs, rhs); -} - namespace internal { struct GrpcBucketMetadataParser; struct GrpcObjectMetadataParser; @@ -73,8 +42,10 @@ struct CommonMetadataParser; * * @see https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ +// TODO(#9897) - remove this class and any references to it template -class CommonMetadata { +class GOOGLE_CLOUD_CPP_DEPRECATED( + "This class will be removed shortly after 2023-06-01") CommonMetadata { public: CommonMetadata() = default; @@ -121,8 +92,10 @@ class CommonMetadata { }; template -inline bool operator==(CommonMetadata const& lhs, - CommonMetadata const& rhs) { +GOOGLE_CLOUD_CPP_DEPRECATED( + "This class will be removed shortly after 2023-06-01") +inline bool +operator==(CommonMetadata const& lhs, CommonMetadata const& rhs) { // etag changes each time the metadata changes, so that is the best field // to short-circuit this comparison. The check the name, project number, // and metadata generation, which have the next best chance to @@ -138,8 +111,10 @@ inline bool operator==(CommonMetadata const& lhs, } template -inline bool operator!=(CommonMetadata const& lhs, - CommonMetadata const& rhs) { +GOOGLE_CLOUD_CPP_DEPRECATED( + "This class will be removed shortly after 2023-06-01") +inline bool +operator!=(CommonMetadata const& lhs, CommonMetadata const& rhs) { return std::rel_ops::operator!=(lhs, rhs); } diff --git a/google/cloud/storage/internal/common_metadata_parser.h b/google/cloud/storage/internal/common_metadata_parser.h index 4ce9425697fdc..23aa9e040fd41 100644 --- a/google/cloud/storage/internal/common_metadata_parser.h +++ b/google/cloud/storage/internal/common_metadata_parser.h @@ -20,13 +20,17 @@ #include "google/cloud/status.h" #include #include + namespace google { namespace cloud { namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN namespace internal { +// TODO(#9897) - remove this class and any references to it template -struct CommonMetadataParser { +struct GOOGLE_CLOUD_CPP_DEPRECATED( + "This class will be removed shortly after 2023-06-01") + CommonMetadataParser { static Status FromJson(CommonMetadata& result, nlohmann::json const& json) { if (!json.is_object()) { diff --git a/google/cloud/storage/internal/grpc_object_metadata_parser.cc b/google/cloud/storage/internal/grpc_object_metadata_parser.cc index fa140dbdac567..ade14c897e24f 100644 --- a/google/cloud/storage/internal/grpc_object_metadata_parser.cc +++ b/google/cloud/storage/internal/grpc_object_metadata_parser.cc @@ -86,13 +86,13 @@ ObjectMetadata GrpcObjectMetadataParser::FromProto( }; ObjectMetadata metadata; - metadata.kind_ = "storage#object"; - metadata.bucket_ = bucket_id(object); - metadata.name_ = std::move(*object.mutable_name()); - metadata.generation_ = object.generation(); - metadata.etag_ = object.etag(); - metadata.id_ = metadata.bucket() + "/" + metadata.name() + "/" + - std::to_string(metadata.generation()); + metadata.set_kind("storage#object"); + metadata.set_bucket(bucket_id(object)); + metadata.set_name(std::move(*object.mutable_name())); + metadata.set_generation(object.generation()); + metadata.set_etag(object.etag()); + metadata.set_id(metadata.bucket() + "/" + metadata.name() + "/" + + std::to_string(metadata.generation())); auto const metadata_endpoint = [&options]() -> std::string { if (options.get() != "https://storage.googleapis.com") { return options.get(); @@ -104,23 +104,23 @@ ObjectMetadata GrpcObjectMetadataParser::FromProto( return "/storage/" + options.get(); }(); auto const rel_path = "/b/" + metadata.bucket() + "/o/" + metadata.name(); - metadata.self_link_ = metadata_endpoint + path + rel_path; - metadata.media_link_ = + metadata.set_self_link(metadata_endpoint + path + rel_path); + metadata.set_media_link( options.get() + "/download" + path + rel_path + - "?generation=" + std::to_string(metadata.generation()) + "&alt=media"; + "?generation=" + std::to_string(metadata.generation()) + "&alt=media"); - metadata.metageneration_ = object.metageneration(); + metadata.set_metageneration(object.metageneration()); if (object.has_owner()) { - metadata.owner_ = storage_internal::FromProto(*object.mutable_owner()); + metadata.set_owner(storage_internal::FromProto(*object.mutable_owner())); } - metadata.storage_class_ = std::move(*object.mutable_storage_class()); + metadata.set_storage_class(std::move(*object.mutable_storage_class())); if (object.has_create_time()) { - metadata.time_created_ = - google::cloud::internal::ToChronoTimePoint(object.create_time()); + metadata.set_time_created( + google::cloud::internal::ToChronoTimePoint(object.create_time())); } if (object.has_update_time()) { - metadata.updated_ = - google::cloud::internal::ToChronoTimePoint(object.update_time()); + metadata.set_updated( + google::cloud::internal::ToChronoTimePoint(object.update_time())); } std::vector acl; acl.reserve(object.acl_size()); @@ -129,53 +129,53 @@ ObjectMetadata GrpcObjectMetadataParser::FromProto( std::move(item), metadata.bucket(), metadata.name(), metadata.generation())); } - metadata.acl_ = std::move(acl); - metadata.cache_control_ = std::move(*object.mutable_cache_control()); - metadata.component_count_ = object.component_count(); - metadata.content_disposition_ = - std::move(*object.mutable_content_disposition()); - metadata.content_encoding_ = std::move(*object.mutable_content_encoding()); - metadata.content_language_ = std::move(*object.mutable_content_language()); - metadata.content_type_ = std::move(*object.mutable_content_type()); + metadata.set_acl(std::move(acl)); + metadata.set_cache_control(std::move(*object.mutable_cache_control())); + metadata.set_component_count(object.component_count()); + metadata.set_content_disposition( + std::move(*object.mutable_content_disposition())); + metadata.set_content_encoding(std::move(*object.mutable_content_encoding())); + metadata.set_content_language(std::move(*object.mutable_content_language())); + metadata.set_content_type(std::move(*object.mutable_content_type())); if (object.has_checksums()) { if (object.checksums().has_crc32c()) { - metadata.crc32c_ = Crc32cFromProto(object.checksums().crc32c()); + metadata.set_crc32c(Crc32cFromProto(object.checksums().crc32c())); } if (!object.checksums().md5_hash().empty()) { - metadata.md5_hash_ = MD5FromProto(object.checksums().md5_hash()); + metadata.set_md5_hash(MD5FromProto(object.checksums().md5_hash())); } } if (object.has_customer_encryption()) { - metadata.customer_encryption_ = - FromProto(std::move(*object.mutable_customer_encryption())); + metadata.set_customer_encryption( + FromProto(std::move(*object.mutable_customer_encryption()))); } if (object.has_event_based_hold()) { - metadata.event_based_hold_ = object.event_based_hold(); + metadata.set_event_based_hold(object.event_based_hold()); } - metadata.kms_key_name_ = std::move(*object.mutable_kms_key()); + metadata.set_kms_key_name(std::move(*object.mutable_kms_key())); for (auto const& kv : object.metadata()) { - metadata.metadata_[kv.first] = kv.second; + metadata.upsert_metadata(kv.first, kv.second); } if (object.has_retention_expire_time()) { - metadata.retention_expiration_time_ = + metadata.set_retention_expiration_time( google::cloud::internal::ToChronoTimePoint( - object.retention_expire_time()); + object.retention_expire_time())); } - metadata.size_ = static_cast(object.size()); - metadata.temporary_hold_ = object.temporary_hold(); + metadata.set_size(static_cast(object.size())); + metadata.set_temporary_hold(object.temporary_hold()); if (object.has_delete_time()) { - metadata.time_deleted_ = - google::cloud::internal::ToChronoTimePoint(object.delete_time()); + metadata.set_time_deleted( + google::cloud::internal::ToChronoTimePoint(object.delete_time())); } if (object.has_update_storage_class_time()) { - metadata.time_storage_class_updated_ = + metadata.set_time_storage_class_updated( google::cloud::internal::ToChronoTimePoint( - object.update_storage_class_time()); + object.update_storage_class_time())); } if (object.has_custom_time()) { - metadata.custom_time_ = - google::cloud::internal::ToChronoTimePoint(object.custom_time()); + metadata.set_custom_time( + google::cloud::internal::ToChronoTimePoint(object.custom_time())); } return metadata; diff --git a/google/cloud/storage/internal/grpc_owner_parser.h b/google/cloud/storage/internal/grpc_owner_parser.h index 30d2acefdb27c..28cafcfce2067 100644 --- a/google/cloud/storage/internal/grpc_owner_parser.h +++ b/google/cloud/storage/internal/grpc_owner_parser.h @@ -15,7 +15,7 @@ #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_GRPC_OWNER_PARSER_H #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_GRPC_OWNER_PARSER_H -#include "google/cloud/storage/internal/common_metadata.h" +#include "google/cloud/storage/owner.h" #include "google/cloud/storage/version.h" #include diff --git a/google/cloud/storage/internal/lifecycle_rule_parser.cc b/google/cloud/storage/internal/lifecycle_rule_parser.cc index 8635485817791..5af0c876c8faa 100644 --- a/google/cloud/storage/internal/lifecycle_rule_parser.cc +++ b/google/cloud/storage/internal/lifecycle_rule_parser.cc @@ -13,7 +13,7 @@ // limitations under the License. #include "google/cloud/storage/internal/lifecycle_rule_parser.h" -#include "google/cloud/storage/internal/common_metadata_parser.h" +#include "google/cloud/storage/internal/metadata_parser.h" namespace google { namespace cloud { diff --git a/google/cloud/storage/internal/object_access_control_parser.cc b/google/cloud/storage/internal/object_access_control_parser.cc index b1b71e0bf68eb..3122fd4bc25b3 100644 --- a/google/cloud/storage/internal/object_access_control_parser.cc +++ b/google/cloud/storage/internal/object_access_control_parser.cc @@ -14,7 +14,7 @@ #include "google/cloud/storage/internal/object_access_control_parser.h" #include "google/cloud/storage/internal/access_control_common_parser.h" -#include "google/cloud/storage/internal/common_metadata_parser.h" +#include "google/cloud/storage/internal/metadata_parser.h" #include namespace google { diff --git a/google/cloud/storage/internal/object_metadata_parser.cc b/google/cloud/storage/internal/object_metadata_parser.cc index e672295ce62df..611ff37d12baa 100644 --- a/google/cloud/storage/internal/object_metadata_parser.cc +++ b/google/cloud/storage/internal/object_metadata_parser.cc @@ -13,10 +13,11 @@ // limitations under the License. #include "google/cloud/storage/internal/object_metadata_parser.h" -#include "google/cloud/storage/internal/common_metadata_parser.h" +#include "google/cloud/storage/internal/metadata_parser.h" #include "google/cloud/storage/internal/object_access_control_parser.h" #include "google/cloud/internal/format_time_point.h" #include +#include namespace google { namespace cloud { @@ -37,84 +38,235 @@ void SetIfNotEmpty(nlohmann::json& json, char const* key, } json[key] = value; } + +Status ParseAcl(ObjectMetadata& meta, nlohmann::json const& json) { + auto i = json.find("acl"); + if (i == json.end()) return Status{}; + std::vector acl; + for (auto const& kv : i->items()) { + auto parsed = ObjectAccessControlParser::FromJson(kv.value()); + if (!parsed) return std::move(parsed).status(); + acl.push_back(*std::move(parsed)); + } + meta.set_acl(std::move(acl)); + return Status{}; +} + +Status ParseComponentCount(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseIntField(json, "componentCount"); + if (!v) return std::move(v).status(); + meta.set_component_count(*v); + return Status{}; +} + +Status ParseCustomTime(ObjectMetadata& meta, nlohmann::json const& json) { + auto f = json.find("customTime"); + if (f == json.end()) return Status{}; + auto v = internal::ParseTimestampField(json, "customTime"); + if (!v) return std::move(v).status(); + meta.set_custom_time(*v); + return Status{}; +} + +Status ParseCustomerEncryption(ObjectMetadata& meta, + nlohmann::json const& json) { + auto f = json.find("customerEncryption"); + if (f == json.end()) return Status{}; + CustomerEncryption e; + e.encryption_algorithm = f->value("encryptionAlgorithm", ""); + e.key_sha256 = f->value("keySha256", ""); + meta.set_customer_encryption(std::move(e)); + return Status{}; +} + +Status ParseEventBasedHold(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseBoolField(json, "eventBasedHold"); + if (!v) return std::move(v).status(); + meta.set_event_based_hold(*v); + return Status{}; +} + +Status ParseGeneration(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseLongField(json, "generation"); + if (!v) return std::move(v).status(); + meta.set_generation(*v); + return Status{}; +} + +Status ParseMetageneration(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseLongField(json, "metageneration"); + if (!v) return std::move(v).status(); + meta.set_metageneration(*v); + return Status{}; +} + +Status ParseMetadata(ObjectMetadata& meta, nlohmann::json const& json) { + auto f = json.find("metadata"); + if (f == json.end()) return Status{}; + std::map metadata; + for (auto const& kv : f->items()) { + metadata.emplace(kv.key(), kv.value().get()); + } + meta.mutable_metadata() = std::move(metadata); + return Status{}; +} + +Status ParseOwner(ObjectMetadata& meta, nlohmann::json const& json) { + auto f = json.find("owner"); + if (f == json.end()) return Status{}; + Owner owner; + owner.entity = f->value("entity", ""); + owner.entity_id = f->value("entityId", ""); + meta.set_owner(std::move(owner)); + return Status{}; +} + +Status ParseRetentionExpirationTime(ObjectMetadata& meta, + nlohmann::json const& json) { + auto v = internal::ParseTimestampField(json, "retentionExpirationTime"); + if (!v) return std::move(v).status(); + meta.set_retention_expiration_time(*v); + return Status{}; +} + +Status ParseSize(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseUnsignedLongField(json, "size"); + if (!v) return std::move(v).status(); + meta.set_size(*v); + return Status{}; +} + +Status ParseTemporaryHold(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = internal::ParseBoolField(json, "temporaryHold"); + if (!v) return std::move(v).status(); + meta.set_temporary_hold(*v); + return Status{}; +} + +Status ParseTimeCreated(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = ParseTimestampField(json, "timeCreated"); + if (!v) return std::move(v).status(); + meta.set_time_created(*std::move(v)); + return Status{}; +} + +Status ParseTimeDeleted(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = ParseTimestampField(json, "timeDeleted"); + if (!v) return std::move(v).status(); + meta.set_time_deleted(*std::move(v)); + return Status{}; +} + +Status ParseTimeStorageClassUpdated(ObjectMetadata& meta, + nlohmann::json const& json) { + auto v = ParseTimestampField(json, "timeStorageClassUpdated"); + if (!v) return std::move(v).status(); + meta.set_time_storage_class_updated(*std::move(v)); + return Status{}; +} + +Status ParseUpdated(ObjectMetadata& meta, nlohmann::json const& json) { + auto v = ParseTimestampField(json, "updated"); + if (!v) return std::move(v).status(); + meta.set_updated(*std::move(v)); + return Status{}; +} + } // namespace StatusOr ObjectMetadataParser::FromJson( nlohmann::json const& json) { - if (!json.is_object()) { - return Status(StatusCode::kInvalidArgument, __func__); - } - ObjectMetadata result{}; - auto status = CommonMetadataParser::FromJson(result, json); - if (!status.ok()) return status; - - if (json.count("acl") != 0) { - for (auto const& kv : json["acl"].items()) { - auto parsed = ObjectAccessControlParser::FromJson(kv.value()); - if (!parsed.ok()) { - return std::move(parsed).status(); - } - result.acl_.emplace_back(std::move(*parsed)); - } - } + if (!json.is_object()) return Status(StatusCode::kInvalidArgument, __func__); - result.bucket_ = json.value("bucket", ""); - result.cache_control_ = json.value("cacheControl", ""); - auto component_count = internal::ParseIntField(json, "componentCount"); - if (!component_count) return std::move(component_count).status(); - result.component_count_ = *component_count; - result.content_disposition_ = json.value("contentDisposition", ""); - result.content_encoding_ = json.value("contentEncoding", ""); - result.content_language_ = json.value("contentLanguage", ""); - result.content_type_ = json.value("contentType", ""); - result.crc32c_ = json.value("crc32c", ""); - if (json.count("customerEncryption") != 0) { - auto const& field = json["customerEncryption"]; - CustomerEncryption e; - e.encryption_algorithm = field.value("encryptionAlgorithm", ""); - e.key_sha256 = field.value("keySha256", ""); - result.customer_encryption_ = std::move(e); - } - auto event_based_hold = internal::ParseBoolField(json, "eventBasedHold"); - if (!event_based_hold) return std::move(event_based_hold).status(); - result.event_based_hold_ = *event_based_hold; - auto generation = internal::ParseLongField(json, "generation"); - if (!generation) return std::move(generation).status(); - result.generation_ = *generation; - result.kms_key_name_ = json.value("kmsKeyName", ""); - result.md5_hash_ = json.value("md5Hash", ""); - result.media_link_ = json.value("mediaLink", ""); - if (json.count("metadata") > 0) { - for (auto const& kv : json["metadata"].items()) { - result.metadata_.emplace(kv.key(), kv.value().get()); - } - } - auto expiration_time = - internal::ParseTimestampField(json, "retentionExpirationTime"); - if (!expiration_time) return std::move(expiration_time).status(); - result.retention_expiration_time_ = *expiration_time; - auto size = internal::ParseUnsignedLongField(json, "size"); - if (!size) return std::move(size).status(); - result.size_ = *size; - auto temporary_hold = internal::ParseBoolField(json, "temporaryHold"); - if (!temporary_hold) return std::move(temporary_hold).status(); - result.temporary_hold_ = *temporary_hold; - auto time_deleted = internal::ParseTimestampField(json, "timeDeleted"); - if (!time_deleted) return std::move(time_deleted).status(); - result.time_deleted_ = *time_deleted; - auto time_storage_class_updated = - internal::ParseTimestampField(json, "timeStorageClassUpdated"); - if (!time_storage_class_updated) - return std::move(time_storage_class_updated).status(); - result.time_storage_class_updated_ = *time_storage_class_updated; - if (json.count("customTime") == 0) { - result.custom_time_.reset(); - } else { - auto v = internal::ParseTimestampField(json, "customTime"); - if (!v) return std::move(v).status(); - result.custom_time_ = *v; + using Parser = std::function; + Parser parsers[] = { + ParseAcl, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_bucket(json.value("bucket", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_cache_control(json.value("cacheControl", "")); + return Status{}; + }, + ParseComponentCount, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_content_disposition(json.value("contentDisposition", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_content_encoding(json.value("contentEncoding", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_content_language(json.value("contentLanguage", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_content_type(json.value("contentType", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_crc32c(json.value("crc32c", "")); + return Status{}; + }, + ParseCustomTime, + ParseCustomerEncryption, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_etag(json.value("etag", "")); + return Status{}; + }, + ParseEventBasedHold, + ParseGeneration, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_id(json.value("id", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_kind(json.value("kind", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_kms_key_name(json.value("kmsKeyName", "")); + return Status{}; + }, + ParseMetageneration, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_md5_hash(json.value("md5Hash", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_media_link(json.value("mediaLink", "")); + return Status{}; + }, + ParseMetadata, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_name(json.value("name", "")); + return Status{}; + }, + ParseOwner, + ParseRetentionExpirationTime, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_self_link(json.value("selfLink", "")); + return Status{}; + }, + [](ObjectMetadata& meta, nlohmann::json const& json) { + meta.set_storage_class(json.value("storageClass", "")); + return Status{}; + }, + ParseSize, + ParseTemporaryHold, + ParseTimeCreated, + ParseTimeDeleted, + ParseTimeStorageClassUpdated, + ParseUpdated, + }; + ObjectMetadata meta; + for (auto const& p : parsers) { + auto status = p(meta, json); + if (!status.ok()) return status; } - return result; + return meta; } StatusOr ObjectMetadataParser::FromString( diff --git a/google/cloud/storage/object_access_control.h b/google/cloud/storage/object_access_control.h index baabd02be2968..df26b7c163dbd 100644 --- a/google/cloud/storage/object_access_control.h +++ b/google/cloud/storage/object_access_control.h @@ -16,7 +16,6 @@ #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OBJECT_ACCESS_CONTROL_H #include "google/cloud/storage/internal/access_control_common.h" -#include "google/cloud/storage/internal/common_metadata.h" #include "google/cloud/storage/internal/patch_builder.h" #include "google/cloud/storage/version.h" #include "google/cloud/status_or.h" diff --git a/google/cloud/storage/object_metadata.cc b/google/cloud/storage/object_metadata.cc index 13a07651f2720..44ed143e9652c 100644 --- a/google/cloud/storage/object_metadata.cc +++ b/google/cloud/storage/object_metadata.cc @@ -27,26 +27,38 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN using ::google::cloud::internal::FormatRfc3339; bool operator==(ObjectMetadata const& lhs, ObjectMetadata const& rhs) { - return static_cast const&>(lhs) == - rhs && - lhs.acl_ == rhs.acl_ && lhs.bucket_ == rhs.bucket_ && - lhs.cache_control_ == rhs.cache_control_ && - lhs.component_count_ == rhs.component_count_ && - lhs.content_disposition_ == rhs.content_disposition_ && - lhs.content_encoding_ == rhs.content_encoding_ && - lhs.content_language_ == rhs.content_language_ && - lhs.content_type_ == rhs.content_type_ && lhs.crc32c_ == rhs.crc32c_ && - lhs.customer_encryption_ == rhs.customer_encryption_ && - lhs.event_based_hold_ == rhs.event_based_hold_ && - lhs.generation_ == rhs.generation_ && - lhs.kms_key_name_ == rhs.kms_key_name_ && - lhs.md5_hash_ == rhs.md5_hash_ && lhs.media_link_ == rhs.media_link_ && - lhs.metadata_ == rhs.metadata_ && - lhs.retention_expiration_time_ == rhs.retention_expiration_time_ && - lhs.temporary_hold_ == rhs.temporary_hold_ && - lhs.time_deleted_ == rhs.time_deleted_ && - lhs.time_storage_class_updated_ == rhs.time_storage_class_updated_ && - lhs.size_ == rhs.size_ && lhs.custom_time_ == rhs.custom_time_; + return lhs.acl_ == rhs.acl_ && lhs.bucket_ == rhs.bucket_ // + && lhs.cache_control_ == rhs.cache_control_ // + && lhs.component_count_ == rhs.component_count_ // + && lhs.content_disposition_ == rhs.content_disposition_ // + && lhs.content_encoding_ == rhs.content_encoding_ // + && lhs.content_language_ == rhs.content_language_ // + && lhs.content_type_ == rhs.content_type_ // + && lhs.crc32c_ == rhs.crc32c_ // + && lhs.custom_time_ == rhs.custom_time_ // + && lhs.customer_encryption_ == rhs.customer_encryption_ // + && lhs.etag_ == rhs.etag_ // + && lhs.event_based_hold_ == rhs.event_based_hold_ // + && lhs.generation_ == rhs.generation_ // + && lhs.id_ == rhs.id_ // + && lhs.kind_ == rhs.kind_ // + && lhs.kms_key_name_ == rhs.kms_key_name_ // + && lhs.md5_hash_ == rhs.md5_hash_ // + && lhs.media_link_ == rhs.media_link_ // + && lhs.metadata_ == rhs.metadata_ // + && lhs.metageneration_ == rhs.metageneration_ // + && lhs.name_ == rhs.name_ // + && lhs.owner_ == rhs.owner_ // + && lhs.retention_expiration_time_ == rhs.retention_expiration_time_ // + && lhs.self_link_ == rhs.self_link_ // + && lhs.size_ == rhs.size_ // + && lhs.storage_class_ == rhs.storage_class_ // + && lhs.temporary_hold_ == rhs.temporary_hold_ // + && lhs.time_created_ == rhs.time_created_ // + && lhs.time_deleted_ == rhs.time_deleted_ // + && (lhs.time_storage_class_updated_ == + rhs.time_storage_class_updated_) // + && lhs.updated_ == rhs.updated_; } std::ostream& operator<<(std::ostream& os, ObjectMetadata const& rhs) { diff --git a/google/cloud/storage/object_metadata.h b/google/cloud/storage/object_metadata.h index 6dbbfb9523eda..67b5b73469ac4 100644 --- a/google/cloud/storage/object_metadata.h +++ b/google/cloud/storage/object_metadata.h @@ -15,9 +15,9 @@ #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OBJECT_METADATA_H #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OBJECT_METADATA_H -#include "google/cloud/storage/internal/common_metadata.h" #include "google/cloud/storage/internal/complex_option.h" #include "google/cloud/storage/object_access_control.h" +#include "google/cloud/storage/owner.h" #include "google/cloud/storage/version.h" #include "google/cloud/optional.h" #include "google/cloud/status_or.h" @@ -96,7 +96,7 @@ inline bool operator>=(CustomerEncryption const& lhs, * @see https://cloud.google.com/storage/docs/json_api/v1/objects for a more * detailed description of each attribute and their effects. */ -class ObjectMetadata : private internal::CommonMetadata { +class ObjectMetadata { public: ObjectMetadata() = default; @@ -118,6 +118,12 @@ class ObjectMetadata : private internal::CommonMetadata { /// The name of the bucket containing this object. std::string const& bucket() const { return bucket_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_bucket(std::string v) { + bucket_ = std::move(v); + return *this; + } + /// The `cacheControl` attribute. std::string const& cache_control() const { return cache_control_; } @@ -130,6 +136,12 @@ class ObjectMetadata : private internal::CommonMetadata { /// The number of components, for objects built using `ComposeObject()`. std::int32_t component_count() const { return component_count_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_component_count(std::int32_t v) { + component_count_ = v; + return *this; + } + /// The `contentDisposition` attribute. std::string content_disposition() const { return content_disposition_; } @@ -169,6 +181,33 @@ class ObjectMetadata : private internal::CommonMetadata { /// The `CRC32C` checksum for the object contents. std::string const& crc32c() const { return crc32c_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_crc32c(std::string v) { + crc32c_ = std::move(v); + return *this; + } + + /// Returns `true` if the object has a `customTime` attribute. + bool has_custom_time() const { return custom_time_.has_value(); } + + /// Returns the object's `customTime` or the system clock's epoch. + std::chrono::system_clock::time_point custom_time() const { + return custom_time_.value_or(std::chrono::system_clock::time_point{}); + } + + /// Changes the `customTime` attribute. + ObjectMetadata& set_custom_time(std::chrono::system_clock::time_point v) { + custom_time_ = v; + return *this; + } + + /// Reset (clears) the `customTime` attribute. `has_custom_time()` returns + /// `false` after calling this function. + ObjectMetadata& reset_custom_time() { + custom_time_.reset(); + return *this; + } + /// Returns `true` if the object uses CSEK (Customer-Supplied Encryption /// Keys). bool has_customer_encryption() const { @@ -185,8 +224,26 @@ class ObjectMetadata : private internal::CommonMetadata { return customer_encryption_.value(); } + /// @note This is only intended for mocking. + ObjectMetadata& set_customer_encryption(CustomerEncryption v) { + customer_encryption_ = std::move(v); + return *this; + } + + /// @note This is only intended for mocking. + ObjectMetadata& reset_customer_encryption() { + customer_encryption_.reset(); + return *this; + } + /// The `Etag` attribute. - using CommonMetadata::etag; + std::string const& etag() const { return etag_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_etag(std::string v) { + etag_ = std::move(v); + return *this; + } /// The `eventBasedHold` attribute. bool event_based_hold() const { return event_based_hold_; } @@ -206,11 +263,29 @@ class ObjectMetadata : private internal::CommonMetadata { */ std::int64_t generation() const { return generation_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_generation(std::int64_t v) { + generation_ = v; + return *this; + } + /// The `id` attribute (the object name) - using CommonMetadata::id; + std::string const& id() const { return id_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_id(std::string v) { + id_ = std::move(v); + return *this; + } /// The `kind` attribute, that is, `storage#object`. - using CommonMetadata::kind; + std::string const& kind() const { return kind_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_kind(std::string v) { + kind_ = std::move(v); + return *this; + } /** * The name of the KMS (Key Management Service) key used in this object. @@ -220,12 +295,30 @@ class ObjectMetadata : private internal::CommonMetadata { */ std::string const& kms_key_name() const { return kms_key_name_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_kms_key_name(std::string v) { + kms_key_name_ = std::move(v); + return *this; + } + /// The MD5 hash of the object contents. Can be empty. std::string const& md5_hash() const { return md5_hash_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_md5_hash(std::string v) { + md5_hash_ = std::move(v); + return *this; + } + /// The HTTPS link to access the object contents. std::string const& media_link() const { return media_link_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_media_link(std::string v) { + media_link_ = std::move(v); + return *this; + } + /** * @name Accessors and modifiers for metadata entries. * @@ -279,9 +372,6 @@ class ObjectMetadata : private internal::CommonMetadata { std::map& mutable_metadata() { return metadata_; } ///@} - /// Returns `true` if the object has an `owner` attribute. - using CommonMetadata::has_owner; - /** * The generation of the object metadata. * @@ -289,10 +379,25 @@ class ObjectMetadata : private internal::CommonMetadata { * attribute) increases the metageneration, but does not change the object * generation. */ - using CommonMetadata::metageneration; + std::int64_t metageneration() const { return metageneration_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_metageneration(std::int64_t v) { + metageneration_ = v; + return *this; + } /// The object name, including bucket and generation. - using CommonMetadata::name; + std::string const& name() const { return name_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_name(std::string v) { + name_ = std::move(v); + return *this; + } + + /// Returns `true` if the object has an `owner` attribute. + bool has_owner() const { return owner_.has_value(); } /** * The object's `owner` attribute. @@ -300,25 +405,56 @@ class ObjectMetadata : private internal::CommonMetadata { * It is undefined behavior to call this member function if * `has_owner() == false`. */ - using CommonMetadata::owner; + Owner const& owner() const { return *owner_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_owner(Owner v) { + owner_ = std::move(v); + return *this; + } + + /// @note This is only intended for mocking. + ObjectMetadata& reset_owner() { + owner_.reset(); + return *this; + } /// The retention expiration time, or the system clock's epoch, if not set. std::chrono::system_clock::time_point retention_expiration_time() const { return retention_expiration_time_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_retention_expiration_time( + std::chrono::system_clock::time_point v) { + retention_expiration_time_ = v; + return *this; + } + /// An HTTPS link to the object metadata. - using CommonMetadata::self_link; + std::string const& self_link() const { return self_link_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_self_link(std::string v) { + self_link_ = std::move(v); + return *this; + } /// The size of the object's data. std::uint64_t size() const { return size_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_size(std::uint64_t v) { + size_ = v; + return *this; + } + /// The `storageClass` attribute. - using CommonMetadata::storage_class; + std::string const& storage_class() const { return storage_class_; } /// Changes the `storageClass` attribute. ObjectMetadata& set_storage_class(std::string v) { - CommonMetadata::set_storage_class(std::move(v)); + storage_class_ = std::move(v); return *this; } @@ -332,39 +468,45 @@ class ObjectMetadata : private internal::CommonMetadata { } /// The object creation timestamp. - using CommonMetadata::time_created; + std::chrono::system_clock::time_point time_created() const { + return time_created_; + } + + /// @note This is only intended for mocking. + ObjectMetadata& set_time_created(std::chrono::system_clock::time_point v) { + time_created_ = v; + return *this; + } /// The object's deletion timestamp. std::chrono::system_clock::time_point time_deleted() const { return time_deleted_; } + /// @note This is only intended for mocking. + ObjectMetadata& set_time_deleted(std::chrono::system_clock::time_point v) { + time_deleted_ = v; + return *this; + } + /// The timestamp for the last storage class change. std::chrono::system_clock::time_point time_storage_class_updated() const { return time_storage_class_updated_; } - /// The timestamp for the last object *metadata* update. - using CommonMetadata::updated; - - /// Returns `true` if the object has a `customTime` attribute. - bool has_custom_time() const { return custom_time_.has_value(); } - - /// Returns the object's `customTime` or the system clock's epoch. - std::chrono::system_clock::time_point custom_time() const { - return custom_time_.value_or(std::chrono::system_clock::time_point{}); - } - - /// Changes the `customTime` attribute. - ObjectMetadata& set_custom_time(std::chrono::system_clock::time_point v) { - custom_time_ = v; + /// @note This is only intended for mocking. + ObjectMetadata& set_time_storage_class_updated( + std::chrono::system_clock::time_point v) { + time_storage_class_updated_ = v; return *this; } - /// Reset (clears) the `customTime` attribute. `has_custom_time()` returns - /// `false` after calling this function. - ObjectMetadata& reset_custom_time() { - custom_time_.reset(); + /// The timestamp for the last object *metadata* update. + std::chrono::system_clock::time_point updated() const { return updated_; } + + /// @note This is only intended for mocking. + ObjectMetadata& set_updated(std::chrono::system_clock::time_point v) { + updated_ = v; return *this; } @@ -374,9 +516,6 @@ class ObjectMetadata : private internal::CommonMetadata { } private: - friend struct internal::ObjectMetadataParser; - friend struct internal::GrpcObjectMetadataParser; - friend std::ostream& operator<<(std::ostream& os, ObjectMetadata const& rhs); // Keep the fields in alphabetical order. std::vector acl_; @@ -388,19 +527,29 @@ class ObjectMetadata : private internal::CommonMetadata { std::string content_language_; std::string content_type_; std::string crc32c_; + absl::optional custom_time_; absl::optional customer_encryption_; + std::string etag_; bool event_based_hold_{false}; std::int64_t generation_{0}; + std::string id_; + std::string kind_; std::string kms_key_name_; + std::int64_t metageneration_{0}; std::string md5_hash_; std::string media_link_; std::map metadata_; + std::string name_; + absl::optional owner_; std::chrono::system_clock::time_point retention_expiration_time_; + std::string self_link_; std::uint64_t size_{0}; + std::string storage_class_; bool temporary_hold_{false}; + std::chrono::system_clock::time_point time_created_; std::chrono::system_clock::time_point time_deleted_; std::chrono::system_clock::time_point time_storage_class_updated_; - absl::optional custom_time_; + std::chrono::system_clock::time_point updated_; }; std::ostream& operator<<(std::ostream& os, ObjectMetadata const& rhs); diff --git a/google/cloud/storage/object_metadata_test.cc b/google/cloud/storage/object_metadata_test.cc index e922d22fecc64..924041aea0ff6 100644 --- a/google/cloud/storage/object_metadata_test.cc +++ b/google/cloud/storage/object_metadata_test.cc @@ -188,14 +188,16 @@ TEST(ObjectMetadataTest, IOStream) { /// @test Verify that ObjectMetadataJsonForCompose works as expected. TEST(ObjectMetadataTest, JsonForComposeEmpty) { - nlohmann::json actual = ObjectMetadataJsonForCompose(ObjectMetadata()); + nlohmann::json actual = + internal::ObjectMetadataJsonForCompose(ObjectMetadata()); nlohmann::json expected({}); EXPECT_EQ(expected, actual); } /// @test Verify that ObjectMetadataJsonForCompose() works as expected. TEST(ObjectMetadataTest, JsonForCompose) { - auto actual = ObjectMetadataJsonForCompose(CreateObjectMetadataForTest()); + auto actual = + internal::ObjectMetadataJsonForCompose(CreateObjectMetadataForTest()); nlohmann::json expected = { {"acl", @@ -224,14 +226,15 @@ TEST(ObjectMetadataTest, JsonForCompose) { /// @test Verify that ObjectMetadataJsonForCopy works as expected. TEST(ObjectMetadataTest, JsonForCopyEmpty) { - nlohmann::json actual = ObjectMetadataJsonForCopy(ObjectMetadata()); + nlohmann::json actual = internal::ObjectMetadataJsonForCopy(ObjectMetadata()); nlohmann::json expected({}); EXPECT_EQ(expected, actual); } /// @test Verify that ObjectMetadataJsonForCopy() works as expected. TEST(ObjectMetadataTest, JsonForCopy) { - auto actual = ObjectMetadataJsonForCopy(CreateObjectMetadataForTest()); + auto actual = + internal::ObjectMetadataJsonForCopy(CreateObjectMetadataForTest()); nlohmann::json expected = { {"acl", @@ -260,14 +263,16 @@ TEST(ObjectMetadataTest, JsonForCopy) { /// @test Verify that ObjectMetadataJsonForInsert works as expected. TEST(ObjectMetadataTest, JsonForInsertEmpty) { - nlohmann::json actual = ObjectMetadataJsonForInsert(ObjectMetadata()); + nlohmann::json actual = + internal::ObjectMetadataJsonForInsert(ObjectMetadata()); nlohmann::json expected({}); EXPECT_EQ(expected, actual); } /// @test Verify that ObjectMetadataJsonForInsert() works as expected. TEST(ObjectMetadataTest, JsonForInsert) { - auto actual = ObjectMetadataJsonForInsert(CreateObjectMetadataForTest()); + auto actual = + internal::ObjectMetadataJsonForInsert(CreateObjectMetadataForTest()); nlohmann::json expected = { {"acl", @@ -298,14 +303,16 @@ TEST(ObjectMetadataTest, JsonForInsert) { /// @test Verify that `ObjectMetadataJsonForRewrite()` works as expected. TEST(ObjectMetadataTest, JsonForRewriteEmpty) { - nlohmann::json actual = ObjectMetadataJsonForRewrite(ObjectMetadata()); + nlohmann::json actual = + internal::ObjectMetadataJsonForRewrite(ObjectMetadata()); nlohmann::json expected({}); EXPECT_EQ(expected, actual); } /// @test Verify that `ObjectMetadataJsonForRewrite()` works as expected. TEST(ObjectMetadataTest, JsonForRewrite) { - auto actual = ObjectMetadataJsonForRewrite(CreateObjectMetadataForTest()); + auto actual = + internal::ObjectMetadataJsonForRewrite(CreateObjectMetadataForTest()); nlohmann::json expected = { {"acl", @@ -334,7 +341,8 @@ TEST(ObjectMetadataTest, JsonForRewrite) { /// @test Verify that ObjectMetadataJsonForUpdate works as expected. TEST(ObjectMetadataTest, JsonForUpdateEmpty) { - nlohmann::json actual = ObjectMetadataJsonForUpdate(ObjectMetadata()); + nlohmann::json actual = + internal::ObjectMetadataJsonForUpdate(ObjectMetadata()); nlohmann::json expected({{"eventBasedHold", false}}); EXPECT_EQ(expected, actual); } @@ -342,7 +350,7 @@ TEST(ObjectMetadataTest, JsonForUpdateEmpty) { /// @test Verify that ObjectMetadataJsonForUpdate works as expected. TEST(ObjectMetadataTest, JsonForUpdate) { auto tested = CreateObjectMetadataForTest(); - nlohmann::json actual = ObjectMetadataJsonForUpdate(tested); + nlohmann::json actual = internal::ObjectMetadataJsonForUpdate(tested); // Create a JSON object with only the writeable fields, because this is what // will be encoded in JsonPayloadForUpdate(). Before adding a new field, diff --git a/google/cloud/storage/owner.h b/google/cloud/storage/owner.h new file mode 100644 index 0000000000000..562724f8b174e --- /dev/null +++ b/google/cloud/storage/owner.h @@ -0,0 +1,65 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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 GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OWNER_H +#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OWNER_H + +#include "google/cloud/storage/version.h" +#include +#include +#include + +namespace google { +namespace cloud { +namespace storage { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN + +/// A simple wrapper for the `owner` field in object and bucket metadata. +struct Owner { + std::string entity; + std::string entity_id; +}; + +inline bool operator==(Owner const& lhs, Owner const& rhs) { + return std::tie(lhs.entity, lhs.entity_id) == + std::tie(rhs.entity, rhs.entity_id); +} + +inline bool operator<(Owner const& lhs, Owner const& rhs) { + return std::tie(lhs.entity, lhs.entity_id) < + std::tie(rhs.entity, rhs.entity_id); +} + +inline bool operator!=(Owner const& lhs, Owner const& rhs) { + return std::rel_ops::operator!=(lhs, rhs); +} + +inline bool operator>(Owner const& lhs, Owner const& rhs) { + return std::rel_ops::operator>(lhs, rhs); +} + +inline bool operator<=(Owner const& lhs, Owner const& rhs) { + return std::rel_ops::operator<=(lhs, rhs); +} + +inline bool operator>=(Owner const& lhs, Owner const& rhs) { + return std::rel_ops::operator>=(lhs, rhs); +} + +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +} // namespace storage +} // namespace cloud +} // namespace google + +#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OWNER_H