com.google.cloud.storage.UnifiedOpts Maven / Gradle / Ivy
Show all versions of google-cloud-storage Show documentation
/*
* 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
*
* http://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.
*/
package com.google.cloud.storage;
import static com.google.cloud.storage.Utils.projectNameCodec;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.not;
import static java.util.Objects.requireNonNull;
import com.google.api.gax.grpc.GrpcCallContext;
import com.google.cloud.storage.Conversions.Decoder;
import com.google.cloud.storage.Storage.BlobField;
import com.google.cloud.storage.Storage.BucketField;
import com.google.cloud.storage.spi.v1.StorageRpc;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import com.google.iam.v1.GetIamPolicyRequest;
import com.google.protobuf.ByteString;
import com.google.protobuf.FieldMask;
import com.google.storage.v2.CommonObjectRequestParams;
import com.google.storage.v2.ComposeObjectRequest;
import com.google.storage.v2.CreateBucketRequest;
import com.google.storage.v2.CreateHmacKeyRequest;
import com.google.storage.v2.DeleteBucketRequest;
import com.google.storage.v2.DeleteHmacKeyRequest;
import com.google.storage.v2.DeleteObjectRequest;
import com.google.storage.v2.GetBucketRequest;
import com.google.storage.v2.GetHmacKeyRequest;
import com.google.storage.v2.GetObjectRequest;
import com.google.storage.v2.ListBucketsRequest;
import com.google.storage.v2.ListHmacKeysRequest;
import com.google.storage.v2.ListObjectsRequest;
import com.google.storage.v2.LockBucketRetentionPolicyRequest;
import com.google.storage.v2.ReadObjectRequest;
import com.google.storage.v2.RewriteObjectRequest;
import com.google.storage.v2.UpdateBucketRequest;
import com.google.storage.v2.UpdateHmacKeyRequest;
import com.google.storage.v2.UpdateObjectRequest;
import com.google.storage.v2.WriteObjectRequest;
import java.io.Serializable;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.spec.SecretKeySpec;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* The set of all "Options" we currently support for per-call parameters.
*
* Most often, each of the respective types correspond to one of the parameters from HTTP headers
* and common query string parameters for JSON. In the case of gRPC, sometimes the parameters
* are in the specific request message or in grpc metadata.
*/
@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
final class UnifiedOpts {
/** Base interface type for each of the new options we're supporting. */
interface Opt extends Serializable {}
/**
* A specialization of {@link java.util.function.UnaryOperator} which maintains its lower type for
* {@link #identity()} and {@link #andThen(Mapper)}.
*/
@FunctionalInterface
interface Mapper {
T apply(T t);
default Mapper andThen(Mapper then) {
return t -> then.apply(apply(t));
}
static Mapper identity() {
return t -> t;
}
}
/** Base, marker interface of those Opts which represent a get/read/origin type relationship. */
private interface SourceOpt extends Opt {}
/**
* Base, marker interface of those Opts which represent a set/write/destination type relationship.
*/
private interface TargetOpt extends Opt {}
/** Base, marker interface of those Opts which apply to listing operations. */
private interface ListOpt extends Opt {}
/** Marker interface of those Opts which are applicable to Bucket operations. */
private interface ApplicableBucket {}
/** Marker interface of those Opts which are applicable to Object/Blob operations. */
private interface ApplicableObject {}
/** Marker interface of those Opts which are applicable to HmacKey operations. */
private interface ApplicableHmacKey {}
/** Base interface for those Opts which may expose their values via gRPC Metadata */
private interface GrpcMetadataMapper {
default Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
}
/** Base interface for those Opts which are applicable to Object List operations */
interface ObjectListOpt extends GrpcMetadataMapper, ListOpt, ApplicableObject {
default Mapper listObjects() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to Object Source (get/read/origin
* relationship) operations
*/
interface ObjectSourceOpt extends GrpcMetadataMapper, SourceOpt, ApplicableObject {
default Mapper readObject() {
return Mapper.identity();
}
default Mapper getObject() {
return Mapper.identity();
}
default Mapper rewriteObject() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to Object Target (set/write/destination
* relationship) operations
*/
interface ObjectTargetOpt extends GrpcMetadataMapper, TargetOpt, ApplicableObject {
default Mapper blobInfo() {
return Mapper.identity();
}
default Mapper writeObject() {
return Mapper.identity();
}
default Mapper updateObject() {
return Mapper.identity();
}
default Mapper deleteObject() {
return Mapper.identity();
}
default Mapper composeObject() {
return Mapper.identity();
}
default Mapper rewriteObject() {
return Mapper.identity();
}
}
/** Base interface for those Opts which are applicable to Bucket List operations */
interface BucketListOpt extends GrpcMetadataMapper, ListOpt, ApplicableBucket {
default Mapper listBuckets() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to Bucket Source (get/read/origin
* relationship) operations
*/
interface BucketSourceOpt extends GrpcMetadataMapper, SourceOpt, ApplicableBucket {
default Mapper getBucket() {
return Mapper.identity();
}
default Mapper getIamPolicy() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to Bucket Target (set/write/destination
* relationship) operations
*/
interface BucketTargetOpt extends GrpcMetadataMapper, TargetOpt, ApplicableBucket {
default Mapper createBucket() {
return Mapper.identity();
}
default Mapper updateBucket() {
return Mapper.identity();
}
default Mapper deleteBucket() {
return Mapper.identity();
}
default Mapper lockBucketRetentionPolicy() {
return Mapper.identity();
}
}
/** Base interface for those Opts which are applicable to HmacKey List operations */
interface HmacKeyListOpt extends GrpcMetadataMapper, ListOpt, ApplicableHmacKey {
default Mapper listHmacKeys() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to HmacKey Source (get/read/origin
* relationship) operations
*/
interface HmacKeySourceOpt extends GrpcMetadataMapper, SourceOpt, ApplicableHmacKey {
default Mapper getHmacKey() {
return Mapper.identity();
}
}
/**
* Base interface for those Opts which are applicable to HmacKey Target (set/write/destination
* relationship) operations
*/
interface HmacKeyTargetOpt extends GrpcMetadataMapper, TargetOpt, ApplicableHmacKey {
default Mapper createHmacKey() {
return Mapper.identity();
}
default Mapper updateHmacKey() {
return Mapper.identity();
}
default Mapper deleteHmacKey() {
return Mapper.identity();
}
}
/**
* Some Options have a corresponding "SOURCE" version, this interface provide a construct for
* accessing an projecting those Opts which can be turned into a "SOURCE" version.
*/
interface ProjectAsSource {
O asSource();
}
/**
* This class extends off {@link ObjectSourceOpt} and {@link ObjectTargetOpt} in order to satisfy
* some the shimming constraints of the subclasses of {@link OptionShim}.
*
* All the methods from these parent interfaces will NEVER be called, and are stubbed simply to
* satisfy the need for them to be declared. They are stubbed to use identity methods so that if
* they somehow do ever leak through and are called they won't cause issue for customers.
*
*
If/when we're able to remove all the {@link Option} classes, this interface should be
* refactored to remove the inheritance, instead providing an explicit pre-processing phase to opt
* resolution.
*/
interface ObjectOptExtractor extends Opt, ObjectSourceOpt, ObjectTargetOpt {
O extractFromBlobInfo(BlobInfo info);
O extractFromBlobId(BlobId id);
@Override
default Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
@Override
default Mapper getObject() {
return Mapper.identity();
}
@Override
default Mapper rewriteObject() {
return Mapper.identity();
}
}
/**
* This class extends off {@link ObjectSourceOpt} and {@link ObjectTargetOpt} in order to satisfy
* some the shimming constraints of the subclasses of {@link OptionShim}.
*
* All the methods from these parent interfaces will NEVER be called, and are stubbed simply to
* satisfy the need for them to be declared. They are stubbed to use identity methods so that if
* they somehow do ever leak through and are called they won't cause issue for customers.
*
*
If/when we're able to remove all the {@link Option} classes, this interface should be
* refactored to remove the inheritance, instead providing an explicit pre-processing phase to opt
* resolution.
*/
interface BucketOptExtractor extends Opt, BucketSourceOpt, BucketTargetOpt {
O extractFromBucketInfo(BucketInfo info);
@Override
default Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
}
/* --
Factory methods for each of the supported Opts, along with some of their requisite
compatibility overloads
-- */
static Crc32cMatch crc32cMatch(int i) {
return new Crc32cMatch(i);
}
static Crc32cMatch crc32cMatch(@NonNull String crc32c) {
requireNonNull(crc32c, "crc32c must be non null");
return new Crc32cMatch(Utils.crc32cCodec.decode(crc32c));
}
static Delimiter currentDirectory() {
return new Delimiter("/");
}
static DecryptionKey decryptionKey(@NonNull String decryptionKey) {
requireNonNull(decryptionKey, "decryptionKey must be non null");
return new DecryptionKey(
new SecretKeySpec(BaseEncoding.base64().decode(decryptionKey), "AES256"));
}
@RequiresNonNull({"decryptionKey", "#1.getEncoded()", "#1.getAlgorithm()"})
static DecryptionKey decryptionKey(@NonNull Key decryptionKey) {
requireNonNull(decryptionKey, "decryptionKey must be non null");
requireNonNull(decryptionKey.getEncoded(), "decryptionKey.getEncoded() must be non null");
requireNonNull(decryptionKey.getAlgorithm(), "decryptionKey.getAlgorithm() must be non null");
return new DecryptionKey(decryptionKey);
}
static Delimiter delimiter(@NonNull String delimiter) {
requireNonNull(delimiter, "delimiter must be non null");
return new Delimiter(delimiter);
}
@Deprecated
static DetectContentType detectContentType() {
return DetectContentType.INSTANCE;
}
static DisableGzipContent disableGzipContent() {
return new DisableGzipContent(true);
}
static GenerationMatch doesNotExist() {
return new GenerationMatch(0);
}
static EncryptionKey encryptionKey(@NonNull String encryptionKey) {
requireNonNull(encryptionKey, "encryptionKey must be non null");
return new EncryptionKey(
new SecretKeySpec(BaseEncoding.base64().decode(encryptionKey), "AES256"));
}
static EncryptionKey encryptionKey(@NonNull Key encryptionKey) {
requireNonNull(encryptionKey, "encryptionKey must be non null");
return new EncryptionKey(encryptionKey);
}
static EndOffset endOffset(@NonNull String endOffset) {
requireNonNull(endOffset, "endOffset must be non null");
return new EndOffset(endOffset);
}
static Fields fields(@NonNull ImmutableSet fields) {
requireNonNull(fields, "fields must be non null");
return new Fields(fields);
}
static GenerationMatch generationMatch(long l) {
return new GenerationMatch(l);
}
static GenerationNotMatch generationNotMatch(long l) {
return new GenerationNotMatch(l);
}
static KmsKeyName kmsKeyName(@NonNull String kmsKeyName) {
requireNonNull(kmsKeyName, "kmsKeyName must be non null");
return new KmsKeyName(kmsKeyName);
}
static MatchGlob matchGlob(@NonNull String glob) {
requireNonNull(glob, "glob must be non null");
return new MatchGlob(glob);
}
static Md5Match md5Match(@NonNull String md5) {
requireNonNull(md5, "md5 must be non null");
return new Md5Match(md5);
}
static MetagenerationMatch metagenerationMatch(long l) {
return new MetagenerationMatch(l);
}
static MetagenerationNotMatch metagenerationNotMatch(long l) {
return new MetagenerationNotMatch(l);
}
static PageSize pageSize(long l) {
return new PageSize(l);
}
static PageToken pageToken(@NonNull String pageToken) {
requireNonNull(pageToken, "pageToken must be non null");
return new PageToken(pageToken);
}
static PredefinedAcl predefinedAcl(Storage.@NonNull PredefinedAcl predefinedAcl) {
requireNonNull(predefinedAcl, "predefinedAcl must be non null");
return new PredefinedAcl(predefinedAcl.getEntry());
}
static PredefinedDefaultObjectAcl predefinedDefaultObjectAcl(
Storage.@NonNull PredefinedAcl predefinedAcl) {
requireNonNull(predefinedAcl, "predefinedAcl must be non null");
return new PredefinedDefaultObjectAcl(predefinedAcl.getEntry());
}
static Prefix prefix(@NonNull String prefix) {
requireNonNull(prefix, "prefix must be non null");
return new Prefix(prefix);
}
static ProjectId projectId(@NonNull String projectId) {
requireNonNull(projectId, "projectId must be non null");
return new ProjectId(projectId);
}
static Projection projection(@NonNull String projection) {
requireNonNull(projection, "projection must be non null");
return new Projection(projection);
}
static RequestedPolicyVersion requestedPolicyVersion(long l) {
return new RequestedPolicyVersion(l);
}
static ReturnRawInputStream returnRawInputStream(boolean b) {
return new ReturnRawInputStream(b);
}
@RequiresNonNull({"serviceAccount", "#1.getEmail()"})
static ServiceAccount serviceAccount(
com.google.cloud.storage.@NonNull ServiceAccount serviceAccount) {
requireNonNull(serviceAccount, "serviceAccount must be non null");
requireNonNull(serviceAccount.getEmail(), "serviceAccount.getEmail() must be non null");
return new ServiceAccount(serviceAccount.getEmail());
}
@VisibleForTesting
static SetContentType setContentType(@NonNull String s) {
requireNonNull(s, "s must be non null");
return new SetContentType(s);
}
static ShowDeletedKeys showDeletedKeys(boolean b) {
return new ShowDeletedKeys(b);
}
static StartOffset startOffset(@NonNull String startOffset) {
requireNonNull(startOffset, "startOffset must be non null");
return new StartOffset(startOffset);
}
static UserProject userProject(@NonNull String userProject) {
requireNonNull(userProject, "userProject must be non null");
return new UserProject(userProject);
}
static VersionsFilter versionsFilter(boolean b) {
return new VersionsFilter(b);
}
@Deprecated
static GenerationMatchExtractor generationMatchExtractor() {
return GenerationMatchExtractor.INSTANCE;
}
@Deprecated
static GenerationNotMatchExtractor generationNotMatchExtractor() {
return GenerationNotMatchExtractor.INSTANCE;
}
@Deprecated
static MetagenerationMatchExtractor metagenerationMatchExtractor() {
return MetagenerationMatchExtractor.INSTANCE;
}
@Deprecated
static MetagenerationNotMatchExtractor metagenerationNotMatchExtractor() {
return MetagenerationNotMatchExtractor.INSTANCE;
}
@Deprecated
static Crc32cMatchExtractor crc32cMatchExtractor() {
return Crc32cMatchExtractor.INSTANCE;
}
@Deprecated
static Md5MatchExtractor md5MatchExtractor() {
return Md5MatchExtractor.INSTANCE;
}
static final class Crc32cMatch implements ObjectTargetOpt {
private static final long serialVersionUID = 8172282701777561769L;
private final int val;
private Crc32cMatch(int val) {
this.val = val;
}
@Override
public Mapper blobInfo() {
return b -> b.setCrc32c(Utils.crc32cCodec.encode(val));
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Crc32cMatch)) {
return false;
}
Crc32cMatch that = (Crc32cMatch) o;
return Objects.equals(val, that.val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getObjectChecksumsBuilder().setCrc32C(val);
return b;
};
}
@Override
public int hashCode() {
return Objects.hash(val);
}
@Override
public String toString() {
return "Crc32cMatch{val='" + val + "'}";
}
}
/** @see EncryptionKey */
static final class DecryptionKey extends RpcOptVal implements ObjectSourceOpt {
private static final long serialVersionUID = -2198422155991275316L;
private DecryptionKey(Key val) {
super(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, val);
}
@Override
public Mapper> mapper() {
return b ->
b.put(
StorageRpc.Option.CUSTOMER_SUPPLIED_KEY,
BaseEncoding.base64().encode(val.getEncoded()));
}
@Override
public Mapper readObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper getObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper rewriteObject() {
return b -> {
CommonObjectRequestParams.Builder builder =
customerSuppliedKey(CommonObjectRequestParams.newBuilder(), val);
return b.setCopySourceEncryptionAlgorithm(builder.getEncryptionAlgorithm())
.setCopySourceEncryptionKeyBytes(builder.getEncryptionKeyBytes())
.setCopySourceEncryptionKeySha256Bytes(builder.getEncryptionKeySha256Bytes());
};
}
}
static final class Delimiter extends RpcOptVal implements ObjectListOpt {
private static final long serialVersionUID = -3789556789947615714L;
private Delimiter(String val) {
super(StorageRpc.Option.DELIMITER, val);
}
@Override
public Mapper listObjects() {
return b -> b.setDelimiter(val);
}
}
static final class DisableGzipContent extends RpcOptVal<@NonNull Boolean>
implements ObjectTargetOpt {
private static final long serialVersionUID = 7445066765944965549L;
private DisableGzipContent(boolean val) {
super(StorageRpc.Option.IF_DISABLE_GZIP_CONTENT, val);
}
}
/** @see DecryptionKey */
static final class EncryptionKey extends RpcOptVal implements ObjectTargetOpt {
private static final long serialVersionUID = -7335988656032764620L;
private EncryptionKey(Key val) {
super(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, val);
}
@Override
public Mapper> mapper() {
return b ->
b.put(
StorageRpc.Option.CUSTOMER_SUPPLIED_KEY,
BaseEncoding.base64().encode(val.getEncoded()));
}
@Override
public Mapper writeObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper updateObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper deleteObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper composeObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
@Override
public Mapper rewriteObject() {
return b -> {
customerSuppliedKey(b.getCommonObjectRequestParamsBuilder(), val);
return b;
};
}
}
/** @see StartOffset */
static final class EndOffset extends RpcOptVal implements ObjectListOpt {
private static final long serialVersionUID = 7446382028145458833L;
private EndOffset(String val) {
super(StorageRpc.Option.END_OFF_SET, val);
}
@Override
public Mapper listObjects() {
return b -> b.setLexicographicEnd(val);
}
}
static final class Fields extends RpcOptVal>
implements ObjectSourceOpt,
ObjectListOpt,
ObjectTargetOpt,
BucketSourceOpt,
BucketTargetOpt,
BucketListOpt {
/**
* Apiary and gRPC have differing handling of where the field selector is evaluated relative to
* the request. For Apiary, it's from the root response document; for gRPC it's from the
* collection of results.
*
* In our current case, this means we exclude some fields when we know it is being consumed
* for a gRPC message. Unfortunately, we don't know if the constructed Fields instance is for
* use with gRPC when it is instantiated so we must define here.
*/
private static final ImmutableSet grpcExcludedFields =
ImmutableSet.of("nextPageToken", "prefixes", "selfLink", "mediaLink", "kind", "id");
private static final long serialVersionUID = 3286889410148272195L;
private Fields(ImmutableSet val) {
super(StorageRpc.Option.FIELDS, val);
}
@Override
public Mapper> mapper() {
return b -> {
String collect =
val.stream().map(NamedField::getApiaryName).collect(Collectors.joining(","));
return b.put(StorageRpc.Option.FIELDS, collect);
};
}
@Override
public Mapper getBucket() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper listBuckets() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper updateBucket() {
return b -> b.setUpdateMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper getObject() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper listObjects() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper readObject() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper updateObject() {
return b -> b.setUpdateMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}
@Override
public Mapper rewriteObject() {
return Mapper.identity();
}
/**
* Define a decoder which can clear out any fields which may have not been selected.
*
* This approach, isn't ideal at the backside after decoding has already taken place.
* However, refactoring the whole model pipeline for both json and grpc is going to be a large
* change.
*/
Decoder clearUnselectedBlobFields() {
return b -> {
if (val.isEmpty()) {
return b;
} else {
Set names = getPaths();
Blob.Builder bldr = b.toBuilder();
blobInfoFieldClearers.entrySet().stream()
.filter(e -> !names.contains(e.getKey()))
.map(Entry::getValue)
.forEach(m -> m.apply(bldr));
return bldr.build();
}
};
}
/**
* Define a decoder which can clear out any fields which may have not been selected.
*
* This approach, isn't ideal at the backside after decoding has already taken place.
* However, refactoring the whole model pipeline for both json and grpc is going to be a large
* change.
*/
Decoder clearUnselectedBucketFields() {
return b -> {
if (val.isEmpty()) {
return b;
} else {
Set names = getPaths();
Bucket.Builder bldr = b.toBuilder();
bucketInfoFieldClearers.entrySet().stream()
.filter(e -> !names.contains(e.getKey()))
.map(Entry::getValue)
.forEach(m -> m.apply(bldr));
return bldr.build();
}
};
}
private Set getPaths() {
//noinspection Guava
return val.stream()
.map(NamedField::stripPrefix)
.map(NamedField::getGrpcName)
.filter(not(grpcExcludedFields::contains))
.collect(Collectors.toSet());
}
// It'd be preferable to define these clearing mappers in the fields themselves, however today
// the fields are enums and require interfaces in order to extend anything which in turn makes
// things public.
//
// To avoid putting more things on the public api that will hopefully take a different form
// in the medium term, we define them here.
private static final ImmutableMap> blobInfoFieldClearers =
ImmutableMap.>builder()
.put(BlobField.ACL.getGrpcName(), BlobInfo.Builder::clearAcl)
.put(BlobField.CACHE_CONTROL.getGrpcName(), BlobInfo.Builder::clearCacheControl)
.put(BlobField.COMPONENT_COUNT.getGrpcName(), BlobInfo.Builder::clearComponentCount)
.put(
BlobField.CONTENT_DISPOSITION.getGrpcName(),
BlobInfo.Builder::clearContentDisposition)
.put(BlobField.CONTENT_ENCODING.getGrpcName(), BlobInfo.Builder::clearContentEncoding)
.put(BlobField.CONTENT_LANGUAGE.getGrpcName(), BlobInfo.Builder::clearContentLanguage)
.put(BlobField.CONTENT_TYPE.getGrpcName(), BlobInfo.Builder::clearContentType)
.put(BlobField.CRC32C.getGrpcName(), BlobInfo.Builder::clearCrc32c)
.put(
BlobField.CUSTOMER_ENCRYPTION.getGrpcName(),
BlobInfo.Builder::clearCustomerEncryption)
.put(BlobField.CUSTOM_TIME.getGrpcName(), BlobInfo.Builder::clearCustomTime)
.put(BlobField.ETAG.getGrpcName(), BlobInfo.Builder::clearEtag)
.put(BlobField.EVENT_BASED_HOLD.getGrpcName(), BlobInfo.Builder::clearEventBasedHold)
.put(
BlobField.GENERATION.getGrpcName(),
b -> {
BlobId current = b.getBlobId();
return b.setBlobId(BlobId.of(current.getBucket(), current.getName()));
})
.put(BlobField.ID.getGrpcName(), BlobInfo.Builder::clearGeneratedId)
.put(BlobField.KMS_KEY_NAME.getGrpcName(), BlobInfo.Builder::clearKmsKeyName)
.put(BlobField.MD5HASH.getGrpcName(), BlobInfo.Builder::clearMd5)
.put(BlobField.MEDIA_LINK.getGrpcName(), BlobInfo.Builder::clearMediaLink)
.put(BlobField.METADATA.getGrpcName(), BlobInfo.Builder::clearMetadata)
.put(BlobField.METAGENERATION.getGrpcName(), BlobInfo.Builder::clearMetageneration)
.put(BlobField.OWNER.getGrpcName(), BlobInfo.Builder::clearOwner)
.put(
BlobField.RETENTION_EXPIRATION_TIME.getGrpcName(),
BlobInfo.Builder::clearRetentionExpirationTime)
.put(BlobField.SELF_LINK.getGrpcName(), BlobInfo.Builder::clearSelfLink)
.put(BlobField.SIZE.getGrpcName(), BlobInfo.Builder::clearSize)
.put(BlobField.STORAGE_CLASS.getGrpcName(), BlobInfo.Builder::clearStorageClass)
.put(BlobField.TEMPORARY_HOLD.getGrpcName(), BlobInfo.Builder::clearTemporaryHold)
.put(BlobField.TIME_CREATED.getGrpcName(), BlobInfo.Builder::clearCreateTime)
.put(BlobField.TIME_DELETED.getGrpcName(), BlobInfo.Builder::clearDeleteTime)
.put(
BlobField.TIME_STORAGE_CLASS_UPDATED.getGrpcName(),
BlobInfo.Builder::clearTimeStorageClassUpdated)
.put(BlobField.UPDATED.getGrpcName(), BlobInfo.Builder::clearUpdateTime)
.build();
private static final ImmutableMap> bucketInfoFieldClearers =
ImmutableMap.>builder()
.put(BucketField.ACL.getGrpcName(), BucketInfo.Builder::clearAcl)
// .put(BucketField.AUTOCLASS.getGrpcName(), b -> b.clearAutoclass())
.put(BucketField.BILLING.getGrpcName(), BucketInfo.Builder::clearRequesterPays)
.put(BucketField.CORS.getGrpcName(), BucketInfo.Builder::clearCors)
.put(
BucketField.CUSTOM_PLACEMENT_CONFIG.getGrpcName(),
BucketInfo.Builder::clearCustomPlacementConfig)
.put(
BucketField.DEFAULT_EVENT_BASED_HOLD.getGrpcName(),
BucketInfo.Builder::clearDefaultEventBasedHold)
.put(BucketField.DEFAULT_OBJECT_ACL.getGrpcName(), BucketInfo.Builder::clearDefaultAcl)
.put(BucketField.ENCRYPTION.getGrpcName(), BucketInfo.Builder::clearDefaultKmsKeyName)
.put(BucketField.ETAG.getGrpcName(), BucketInfo.Builder::clearEtag)
.put(
BucketField.IAMCONFIGURATION.getGrpcName(),
BucketInfo.Builder::clearIamConfiguration)
.put(BucketField.ID.getGrpcName(), BucketInfo.Builder::clearGeneratedId)
.put(BucketField.LABELS.getGrpcName(), BucketInfo.Builder::clearLabels)
.put(BucketField.LIFECYCLE.getGrpcName(), BucketInfo.Builder::clearLifecycleRules)
.put(BucketField.LOCATION.getGrpcName(), BucketInfo.Builder::clearLocation)
.put(BucketField.LOCATION_TYPE.getGrpcName(), BucketInfo.Builder::clearLocationType)
.put(BucketField.LOGGING.getGrpcName(), BucketInfo.Builder::clearLogging)
.put(BucketField.METAGENERATION.getGrpcName(), BucketInfo.Builder::clearMetageneration)
.put(BucketField.NAME.getGrpcName(), BucketInfo.Builder::clearName)
.put(BucketField.OWNER.getGrpcName(), BucketInfo.Builder::clearOwner)
.put(
BucketField.RETENTION_POLICY.getGrpcName(),
b ->
b.clearRetentionEffectiveTime()
.clearRetentionPolicyIsLocked()
.clearRetentionPeriod())
.put(BucketField.RPO.getGrpcName(), BucketInfo.Builder::clearRpo)
.put(BucketField.STORAGE_CLASS.getGrpcName(), BucketInfo.Builder::clearStorageClass)
.put(BucketField.TIME_CREATED.getGrpcName(), BucketInfo.Builder::clearCreateTime)
.put(BucketField.UPDATED.getGrpcName(), BucketInfo.Builder::clearUpdateTime)
.put(BucketField.VERSIONING.getGrpcName(), BucketInfo.Builder::clearVersioningEnabled)
.put(BucketField.WEBSITE.getGrpcName(), b -> b.clearIndexPage().clearNotFoundPage())
.put("project", BucketInfo.Builder::clearProject)
.build();
}
/**
* @see GenerationNotMatch
* @see SourceGenerationMatch
*/
static final class GenerationMatch extends RpcOptVal<@NonNull Long>
implements ObjectSourceOpt, ObjectTargetOpt, ProjectAsSource {
private static final long serialVersionUID = 2645517179434741007L;
private GenerationMatch(long val) {
super(StorageRpc.Option.IF_GENERATION_MATCH, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().setIfGenerationMatch(val);
return b;
};
}
@Override
public Mapper readObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public Mapper getObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public Mapper updateObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public Mapper deleteObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public Mapper composeObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfGenerationMatch(val);
}
@Override
public SourceGenerationMatch asSource() {
return new SourceGenerationMatch(val);
}
}
/**
* @see GenerationMatch
* @see SourceGenerationNotMatch
*/
static final class GenerationNotMatch extends RpcOptVal<@NonNull Long>
implements ObjectSourceOpt, ObjectTargetOpt, ProjectAsSource {
private static final long serialVersionUID = 156505623580743531L;
private GenerationNotMatch(long val) {
super(StorageRpc.Option.IF_GENERATION_NOT_MATCH, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().setIfGenerationNotMatch(val);
return b;
};
}
@Override
public Mapper readObject() {
return b -> b.setIfGenerationNotMatch(val);
}
@Override
public Mapper getObject() {
return b -> b.setIfGenerationNotMatch(val);
}
@Override
public Mapper updateObject() {
return b -> b.setIfGenerationNotMatch(val);
}
@Override
public Mapper deleteObject() {
return b -> b.setIfGenerationNotMatch(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfGenerationNotMatch(val);
}
@Override
public SourceGenerationNotMatch asSource() {
return new SourceGenerationNotMatch(val);
}
}
static final class KmsKeyName extends RpcOptVal implements ObjectTargetOpt {
private static final long serialVersionUID = -3053839109272566113L;
private KmsKeyName(String val) {
super(StorageRpc.Option.KMS_KEY_NAME, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().getResourceBuilder().setKmsKey(val);
return b;
};
}
@Override
public Mapper composeObject() {
return b -> b.setKmsKey(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setDestinationKmsKey(val);
}
}
static final class MatchGlob extends RpcOptVal implements ObjectListOpt {
private static final long serialVersionUID = 8819855597395473178L;
private MatchGlob(String val) {
super(StorageRpc.Option.MATCH_GLOB, val);
}
@Override
public Mapper listObjects() {
return CrossTransportUtils.throwHttpJsonOnly(
com.google.cloud.storage.Storage.BlobListOption.class, "matchGlob(String)");
}
}
@Deprecated
static final class Md5Match implements ObjectTargetOpt {
private static final long serialVersionUID = 5237207911268363887L;
private final String val;
private Md5Match(String val) {
this.val = val;
}
@Override
public Mapper blobInfo() {
return b -> b.setMd5(val);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Md5Match)) {
return false;
}
Md5Match md5Match = (Md5Match) o;
return Objects.equals(val, md5Match.val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getObjectChecksumsBuilder()
.setMd5Hash(ByteString.copyFrom(BaseEncoding.base64().decode(val)));
return b;
};
}
@Override
public int hashCode() {
return Objects.hash(val);
}
@Override
public String toString() {
return "Md5Match{val='" + val + "'}";
}
}
/**
* @see MetagenerationNotMatch
* @see SourceMetagenerationMatch
*/
static final class MetagenerationMatch extends RpcOptVal<@NonNull Long>
implements BucketSourceOpt,
BucketTargetOpt,
ObjectSourceOpt,
ObjectTargetOpt,
ProjectAsSource {
private static final long serialVersionUID = 49086960234390739L;
private MetagenerationMatch(long val) {
super(StorageRpc.Option.IF_METAGENERATION_MATCH, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().setIfMetagenerationMatch(val);
return b;
};
}
@Override
public Mapper readObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper getObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper updateObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper deleteObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper composeObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper updateBucket() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper deleteBucket() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper getBucket() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public Mapper lockBucketRetentionPolicy() {
return b -> b.setIfMetagenerationMatch(val);
}
@Override
public SourceMetagenerationMatch asSource() {
return new SourceMetagenerationMatch(val);
}
}
/**
* @see MetagenerationMatch
* @see SourceMetagenerationNotMatch
*/
static final class MetagenerationNotMatch extends RpcOptVal<@NonNull Long>
implements BucketSourceOpt,
BucketTargetOpt,
ObjectSourceOpt,
ObjectTargetOpt,
ProjectAsSource {
private static final long serialVersionUID = -1795350187419586248L;
private MetagenerationNotMatch(long val) {
super(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().setIfMetagenerationNotMatch(val);
return b;
};
}
@Override
public Mapper readObject() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper getObject() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper updateObject() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper deleteObject() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper updateBucket() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper deleteBucket() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public Mapper getBucket() {
return b -> b.setIfMetagenerationNotMatch(val);
}
@Override
public SourceMetagenerationNotMatch asSource() {
return new SourceMetagenerationNotMatch(val);
}
}
static final class PageSize extends RpcOptVal<@NonNull Long>
implements BucketListOpt, ObjectListOpt, HmacKeyListOpt {
private static final long serialVersionUID = -8184518840397826601L;
private PageSize(long val) {
super(StorageRpc.Option.MAX_RESULTS, val);
}
@Override
public Mapper listHmacKeys() {
return b -> b.setPageSize(Math.toIntExact(val));
}
@Override
public Mapper listBuckets() {
return b -> b.setPageSize(Math.toIntExact(val));
}
@Override
public Mapper listObjects() {
return b -> b.setPageSize(Math.toIntExact(val));
}
}
static final class PageToken extends RpcOptVal
implements BucketListOpt, ObjectListOpt, HmacKeyListOpt {
private static final long serialVersionUID = -1370658416509499177L;
private PageToken(String val) {
super(StorageRpc.Option.PAGE_TOKEN, val);
}
@Override
public Mapper listHmacKeys() {
return b -> b.setPageToken(val);
}
@Override
public Mapper listBuckets() {
return b -> b.setPageToken(val);
}
@Override
public Mapper listObjects() {
return b -> b.setPageToken(val);
}
}
static final class PredefinedAcl extends RpcOptVal
implements BucketTargetOpt, ObjectTargetOpt {
private static final long serialVersionUID = -1743736785228368741L;
private PredefinedAcl(String val) {
super(StorageRpc.Option.PREDEFINED_ACL, val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().setPredefinedAcl(val);
return b;
};
}
@Override
public Mapper updateObject() {
return b -> b.setPredefinedAcl(val);
}
@Override
public Mapper composeObject() {
return b -> b.setDestinationPredefinedAcl(val);
}
@Override
public Mapper updateBucket() {
return b -> b.setPredefinedAcl(val);
}
@Override
public Mapper createBucket() {
return b -> b.setPredefinedAcl(val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setDestinationPredefinedAcl(val);
}
}
static final class PredefinedDefaultObjectAcl extends RpcOptVal
implements BucketTargetOpt {
private static final long serialVersionUID = -1771832790114963130L;
private PredefinedDefaultObjectAcl(String val) {
super(StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL, val);
}
@Override
public Mapper createBucket() {
return b -> b.setPredefinedDefaultObjectAcl(val);
}
@Override
public Mapper updateBucket() {
return b -> b.setPredefinedDefaultObjectAcl(val);
}
}
static final class Prefix extends RpcOptVal implements BucketListOpt, ObjectListOpt {
private static final long serialVersionUID = -3973478772547687371L;
private Prefix(String val) {
super(StorageRpc.Option.PREFIX, val);
}
@Override
public Mapper listBuckets() {
return b -> b.setPrefix(val);
}
@Override
public Mapper listObjects() {
return b -> b.setPrefix(val);
}
}
/**
* This is a required property of hmac related operations. Preferably, we'd be able to push the
* defaulting to the creation of a new instance of one of the model objects
*/
@Deprecated
static final class ProjectId extends RpcOptVal
implements HmacKeySourceOpt, HmacKeyTargetOpt, HmacKeyListOpt, BucketListOpt {
private static final long serialVersionUID = 6273807286378420321L;
private ProjectId(String val) {
super(StorageRpc.Option.PROJECT_ID, val);
}
@Override
public Mapper listHmacKeys() {
return b -> b.setProject(projectNameCodec.encode(val));
}
@Override
public Mapper getHmacKey() {
return b -> b.setProject(projectNameCodec.encode(val));
}
@Override
public Mapper createHmacKey() {
return b -> b.setProject(projectNameCodec.encode(val));
}
@Override
public Mapper listBuckets() {
return b -> b.setParent(projectNameCodec.encode(val));
}
}
static final class Projection extends RpcOptVal implements BucketTargetOpt {
private static final long serialVersionUID = -7394684784418942133L;
private Projection(String val) {
super(StorageRpc.Option.PROJECTION, val);
}
}
/**
* @see GenerationMatch
* @see SourceGenerationNotMatch
*/
static final class SourceGenerationMatch extends RpcOptVal<@NonNull Long>
implements ObjectSourceOpt, ObjectTargetOpt {
private static final long serialVersionUID = -4074703368515265616L;
private SourceGenerationMatch(@NonNull Long val) {
super(StorageRpc.Option.IF_SOURCE_GENERATION_MATCH, val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfSourceGenerationMatch(val);
}
}
/**
* @see GenerationNotMatch
* @see SourceGenerationMatch
*/
static final class SourceGenerationNotMatch extends RpcOptVal<@NonNull Long>
implements ObjectSourceOpt, ObjectTargetOpt {
private static final long serialVersionUID = -5232032184462880657L;
private SourceGenerationNotMatch(@NonNull Long val) {
super(StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH, val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfSourceGenerationNotMatch(val);
}
}
/**
* @see MetagenerationMatch
* @see SourceMetagenerationNotMatch
*/
static final class SourceMetagenerationMatch extends RpcOptVal<@NonNull Long>
implements BucketSourceOpt, BucketTargetOpt, ObjectSourceOpt, ObjectTargetOpt {
private static final long serialVersionUID = 5223360761780436495L;
private SourceMetagenerationMatch(@NonNull Long val) {
super(StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH, val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfSourceMetagenerationMatch(val);
}
}
/**
* @see MetagenerationNotMatch
* @see SourceMetagenerationMatch
*/
static final class SourceMetagenerationNotMatch extends RpcOptVal<@NonNull Long>
implements BucketSourceOpt, BucketTargetOpt, ObjectSourceOpt, ObjectTargetOpt {
private static final long serialVersionUID = 2679308305890468285L;
private SourceMetagenerationNotMatch(@NonNull Long val) {
super(StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH, val);
}
@Override
public Mapper rewriteObject() {
return b -> b.setIfSourceMetagenerationNotMatch(val);
}
}
static final class RequestedPolicyVersion extends RpcOptVal<@NonNull Long>
implements BucketSourceOpt {
private static final long serialVersionUID = -3606062322328656218L;
private RequestedPolicyVersion(Long val) {
super(StorageRpc.Option.REQUESTED_POLICY_VERSION, val);
}
@Override
public Mapper getIamPolicy() {
return b -> {
b.getOptionsBuilder().setRequestedPolicyVersion(Math.toIntExact(val));
return b;
};
}
}
@Deprecated
static final class ReturnRawInputStream extends RpcOptVal<@NonNull Boolean>
implements ObjectSourceOpt {
private static final long serialVersionUID = -5741791424843430584L;
private ReturnRawInputStream(boolean val) {
super(StorageRpc.Option.RETURN_RAW_INPUT_STREAM, val);
}
}
static final class ServiceAccount extends RpcOptVal implements HmacKeyListOpt {
private static final long serialVersionUID = 5617709092359745482L;
private ServiceAccount(String val) {
super(StorageRpc.Option.SERVICE_ACCOUNT_EMAIL, val);
}
@Override
public Mapper listHmacKeys() {
return b -> b.setServiceAccountEmail(val);
}
}
static final class SetContentType implements ObjectTargetOpt {
private static final long serialVersionUID = -5715260463246857009L;
private final String val;
private SetContentType(String val) {
this.val = val;
}
@Override
public Mapper blobInfo() {
return b -> b.setContentType(val);
}
@Override
public Mapper writeObject() {
return b -> {
b.getWriteObjectSpecBuilder().getResourceBuilder().setContentType(val);
return b;
};
}
@Override
public Mapper updateObject() {
return b -> {
b.getObjectBuilder().setContentType(val);
return b;
};
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SetContentType)) {
return false;
}
SetContentType that = (SetContentType) o;
return Objects.equals(val, that.val);
}
@Override
public int hashCode() {
return Objects.hash(val);
}
@Override
public String toString() {
return "SetContentType{val='" + val + "'}";
}
}
static final class ShowDeletedKeys extends RpcOptVal<@NonNull Boolean> implements HmacKeyListOpt {
private static final long serialVersionUID = -6604176744362903487L;
private ShowDeletedKeys(boolean val) {
super(StorageRpc.Option.SHOW_DELETED_KEYS, val);
}
@Override
public Mapper listHmacKeys() {
return b -> b.setShowDeletedKeys(val);
}
}
/** @see EndOffset */
static final class StartOffset extends RpcOptVal implements ObjectListOpt {
private static final long serialVersionUID = -1459727336598737833L;
private StartOffset(String val) {
super(StorageRpc.Option.START_OFF_SET, val);
}
@Override
public Mapper listObjects() {
return b -> b.setLexicographicStart(val);
}
}
static final class UserProject extends RpcOptVal
implements BucketSourceOpt,
BucketTargetOpt,
BucketListOpt,
ObjectSourceOpt,
ObjectTargetOpt,
ObjectListOpt,
HmacKeySourceOpt,
HmacKeyTargetOpt,
HmacKeyListOpt {
private static final long serialVersionUID = 3962499996741180460L;
private UserProject(String val) {
super(StorageRpc.Option.USER_PROJECT, val);
}
@Override
public Mapper getGrpcMetadataMapper() {
return ctx ->
ctx.withExtraHeaders(ImmutableMap.of("X-Goog-User-Project", ImmutableList.of(val)));
}
@Override
public Mapper rewriteObject() {
return Mapper.identity();
}
}
static final class VersionsFilter extends RpcOptVal<@NonNull Boolean> implements ObjectListOpt {
private VersionsFilter(boolean val) {
super(StorageRpc.Option.VERSIONS, val);
}
@Override
public Mapper listObjects() {
return b -> b.setVersions(val);
}
}
/**
* Attempt to extract a crc32c value from an Object. If no crc32c value is extracted the produced
* Opt will be an effective no-op.
*
* @see Crc32cMatch
* @deprecated Use {@link BlobInfo.Builder#setCrc32c(String)}
*/
@Deprecated
static final class Crc32cMatchExtractor implements ObjectOptExtractor {
private static final Crc32cMatchExtractor INSTANCE = new Crc32cMatchExtractor();
private static final long serialVersionUID = 7045998436157555676L;
@Deprecated
private Crc32cMatchExtractor() {}
@Override
public ObjectTargetOpt extractFromBlobInfo(BlobInfo info) {
String crc32c = info.getCrc32c();
if (crc32c != null) {
return crc32cMatch(crc32c);
} else {
return NoOpObjectTargetOpt.INSTANCE;
}
}
@Override
public ObjectTargetOpt extractFromBlobId(BlobId id) {
return NoOpObjectTargetOpt.INSTANCE;
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to determine the content type of an Object based on it's {@link BlobInfo#getName()}. If
* no name value is extracted, or the value is not a known extension the content type will be
* {@code application/octet-stream}
*
* @see SetContentType
* @see URLConnection#getFileNameMap()
* @see FileNameMap
* @deprecated Use {@link BlobInfo.Builder#setContentType(String)}
*/
@Deprecated
static final class DetectContentType implements ObjectOptExtractor {
@Deprecated private static final DetectContentType INSTANCE = new DetectContentType();
private static final FileNameMap FILE_NAME_MAP = URLConnection.getFileNameMap();
private static final long serialVersionUID = -1089120180148634090L;
@Deprecated
private DetectContentType() {}
@Override
public ObjectTargetOpt extractFromBlobInfo(BlobInfo info) {
String contentType = info.getContentType();
if (contentType != null && !contentType.isEmpty()) {
return NoOpObjectTargetOpt.INSTANCE;
}
return detectForName(info.getName());
}
@Override
public ObjectTargetOpt extractFromBlobId(BlobId id) {
return detectForName(id.getName());
}
private ObjectTargetOpt detectForName(String name) {
if (name != null) {
String nameLower = name.toLowerCase(Locale.ENGLISH);
String contentTypeFor = FILE_NAME_MAP.getContentTypeFor(nameLower);
if (contentTypeFor != null) {
return new SetContentType(contentTypeFor);
}
}
return new SetContentType("application/octet-stream");
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to extract a generation value from an Object. If no generation value is extracted an
* {@link IllegalArgumentException} will be thrown.
*
* @see GenerationMatch
* @deprecated Use {@link #generationMatch(long)}
*/
@Deprecated
static final class GenerationMatchExtractor implements ObjectOptExtractor {
private static final GenerationMatchExtractor INSTANCE = new GenerationMatchExtractor();
private static final long serialVersionUID = -7211192249703566097L;
@Deprecated
private GenerationMatchExtractor() {}
@Override
public GenerationMatch extractFromBlobInfo(BlobInfo info) {
Long generation = info.getGeneration();
checkArgument(generation != null, "Option ifGenerationMatch is missing a value");
return generationMatch(generation);
}
@Override
public GenerationMatch extractFromBlobId(BlobId id) {
Long generation = id.getGeneration();
checkArgument(generation != null, "Option ifGenerationMatch is missing a value");
return generationMatch(generation);
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to extract a generation value from an Object. If no generation value is extracted an
* {@link IllegalArgumentException} will be thrown.
*
* @see GenerationNotMatch
* @deprecated Use {@link #generationNotMatch(long)}
*/
@Deprecated
static final class GenerationNotMatchExtractor implements ObjectOptExtractor {
private static final GenerationNotMatchExtractor INSTANCE = new GenerationNotMatchExtractor();
private static final long serialVersionUID = -107520114846569713L;
@Deprecated
private GenerationNotMatchExtractor() {}
@Override
public GenerationNotMatch extractFromBlobInfo(BlobInfo info) {
Long generation = info.getGeneration();
checkArgument(generation != null, "Option ifGenerationNotMatch is missing a value");
return generationNotMatch(generation);
}
@Override
public GenerationNotMatch extractFromBlobId(BlobId id) {
Long generation = id.getGeneration();
checkArgument(generation != null, "Option ifGenerationNotMatch is missing a value");
return generationNotMatch(generation);
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to extract an md5 value from an Object. If no md5 value is extracted the produced Opt
* will be an effective no-op.
*
* @see Md5Match
* @deprecated Use {@link BlobInfo.Builder#setMd5(String)}
*/
@Deprecated
static final class Md5MatchExtractor implements ObjectOptExtractor {
private static final Md5MatchExtractor INSTANCE = new Md5MatchExtractor();
private static final long serialVersionUID = 8375506989224962531L;
@Deprecated
private Md5MatchExtractor() {}
@Override
public ObjectTargetOpt extractFromBlobInfo(BlobInfo info) {
String md5 = info.getMd5();
if (md5 != null) {
return md5Match(md5);
} else {
return NoOpObjectTargetOpt.INSTANCE;
}
}
@Override
public ObjectTargetOpt extractFromBlobId(BlobId id) {
return NoOpObjectTargetOpt.INSTANCE;
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to extract a metageneration value from a Bucket or Object. If no metageneration value
* is extracted an {@link IllegalArgumentException} will be thrown.
*
* @see MetagenerationMatch
* @deprecated Use {@link #metagenerationMatch(long)}
*/
@Deprecated
static final class MetagenerationMatchExtractor
implements ObjectOptExtractor, BucketOptExtractor {
private static final MetagenerationMatchExtractor INSTANCE = new MetagenerationMatchExtractor();
private static final long serialVersionUID = -4165372534008844973L;
@Deprecated
private MetagenerationMatchExtractor() {}
@Override
public MetagenerationMatch extractFromBlobInfo(BlobInfo info) {
Long metageneration = info.getMetageneration();
checkArgument(metageneration != null, "Option ifMetagenerationMatch is missing a value");
return metagenerationMatch(metageneration);
}
@Override
public ObjectTargetOpt extractFromBlobId(BlobId id) {
return NoOpObjectTargetOpt.INSTANCE;
}
@Override
public MetagenerationMatch extractFromBucketInfo(BucketInfo info) {
Long metageneration = info.getMetageneration();
checkArgument(metageneration != null, "Option ifMetagenerationMatch is missing a value");
return metagenerationMatch(metageneration);
}
// Both parent interfaces define this method, we need to declare a dis-ambiguous one
@Override
public Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Attempt to extract a metageneration value from a Bucket or Object. If no metageneration value
* is extracted an {@link IllegalArgumentException} will be thrown.
*
* @see MetagenerationNotMatch
* @deprecated Use {@link #metagenerationNotMatch(long)}
*/
@Deprecated
static final class MetagenerationNotMatchExtractor
implements ObjectOptExtractor, BucketOptExtractor {
private static final MetagenerationNotMatchExtractor INSTANCE =
new MetagenerationNotMatchExtractor();
private static final long serialVersionUID = 6544628474151482319L;
@Deprecated
private MetagenerationNotMatchExtractor() {}
@Override
public MetagenerationNotMatch extractFromBlobInfo(BlobInfo info) {
Long metageneration = info.getMetageneration();
checkArgument(metageneration != null, "Option ifMetagenerationNotMatch is missing a value");
return metagenerationNotMatch(metageneration);
}
@Override
public ObjectTargetOpt extractFromBlobId(BlobId id) {
return NoOpObjectTargetOpt.INSTANCE;
}
@Override
public MetagenerationNotMatch extractFromBucketInfo(BucketInfo info) {
Long metageneration = info.getMetageneration();
checkArgument(metageneration != null, "Option ifMetagenerationNotMatch is missing a value");
return metagenerationNotMatch(metageneration);
}
// Both parent interfaces define this method, we need to declare a dis-ambiguous one
@Override
public Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* Internal only implementation of {@link ObjectTargetOpt} which is a No-op.
*
* The instance of this class can be returned when a no-op is necessary.
*/
@VisibleForTesting
static final class NoOpObjectTargetOpt implements ObjectTargetOpt {
@VisibleForTesting static final NoOpObjectTargetOpt INSTANCE = new NoOpObjectTargetOpt();
private static final long serialVersionUID = -5356245440686012545L;
private NoOpObjectTargetOpt() {}
@Override
public Mapper getGrpcMetadataMapper() {
return Mapper.identity();
}
@Override
public Mapper blobInfo() {
return Mapper.identity();
}
/** prevent java serialization from using a new instance */
private Object readResolve() {
return INSTANCE;
}
}
/**
* A shim class used by {@link Option} to allow a common parent which isn't part of the public
* api.
*
* {@link Option} itself and all it's subclasses are now obsolete, and should be removed when
* we're able to remove them from the public api.
*/
@Deprecated
abstract static class OptionShim implements Serializable {
private static final long serialVersionUID = 3410752214075057852L;
private final O opt;
OptionShim(O opt) {
this.opt = opt;
}
O getOpt() {
return opt;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof OptionShim)) {
return false;
}
OptionShim> that = (OptionShim>) o;
return Objects.equals(opt, that.opt);
}
@Override
public int hashCode() {
return Objects.hash(opt);
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{opt=" + opt + '}';
}
}
/**
* Base class for those {@link Opt}s which correspond to one or more {@link StorageRpc.Option}
* keys.
*
* @param
*/
private abstract static class RpcOptVal implements Opt {
private static final long serialVersionUID = 9170283346051824148L;
protected final StorageRpc.Option key;
protected final T val;
private RpcOptVal(StorageRpc.Option key, T val) {
this.key = requireNonNull(key, "key must be non null");
this.val = requireNonNull(val, "val must be non null");
}
public Mapper> mapper() {
return b -> b.put(key, val);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof RpcOptVal)) {
return false;
}
RpcOptVal> rpcOptVal = (RpcOptVal>) o;
return Objects.equals(key, rpcOptVal.key) && Objects.equals(val, rpcOptVal.val);
}
@Override
public int hashCode() {
return Objects.hash(key, val);
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{key=" + key + ", val=" + val + '}';
}
}
/**
* Internal "collection" class to represent a set of {@link Opt}s, and to provide useful
* transformations to individual mappers or to resolve any extractors providing a new instance
* without extractors.
*/
@SuppressWarnings("unchecked")
static final class Opts {
private static final Function, ImmutableMap, ?>> mapBuild;
static {
Function, ImmutableMap, ?>> tmp;
// buildOrThrow was added in guava 31.0
// if it fails, fallback to the older build() method instead.
// The behavior was the same, but the new name makes the behavior clear
try {
ImmutableMap.builder().buildOrThrow();
tmp = ImmutableMap.Builder::buildOrThrow;
} catch (NoSuchMethodError e) {
tmp = ImmutableMap.Builder::build;
}
mapBuild = tmp;
}
private final ImmutableList opts;
private Opts(ImmutableList opts) {
this.opts = opts;
}
/**
* Resolve any extractors relative to the provided {@link BlobInfo} and return a new instance.
*/
Opts resolveFrom(BlobInfo info) {
ImmutableList resolvedOpts =
opts.stream()
.map(
o -> {
if (o instanceof ObjectOptExtractor) {
ObjectOptExtractor ex = (ObjectOptExtractor) o;
return ex.extractFromBlobInfo(info);
} else {
return o;
}
})
.collect(ImmutableList.toImmutableList());
return new Opts<>(resolvedOpts);
}
/** Resolve any extractors relative to the provided {@link BlobId} and return a new instance. */
Opts resolveFrom(BlobId id) {
ImmutableList resolvedOpts =
opts.stream()
.map(
o -> {
if (o instanceof ObjectOptExtractor) {
ObjectOptExtractor ex = (ObjectOptExtractor) o;
return ex.extractFromBlobId(id);
} else {
return o;
}
})
.collect(ImmutableList.toImmutableList());
return new Opts<>(resolvedOpts);
}
/**
* Resolve any extractors relative to the provided {@link BucketInfo} and return a new instance.
*/
Opts resolveFrom(BucketInfo info) {
ImmutableList resolvedOpts =
opts.stream()
.map(
o -> {
if (o instanceof BucketOptExtractor) {
BucketOptExtractor ex = (BucketOptExtractor) o;
return ex.extractFromBucketInfo(info);
} else {
return o;
}
})
.collect(ImmutableList.toImmutableList());
return new Opts<>(resolvedOpts);
}
Opts projectAsSource() {
ImmutableList projectedOpts =
opts.stream()
.map(
o -> {
if (o instanceof ProjectAsSource) {
ProjectAsSource p = (ProjectAsSource) o;
return p.asSource();
} else {
return o;
}
})
.collect(ImmutableList.toImmutableList());
return new Opts<>(projectedOpts);
}
/**
* Attempt to construct a {@link StorageRpc} compatible map of {@link StorageRpc.Option}.
*
* Validation ensures an absence of duplicate keys, and mutually exclusive keys.
*/
ImmutableMap getRpcOptions() {
ImmutableMap.Builder builder =
rpcOptionMapper().apply(ImmutableMap.builder());
return (ImmutableMap) mapBuild.apply(builder);
}
Mapper grpcMetadataMapper() {
return fuseMappers(GrpcMetadataMapper.class, GrpcMetadataMapper::getGrpcMetadataMapper);
}
Mapper createBucketsRequest() {
return fuseMappers(BucketTargetOpt.class, BucketTargetOpt::createBucket);
}
Mapper getBucketsRequest() {
return fuseMappers(BucketSourceOpt.class, BucketSourceOpt::getBucket);
}
Mapper listBucketsRequest() {
return fuseMappers(BucketListOpt.class, BucketListOpt::listBuckets);
}
Mapper updateBucketsRequest() {
return fuseMappers(BucketTargetOpt.class, BucketTargetOpt::updateBucket);
}
Mapper deleteBucketsRequest() {
return fuseMappers(BucketTargetOpt.class, BucketTargetOpt::deleteBucket);
}
Mapper lockBucketRetentionPolicyRequest() {
return fuseMappers(BucketTargetOpt.class, BucketTargetOpt::lockBucketRetentionPolicy);
}
Mapper writeObjectRequest() {
return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::writeObject);
}
Mapper getObjectsRequest() {
return fuseMappers(ObjectSourceOpt.class, ObjectSourceOpt::getObject);
}
Mapper readObjectRequest() {
return fuseMappers(ObjectSourceOpt.class, ObjectSourceOpt::readObject);
}
Mapper listObjectsRequest() {
return fuseMappers(ObjectListOpt.class, ObjectListOpt::listObjects);
}
Mapper updateObjectsRequest() {
return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::updateObject);
}
Mapper deleteObjectsRequest() {
return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::deleteObject);
}
Mapper composeObjectsRequest() {
return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::composeObject);
}
Mapper rewriteObjectsRequest() {
return opts.stream()
.filter(isInstanceOf(ObjectTargetOpt.class).or(isInstanceOf(ObjectSourceOpt.class)))
.map(
o -> {
// TODO: Do we need to formalize this type of dual relationship with it's own
// interface?
if (o instanceof ObjectTargetOpt) {
ObjectTargetOpt oto = (ObjectTargetOpt) o;
return oto.rewriteObject();
} else if (o instanceof ObjectSourceOpt) {
ObjectSourceOpt oso = (ObjectSourceOpt) o;
return oso.rewriteObject();
} else {
// in practice this shouldn't happen because of the filter guard upstream
throw new IllegalStateException("Unexpected type: %s" + o.getClass());
}
})
.reduce(Mapper.identity(), Mapper::andThen);
}
Mapper createHmacKeysRequest() {
return fuseMappers(HmacKeyTargetOpt.class, HmacKeyTargetOpt::createHmacKey);
}
Mapper getHmacKeysRequest() {
return fuseMappers(HmacKeySourceOpt.class, HmacKeySourceOpt::getHmacKey);
}
Mapper listHmacKeysRequest() {
return fuseMappers(HmacKeyListOpt.class, HmacKeyListOpt::listHmacKeys);
}
Mapper updateHmacKeysRequest() {
return fuseMappers(HmacKeyTargetOpt.class, HmacKeyTargetOpt::updateHmacKey);
}
Mapper deleteHmacKeysRequest() {
return fuseMappers(HmacKeyTargetOpt.class, HmacKeyTargetOpt::deleteHmacKey);
}
Mapper getIamPolicyRequest() {
return fuseMappers(BucketSourceOpt.class, BucketSourceOpt::getIamPolicy);
}
Mapper blobInfoMapper() {
return fuseMappers(ObjectTargetOpt.class, ObjectTargetOpt::blobInfo);
}
/**
* Here for compatibility. This should NOT be an "Opt" instead an attribute of the channel
* builder. When {@link ReturnRawInputStream} is removed, this method should be removed as well.
*
* @see
* GapicDownloadSessionBuilder.ReadableByteChannelSessionBuilder#setAutoGzipDecompression(boolean)
*/
@Deprecated
boolean autoGzipDecompression() {
return filterTo(ReturnRawInputStream.class).findFirst().map(r -> r.val).orElse(true);
}
Decoder clearBlobFields() {
return filterTo(Fields.class)
.findFirst()
.map(Fields::clearUnselectedBlobFields)
.orElse(Decoder.identity());
}
Decoder clearBucketFields() {
return filterTo(Fields.class)
.findFirst()
.map(Fields::clearUnselectedBucketFields)
.orElse(Decoder.identity());
}
/**
* Create a new instance of Opts where {@code toPrepend} and {@code this}. If an {@link Opt}
* type ({@code Class}) is present in both {@code toPrepend} and {@code this}, the {@link
* Opt} from {@code this} will take priority when applied via one of the produced mappers.
*/
Opts prepend(Opts extends T> toPrepend) {
// inventory the Opt types already present in this
Set extends Class extends Opt>> existingOptTypes =
this.opts.stream().map(Opt::getClass).collect(Collectors.toSet());
ImmutableList list =
Stream.of(
toPrepend.opts.stream()
// exclude those opt types which are already present in this
.filter(o -> !existingOptTypes.contains(o.getClass())),
this.opts.stream())
.flatMap(x -> x)
.collect(ImmutableList.toImmutableList());
return new Opts<>(list);
}
/**
* Create a new instance of {@code Opts} consisting of those {@code Opt}s which are also an
* {@code R}.
*
* i.e. Given {@code Opts} produce {@code Opts}
*/
Opts constrainTo(Class c) {
return new Opts<>(filterTo(c).collect(ImmutableList.toImmutableList()));
}
private Mapper> rpcOptionMapper() {
return fuseMappers(RpcOptVal.class, RpcOptVal::mapper);
}
private Mapper fuseMappers(Class