| 236 | } |
| 237 | |
| 238 | Result<std::shared_ptr<const KeyValueMetadata>> FromObjectMetadata( |
| 239 | const gcs::ObjectMetadata& m) { |
| 240 | auto format_time = [](std::chrono::system_clock::time_point tp) { |
| 241 | return absl::FormatTime(absl::RFC3339_full, absl::FromChrono(tp), |
| 242 | absl::UTCTimeZone()); |
| 243 | }; |
| 244 | auto result = std::make_shared<KeyValueMetadata>(); |
| 245 | // The fields are in the same order as defined in: |
| 246 | // https://cloud.google.com/storage/docs/json_api/v1/objects |
| 247 | // Where previous practice in Arrow suggested using a different field name (Content-Type |
| 248 | // vs. contentType) we prefer the existing practice in Arrow. |
| 249 | result->Append("id", m.id()); |
| 250 | result->Append("selfLink", m.self_link()); |
| 251 | result->Append("name", m.name()); |
| 252 | result->Append("bucket", m.bucket()); |
| 253 | result->Append("generation", ToChars(m.generation())); |
| 254 | result->Append("Content-Type", m.content_type()); |
| 255 | result->Append("timeCreated", format_time(m.time_created())); |
| 256 | result->Append("updated", format_time(m.updated())); |
| 257 | if (m.has_custom_time()) { |
| 258 | result->Append("customTime", format_time(m.custom_time())); |
| 259 | } |
| 260 | if (m.time_deleted() != std::chrono::system_clock::time_point()) { |
| 261 | result->Append("timeDeleted", format_time(m.time_deleted())); |
| 262 | } |
| 263 | result->Append("temporaryHold", m.temporary_hold() ? "true" : "false"); |
| 264 | result->Append("eventBasedHold", m.event_based_hold() ? "true" : "false"); |
| 265 | if (m.retention_expiration_time() != std::chrono::system_clock::time_point()) { |
| 266 | result->Append("retentionExpirationTime", format_time(m.retention_expiration_time())); |
| 267 | } |
| 268 | result->Append("storageClass", m.storage_class()); |
| 269 | if (m.time_storage_class_updated() != std::chrono::system_clock::time_point()) { |
| 270 | result->Append("timeStorageClassUpdated", |
| 271 | format_time(m.time_storage_class_updated())); |
| 272 | } |
| 273 | result->Append("size", ToChars(m.size())); |
| 274 | result->Append("md5Hash", m.md5_hash()); |
| 275 | result->Append("mediaLink", m.media_link()); |
| 276 | result->Append("Content-Encoding", m.content_encoding()); |
| 277 | result->Append("Content-Disposition", m.content_disposition()); |
| 278 | result->Append("Content-Language", m.content_language()); |
| 279 | result->Append("Cache-Control", m.cache_control()); |
| 280 | for (const auto& kv : m.metadata()) { |
| 281 | result->Append("metadata." + kv.first, kv.second); |
| 282 | } |
| 283 | // Skip "acl" because it is overly complex |
| 284 | if (m.has_owner()) { |
| 285 | result->Append("owner.entity", m.owner().entity); |
| 286 | result->Append("owner.entityId", m.owner().entity_id); |
| 287 | } |
| 288 | result->Append("crc32c", m.crc32c()); |
| 289 | result->Append("componentCount", ToChars(m.component_count())); |
| 290 | result->Append("etag", m.etag()); |
| 291 | if (m.has_customer_encryption()) { |
| 292 | result->Append("customerEncryption.encryptionAlgorithm", |
| 293 | m.customer_encryption().encryption_algorithm); |
| 294 | result->Append("customerEncryption.keySha256", m.customer_encryption().key_sha256); |
| 295 | } |