mirror of https://github.com/apache/jclouds.git
JCLOUDS-1008: Use @Encoded with GCS.
Google cloud storage should use the @Encoded annotation with the object names to make sure that the object is percent-encoded prior to being submitted in the path of the request. This was previously broken because the default path encoding ignores "/" and encodes the entire string. The @Encoded annotation instructs jclouds annotation processor to not encode the parameters to which it is attached and not to encode the entire request path. Parameters that are not annotated with @Encoded are URL encoded prior to being add to the path.
This commit is contained in:
parent
06125793d2
commit
2385ba901e
|
@ -20,8 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static org.jclouds.googlecloudstorage.domain.DomainResourceReferences.ObjectRole.READER;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -41,11 +39,11 @@ import org.jclouds.blobstore.domain.internal.BlobImpl;
|
|||
import org.jclouds.blobstore.domain.internal.PageSetImpl;
|
||||
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||
import org.jclouds.blobstore.internal.BaseBlobStore;
|
||||
import org.jclouds.blobstore.options.CopyOptions;
|
||||
import org.jclouds.blobstore.options.CreateContainerOptions;
|
||||
import org.jclouds.blobstore.options.GetOptions;
|
||||
import org.jclouds.blobstore.options.ListContainerOptions;
|
||||
import org.jclouds.blobstore.options.PutOptions;
|
||||
import org.jclouds.blobstore.options.CopyOptions;
|
||||
import org.jclouds.blobstore.strategy.internal.FetchBlobMetadata;
|
||||
import org.jclouds.blobstore.util.BlobUtils;
|
||||
import org.jclouds.collect.Memoized;
|
||||
|
@ -73,11 +71,10 @@ import org.jclouds.http.HttpResponseException;
|
|||
import org.jclouds.io.ContentMetadata;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.io.PayloadSlicer;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.hash.HashCode;
|
||||
|
@ -204,12 +201,7 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
*/
|
||||
@Override
|
||||
public boolean blobExists(String container, String name) {
|
||||
try {
|
||||
String urlName = name.contains("/") ? URLEncoder.encode(name, Charsets.UTF_8.toString()) : name;
|
||||
return api.getObjectApi().objectExists(container, urlName);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return api.getObjectApi().objectExists(container, Strings2.urlEncode(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,12 +231,12 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
|
||||
@Override
|
||||
public BlobMetadata blobMetadata(String container, String name) {
|
||||
return objectToBlobMetadata.apply(api.getObjectApi().getObject(container, name));
|
||||
return objectToBlobMetadata.apply(api.getObjectApi().getObject(container, Strings2.urlEncode(name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob getBlob(String container, String name, GetOptions options) {
|
||||
GoogleCloudStorageObject gcsObject = api.getObjectApi().getObject(container, name);
|
||||
GoogleCloudStorageObject gcsObject = api.getObjectApi().getObject(container, Strings2.urlEncode(name));
|
||||
if (gcsObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -252,7 +244,7 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
MutableBlobMetadata metadata = objectToBlobMetadata.apply(gcsObject);
|
||||
Blob blob = new BlobImpl(metadata);
|
||||
// TODO: Does getObject not get the payload?!
|
||||
Payload payload = api.getObjectApi().download(container, name, httpOptions).getPayload();
|
||||
Payload payload = api.getObjectApi().download(container, Strings2.urlEncode(name), httpOptions).getPayload();
|
||||
payload.setContentMetadata(metadata.getContentMetadata()); // Doing this first retains it on setPayload.
|
||||
blob.setPayload(payload);
|
||||
return blob;
|
||||
|
@ -260,18 +252,13 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
|
||||
@Override
|
||||
public void removeBlob(String container, String name) {
|
||||
String urlName;
|
||||
try {
|
||||
urlName = name.contains("/") ? URLEncoder.encode(name, Charsets.UTF_8.toString()) : name;
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw Throwables.propagate(uee);
|
||||
}
|
||||
api.getObjectApi().deleteObject(container, urlName);
|
||||
api.getObjectApi().deleteObject(container, Strings2.urlEncode(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlobAccess getBlobAccess(String container, String name) {
|
||||
ObjectAccessControls controls = api.getObjectAccessControlsApi().getObjectAccessControls(container, name, "allUsers");
|
||||
ObjectAccessControls controls = api.getObjectAccessControlsApi().getObjectAccessControls(container,
|
||||
Strings2.urlEncode(name), "allUsers");
|
||||
if (controls != null && controls.role() == DomainResourceReferences.ObjectRole.READER) {
|
||||
return BlobAccess.PUBLIC_READ;
|
||||
} else {
|
||||
|
@ -287,9 +274,9 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
.bucket(container)
|
||||
.role(READER)
|
||||
.build();
|
||||
api.getObjectApi().patchObject(container, name, new ObjectTemplate().addAcl(controls));
|
||||
api.getObjectApi().patchObject(container, Strings2.urlEncode(name), new ObjectTemplate().addAcl(controls));
|
||||
} else {
|
||||
api.getObjectAccessControlsApi().deleteObjectAccessControls(container, name, "allUsers");
|
||||
api.getObjectAccessControlsApi().deleteObjectAccessControls(container, Strings2.urlEncode(name), "allUsers");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +299,8 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
public String copyBlob(String fromContainer, String fromName, String toContainer, String toName,
|
||||
CopyOptions options) {
|
||||
if (!options.getContentMetadata().isPresent() && !options.getUserMetadata().isPresent()) {
|
||||
return api.getObjectApi().copyObject(toContainer, toName, fromContainer, fromName).etag();
|
||||
return api.getObjectApi().copyObject(toContainer, Strings2.urlEncode(toName), fromContainer,
|
||||
Strings2.urlEncode(fromName)).etag();
|
||||
}
|
||||
|
||||
ObjectTemplate template = new ObjectTemplate();
|
||||
|
@ -349,7 +337,8 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
template.customMetadata(options.getUserMetadata().get());
|
||||
}
|
||||
|
||||
return api.getObjectApi().copyObject(toContainer, toName, fromContainer, fromName, template).etag();
|
||||
return api.getObjectApi().copyObject(toContainer, Strings2.urlEncode(toName), fromContainer,
|
||||
Strings2.urlEncode(fromName), template).etag();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -372,12 +361,14 @@ public final class GoogleCloudStorageBlobStore extends BaseBlobStore {
|
|||
public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) {
|
||||
ImmutableList.Builder<GoogleCloudStorageObject> builder = ImmutableList.builder();
|
||||
for (MultipartPart part : parts) {
|
||||
builder.add(api.getObjectApi().getObject(mpu.containerName(), getMPUPartName(mpu, part.partNumber())));
|
||||
builder.add(api.getObjectApi().getObject(mpu.containerName(),
|
||||
Strings2.urlEncode(getMPUPartName(mpu, part.partNumber()))));
|
||||
}
|
||||
ObjectTemplate destination = blobMetadataToObjectTemplate.apply(mpu.blobMetadata());
|
||||
ComposeObjectTemplate template = ComposeObjectTemplate.builder().fromGoogleCloudStorageObject(builder.build())
|
||||
.destination(destination).build();
|
||||
return api.getObjectApi().composeObjects(mpu.containerName(), mpu.blobName(), template).etag();
|
||||
return api.getObjectApi().composeObjects(mpu.containerName(), Strings2.urlEncode(mpu.blobName()), template)
|
||||
.etag();
|
||||
// TODO: delete components?
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
|||
import javax.inject.Named;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.Encoded;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
|
@ -41,7 +42,6 @@ import org.jclouds.rest.annotations.Fallback;
|
|||
import org.jclouds.rest.annotations.PATCH;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.SelectJson;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,6 @@ import org.jclouds.rest.binders.BindToJsonPayload;
|
|||
*
|
||||
* @see <a href = " https://developers.google.com/storage/docs/json_api/v1/objectAccessControls "/>
|
||||
*/
|
||||
@SkipEncoding({ '/', '=' })
|
||||
@RequestFilters(OAuthFilter.class)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public interface ObjectAccessControlsApi {
|
||||
|
@ -74,7 +73,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
ObjectAccessControls getObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity);
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity);
|
||||
|
||||
/**
|
||||
* Returns the acl entry for the specified entity on the specified object.
|
||||
|
@ -97,7 +96,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
ObjectAccessControls getObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@QueryParam("generation") Long generation);
|
||||
|
||||
/**
|
||||
|
@ -116,7 +115,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl")
|
||||
ObjectAccessControls createObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template);
|
||||
|
||||
/**
|
||||
|
@ -137,7 +136,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl")
|
||||
ObjectAccessControls createObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template,
|
||||
@QueryParam("generation") Long generation);
|
||||
|
||||
|
@ -155,8 +154,8 @@ public interface ObjectAccessControlsApi {
|
|||
@Named("ObjectAccessControls:delete")
|
||||
@DELETE
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
void deleteObjectAccessControls(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@PathParam("entity") String entity);
|
||||
void deleteObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity);
|
||||
|
||||
/**
|
||||
* Permanently deletes the acl entry for the specified entity on the specified bucket.
|
||||
|
@ -174,8 +173,9 @@ public interface ObjectAccessControlsApi {
|
|||
@Named("ObjectAccessControls:delete")
|
||||
@DELETE
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
void deleteObjectAccessControls(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@PathParam("entity") String entity, @QueryParam("generation") Long generation);
|
||||
void deleteObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@QueryParam("generation") Long generation);
|
||||
|
||||
/**
|
||||
* Retrieves acl entries on a specified object
|
||||
|
@ -193,7 +193,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
List<ObjectAccessControls> listObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName);
|
||||
@PathParam("object") @Encoded String objectName);
|
||||
|
||||
/**
|
||||
* Retrieves acl entries on a specified object
|
||||
|
@ -214,7 +214,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
List<ObjectAccessControls> listObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @QueryParam("generation") Long generation);
|
||||
@PathParam("object") @Encoded String objectName, @QueryParam("generation") Long generation);
|
||||
|
||||
/**
|
||||
* Updates an acl entry on the specified object
|
||||
|
@ -237,7 +237,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
ObjectAccessControls updateObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template);
|
||||
|
||||
/**
|
||||
|
@ -262,7 +262,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
ObjectAccessControls updateObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template,
|
||||
@QueryParam("generation") Long generation);
|
||||
|
||||
|
@ -286,7 +286,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
ObjectAccessControls patchObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template);
|
||||
|
||||
/**
|
||||
|
@ -311,7 +311,7 @@ public interface ObjectAccessControlsApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("/b/{bucket}/o/{object}/acl/{entity}")
|
||||
ObjectAccessControls patchObjectAccessControls(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") String objectName, @PathParam("entity") String entity,
|
||||
@PathParam("object") @Encoded String objectName, @PathParam("entity") String entity,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectAccessControlsTemplate template,
|
||||
@QueryParam("generation") Long generation);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
|
|||
import javax.inject.Named;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.Encoded;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
|
@ -60,7 +61,6 @@ import org.jclouds.rest.annotations.PayloadParam;
|
|||
import org.jclouds.rest.annotations.QueryParams;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,6 @@ import org.jclouds.rest.binders.BindToJsonPayload;
|
|||
*
|
||||
* @see <a href="https://developers.google.com/storage/docs/json_api/v1/objects"/>
|
||||
*/
|
||||
@SkipEncoding({ '/', '=' })
|
||||
@RequestFilters(OAuthFilter.class)
|
||||
public interface ObjectApi {
|
||||
|
||||
|
@ -87,7 +86,7 @@ public interface ObjectApi {
|
|||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(FalseOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
boolean objectExists(@PathParam("bucket") String bucketName, @PathParam("object") String objectName);
|
||||
boolean objectExists(@PathParam("bucket") String bucketName, @PathParam("object") @Encoded String objectName);
|
||||
|
||||
/**
|
||||
* Retrieve an object metadata
|
||||
|
@ -105,7 +104,8 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
GoogleCloudStorageObject getObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName);
|
||||
GoogleCloudStorageObject getObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName);
|
||||
|
||||
/**
|
||||
* Retrieves objects metadata
|
||||
|
@ -126,8 +126,8 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
GoogleCloudStorageObject getObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
HttpRequestOptions options);
|
||||
GoogleCloudStorageObject getObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName, HttpRequestOptions options);
|
||||
|
||||
/**
|
||||
* Retrieve an object or their metadata
|
||||
|
@ -146,7 +146,7 @@ public interface ObjectApi {
|
|||
@ResponseParser(ParseToPayloadEnclosing.class)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable
|
||||
PayloadEnclosing download(@PathParam("bucket") String bucketName, @PathParam("object") String objectName);
|
||||
PayloadEnclosing download(@PathParam("bucket") String bucketName, @PathParam("object") @Encoded String objectName);
|
||||
|
||||
/**
|
||||
* Retrieves objects
|
||||
|
@ -167,7 +167,8 @@ public interface ObjectApi {
|
|||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@ResponseParser(ParseToPayloadEnclosing.class)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
@Nullable PayloadEnclosing download(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@Nullable
|
||||
PayloadEnclosing download(@PathParam("bucket") String bucketName, @PathParam("object") @Encoded String objectName,
|
||||
HttpRequestOptions options);
|
||||
|
||||
/**
|
||||
|
@ -204,7 +205,7 @@ public interface ObjectApi {
|
|||
@DELETE
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(FalseOnNotFoundOr404.class)
|
||||
boolean deleteObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName);
|
||||
boolean deleteObject(@PathParam("bucket") String bucketName, @PathParam("object") @Encoded String objectName);
|
||||
|
||||
/**
|
||||
* Deletes an object and its metadata. Deletions are permanent if versioning is not enabled for the bucket, or if the
|
||||
|
@ -221,7 +222,7 @@ public interface ObjectApi {
|
|||
@DELETE
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(FalseOnNotFoundOr404.class)
|
||||
boolean deleteObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
boolean deleteObject(@PathParam("bucket") String bucketName, @PathParam("object") @Encoded String objectName,
|
||||
DeleteObjectOptions options);
|
||||
|
||||
/**
|
||||
|
@ -271,8 +272,9 @@ public interface ObjectApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
GoogleCloudStorageObject updateObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate);
|
||||
GoogleCloudStorageObject updateObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate);
|
||||
|
||||
/**
|
||||
* Updates an object
|
||||
|
@ -294,8 +296,9 @@ public interface ObjectApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
GoogleCloudStorageObject updateObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate, UpdateObjectOptions options);
|
||||
GoogleCloudStorageObject updateObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate, UpdateObjectOptions options);
|
||||
|
||||
/**
|
||||
* Updates an object according to patch semantics
|
||||
|
@ -315,8 +318,9 @@ public interface ObjectApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
GoogleCloudStorageObject patchObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate);
|
||||
GoogleCloudStorageObject patchObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate);
|
||||
|
||||
/**
|
||||
* Updates an object according to patch semantics
|
||||
|
@ -338,8 +342,9 @@ public interface ObjectApi {
|
|||
@Produces(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{bucket}/o/{object}")
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
GoogleCloudStorageObject patchObject(@PathParam("bucket") String bucketName, @PathParam("object") String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate, UpdateObjectOptions options);
|
||||
GoogleCloudStorageObject patchObject(@PathParam("bucket") String bucketName,
|
||||
@PathParam("object") @Encoded String objectName,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate objectTemplate, UpdateObjectOptions options);
|
||||
|
||||
/**
|
||||
* Concatenates a list of existing objects into a new object in the same bucket.
|
||||
|
@ -358,7 +363,7 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{destinationBucket}/o/{destinationObject}/compose")
|
||||
GoogleCloudStorageObject composeObjects(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject,
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@BinderParam(BindToJsonPayload.class) ComposeObjectTemplate composeObjectTemplate);
|
||||
|
||||
/**
|
||||
|
@ -380,7 +385,7 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("storage/v1/b/{destinationBucket}/o/{destinationObject}/compose")
|
||||
GoogleCloudStorageObject composeObjects(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject,
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@BinderParam(BindToJsonPayload.class) ComposeObjectTemplate composeObjectTemplate,
|
||||
ComposeObjectOptions options);
|
||||
|
||||
|
@ -403,8 +408,9 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("/storage/v1/b/{sourceBucket}/o/{sourceObject}/copyTo/b/{destinationBucket}/o/{destinationObject}")
|
||||
GoogleCloudStorageObject copyObject(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") String sourceObject);
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") @Encoded String sourceObject);
|
||||
|
||||
/**
|
||||
* Copies an object to a specified location with updated metadata.
|
||||
|
@ -427,8 +433,10 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("/storage/v1/b/{sourceBucket}/o/{sourceObject}/copyTo/b/{destinationBucket}/o/{destinationObject}")
|
||||
GoogleCloudStorageObject copyObject(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") String sourceObject, @BinderParam(BindToJsonPayload.class) ObjectTemplate template);
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") @Encoded String sourceObject,
|
||||
@BinderParam(BindToJsonPayload.class) ObjectTemplate template);
|
||||
|
||||
/**
|
||||
* Copies an object to a specified location. Optionally overrides metadata.
|
||||
|
@ -451,8 +459,9 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("/storage/v1/b/{sourceBucket}/o/{sourceObject}/copyTo/b/{destinationBucket}/o/{destinationObject}")
|
||||
GoogleCloudStorageObject copyObject(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") String sourceObject, CopyObjectOptions options);
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") @Encoded String sourceObject, CopyObjectOptions options);
|
||||
|
||||
/**
|
||||
* Stores a new object with metadata.
|
||||
|
@ -495,9 +504,8 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("/storage/v1/b/{sourceBucket}/o/{sourceObject}/rewriteTo/b/{destinationBucket}/o/{destinationObject}")
|
||||
RewriteResponse rewriteObjects(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") String sourceObject);
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket, @PathParam("sourceObject") @Encoded String sourceObject);
|
||||
|
||||
/**
|
||||
* Rewrites a source object to a destination object.
|
||||
|
@ -520,8 +528,8 @@ public interface ObjectApi {
|
|||
@Consumes(APPLICATION_JSON)
|
||||
@Path("/storage/v1/b/{sourceBucket}/o/{sourceObject}/rewriteTo/b/{destinationBucket}/o/{destinationObject}")
|
||||
RewriteResponse rewriteObjects(@PathParam("destinationBucket") String destinationBucket,
|
||||
@PathParam("destinationObject") String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") String sourceObject,
|
||||
RewriteObjectOptions options);
|
||||
@PathParam("destinationObject") @Encoded String destinationObject,
|
||||
@PathParam("sourceBucket") String sourceBucket,
|
||||
@PathParam("sourceObject") @Encoded String sourceObject,
|
||||
RewriteObjectOptions options);
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@ public class GoogleCloudStorageBlobIntegrationLiveTest extends BaseBlobIntegrati
|
|||
|
||||
return new Object[][] { { "file.xml", "text/xml", file, realObject },
|
||||
{ "string.xml", "text/xml", realObject, realObject },
|
||||
{ "stringwith/slash.xml", "text/xml", realObject, realObject },
|
||||
{ "bytes.xml", "application/octet-stream", realObject.getBytes(), realObject } };
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.jclouds.googlecloudstorage.parse.ParseGoogleCloudStorageObjectListTes
|
|||
import org.jclouds.googlecloudstorage.parse.ParseObjectRewriteResponse;
|
||||
import org.jclouds.http.internal.PayloadEnclosingImpl;
|
||||
import org.jclouds.io.PayloadEnclosing;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.net.MediaType;
|
||||
|
@ -57,6 +58,13 @@ public class ObjectApiMockTest extends BaseGoogleCloudStorageApiMockTest {
|
|||
assertSent(server, "GET", "/storage/v1/b/test/o/file_name", null);
|
||||
}
|
||||
|
||||
public void existsEncoded() throws Exception {
|
||||
server.enqueue(jsonResponse("/object_encoded_get.json"));
|
||||
|
||||
assertTrue(objectApi().objectExists("test", Strings2.urlEncode("dir/file name")));
|
||||
assertSent(server, "GET", "/storage/v1/b/test/o/dir%2Ffile%20name", null);
|
||||
}
|
||||
|
||||
public void exists_4xx() throws Exception {
|
||||
server.enqueue(response404());
|
||||
|
||||
|
@ -120,6 +128,14 @@ public class ObjectApiMockTest extends BaseGoogleCloudStorageApiMockTest {
|
|||
assertSent(server, "DELETE", "/storage/v1/b/test/o/object_name", null);
|
||||
}
|
||||
|
||||
public void delete_encoded() throws Exception {
|
||||
server.enqueue(new MockResponse());
|
||||
|
||||
// TODO: Should this be returning True on not found?
|
||||
assertTrue(objectApi().deleteObject("test", Strings2.urlEncode("dir/object name")));
|
||||
assertSent(server, "DELETE", "/storage/v1/b/test/o/dir%2Fobject%20name", null);
|
||||
}
|
||||
|
||||
public void list() throws Exception {
|
||||
server.enqueue(jsonResponse("/object_list.json"));
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"kind": "storage#object",
|
||||
"id": "test/dir%2Ffile%20name/1000",
|
||||
"selfLink": "https://www.googleapis.com/storage/v1/b/test/o/dir%2Ffile%20name",
|
||||
"name": "dir%2Ffile%20name",
|
||||
"bucket": "test",
|
||||
"generation": "1000",
|
||||
"metageneration": "8",
|
||||
"contentType": "application/x-tar",
|
||||
"updated": "2014-09-27T00:01:44.819",
|
||||
"storageClass": "STANDARD",
|
||||
"size": "1000",
|
||||
"md5Hash": "md5Hash",
|
||||
"mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/dir%2Ffile%20name?generation=1000&alt=media",
|
||||
"owner": {
|
||||
"entity": "entity",
|
||||
"entityId": "entityId"
|
||||
},
|
||||
"crc32c": "crc32c",
|
||||
"etag": "etag"
|
||||
}
|
Loading…
Reference in New Issue