/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.usergrid.rest.applications.assets;
import net.jcip.annotations.NotThreadSafe;
import org.apache.commons.io.IOUtils;
import org.apache.usergrid.rest.applications.assets.rules.NoAWSCredsRule;
import org.apache.usergrid.rest.applications.assets.rules.NoGoogleCredsRule;
import org.apache.usergrid.rest.test.resource.AbstractRestIT;
import org.apache.usergrid.rest.test.resource.model.ApiResponse;
import org.apache.usergrid.rest.test.resource.model.Entity;
import org.apache.usergrid.services.assets.data.AssetUtils;
import org.apache.usergrid.services.exceptions.AwsPropertiesNotFoundException;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.junit.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_USERGRID_BINARY_UPLOADER;
import static org.apache.usergrid.utils.MapUtils.hashMap;
import static org.junit.Assert.*;
@NotThreadSafe
public class GoogleAssetResourceIT extends AbstractRestIT {
private Map<String, Object> originalProperties;
private static final Logger logger = LoggerFactory.getLogger(GoogleAssetResourceIT.class);
/**
* Mark tests as ignored if no credentials are present
*/
@Rule
public NoGoogleCredsRule credsRule = new NoGoogleCredsRule();
@Before
public void setup() {
originalProperties = getRemoteTestProperties();
setTestProperty(PROPERTIES_USERGRID_BINARY_UPLOADER, "google");
}
@After
public void teardown() {
setTestProperties(originalProperties);
}
@Test
public void ensureMissingFileReturns404() {
Map<String, String> payload = hashMap("name", "assettest");
ApiResponse postResponse = pathResource(getOrgAppPath("missingFile")).post(payload);
UUID assetId = postResponse.getEntities().get(0).getUuid();
assertNotNull(assetId);
try {
pathResource(getOrgAppPath("missingFile/assettest")).getAssetAsStream(true);
fail("Should fail as there isn't an asset to retrieve.");
} catch (NotFoundException nfe) {
} catch (Exception e) {
logger.error("Unexpected exception", e);
fail("Shouldn't return any other kind of exception");
}
}
@Test
public void errorCheckingInvalidProperties() throws Exception {
Map<String, Object> errorTestProperties;
errorTestProperties = getRemoteTestProperties();
setTestProperty("usergrid.binary.bucketname", "xxx");
try {
Map<String, String> payload = hashMap("name", "assetname");
ApiResponse postResponse = pathResource(getOrgAppPath("foos")).post(payload);
UUID assetId = postResponse.getEntities().get(0).getUuid();
assertNotNull(assetId);
// post a binary asset to that entity
byte[] data = IOUtils.toByteArray(getClass().getResourceAsStream("/cassandra_eye.jpg"));
ApiResponse putResponse =
pathResource(getOrgAppPath("foos/" + assetId)).put(data, MediaType.APPLICATION_OCTET_STREAM_TYPE);
} catch (AwsPropertiesNotFoundException e) {
fail("Shouldn't interrupt runtime if access key isnt found.");
} catch (InternalServerErrorException uie) {
assertEquals(500, uie.getResponse().getStatus());
} finally {
setTestProperties(errorTestProperties);
}
}
@Test
public void errorCheckingInvalidPropertiesMultipartUpload() throws Exception {
Map<String, Object> errorTestProperties;
errorTestProperties = getRemoteTestProperties();
//test that we fail gracefully if we have missing properties
setTestProperty("usergrid.binary.bucketname", "xxx");
try {
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/file-bigger-than-5M"));
FormDataMultiPart form = new FormDataMultiPart().field("file", data, MediaType.MULTIPART_FORM_DATA_TYPE);
ApiResponse postResponse = pathResource(getOrgAppPath("foos")).post(form);
UUID assetId = postResponse.getEntities().get(0).getUuid();
logger.info("Waiting for upload to finish...");
Thread.sleep(5000);
// check that entire file was uploaded
ApiResponse getResponse = pathResource(getOrgAppPath("foos/" + assetId)).get(ApiResponse.class);
logger.info("Upload complete!");
InputStream is = pathResource(getOrgAppPath("foos/" + assetId)).getAssetAsStream();
byte[] foundData = IOUtils.toByteArray(is);
assertEquals(data.length, foundData.length);
// delete file
pathResource(getOrgAppPath("foos/" + assetId)).delete();
} catch (AwsPropertiesNotFoundException e) {
fail("Shouldn't interrupt runtime if access key isnt found.");
} catch (ForbiddenException fe) {
assertEquals(403, fe.getResponse().getStatus());
} finally {
setTestProperties(errorTestProperties);
}
}
@Test
public void octetStreamOnDynamicEntity() throws Exception {
this.waitForQueueDrainAndRefreshIndex();
// post an asset entity
Map<String, String> payload = hashMap("name", "assetname");
ApiResponse postResponse = pathResource(getOrgAppPath("foos")).post(payload);
UUID assetId = postResponse.getEntities().get(0).getUuid();
assertNotNull(assetId);
// post a binary asset to that entity
byte[] data = IOUtils.toByteArray(getClass().getResourceAsStream("/cassandra_eye.jpg"));
ApiResponse putResponse = pathResource(getOrgAppPath("foos/" + assetId))
.put(data, MediaType.APPLICATION_OCTET_STREAM_TYPE);
// check that the asset entity has asset metadata
ApiResponse getResponse = pathResource(getOrgAppPath("foos/" + assetId)).get(ApiResponse.class);
Entity entity = getResponse.getEntities().get(0);
Map<String, Object> fileMetadata = (Map<String, Object>) entity.get("file-metadata");
Assert.assertEquals("image/jpeg", fileMetadata.get("content-type"));
Assert.assertEquals(7979, fileMetadata.get("content-length"));
assertEquals(assetId, entity.getUuid());
// get binary asset by UUID
InputStream is = pathResource(getOrgAppPath("foos/" + assetId)).getAssetAsStream();
byte[] foundData = IOUtils.toByteArray(is);
assertEquals(7979, foundData.length);
// get binary asset by name
is = pathResource(getOrgAppPath("foos/assetname")).getAssetAsStream();
foundData = IOUtils.toByteArray(is);
assertEquals(7979, foundData.length);
}
@Test
public void multipartPostFormOnDynamicEntity() throws Exception {
this.waitForQueueDrainAndRefreshIndex();
// post data larger than 5M
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/file-bigger-than-5M"));
FormDataMultiPart form = new FormDataMultiPart().field("file", data, MediaType.MULTIPART_FORM_DATA_TYPE);
ApiResponse putResponse = pathResource(getOrgAppPath("foos")).post(form);
this.waitForQueueDrainAndRefreshIndex();
UUID assetId = putResponse.getEntities().get(0).getUuid();
assertNotNull(assetId);
// retry until upload complete and we can get the data
int retries = 0;
boolean done = false;
byte[] foundData = new byte[0];
while (!done && retries < 30) {
try {
InputStream is = pathResource(getOrgAppPath("foos/" + assetId)).getAssetAsStream();
foundData = IOUtils.toByteArray(is);
done = true;
} catch (Exception intentiallyIgnored) {
}
Thread.sleep(1000);
retries++;
}
// did we get expected number of bytes of data?
assertEquals(5324800, foundData.length);
pathResource(getOrgAppPath("foos/" + assetId)).delete();
}
@Test
public void multipartPutFormOnDynamicEntity() throws Exception {
this.waitForQueueDrainAndRefreshIndex();
// post an entity
Map<String, String> payload = hashMap("foo", "bar");
ApiResponse postResponse = pathResource(getOrgAppPath("foos")).post(payload);
UUID assetId = postResponse.getEntities().get(0).getUuid();
assertNotNull(assetId);
// post asset to that entity
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/cassandra_eye.jpg"));
FormDataMultiPart form = new FormDataMultiPart()
.field("foo", "bar2")
.field("file", data, MediaType.MULTIPART_FORM_DATA_TYPE);
ApiResponse putResponse = pathResource(getOrgAppPath("foos/" + assetId)).put(form);
this.waitForQueueDrainAndRefreshIndex();
// get entity and check asset metadata
ApiResponse getResponse = pathResource(getOrgAppPath("foos/" + assetId)).get(ApiResponse.class);
Entity entity = getResponse.getEntities().get(0);
Map<String, Object> fileMetadata = (Map<String, Object>) entity.get("file-metadata");
long lastModified = Long.parseLong(fileMetadata.get(AssetUtils.LAST_MODIFIED).toString());
assertEquals(assetId, entity.getUuid());
assertEquals("bar2", entity.get("foo"));
assertEquals("image/jpeg", fileMetadata.get(AssetUtils.CONTENT_TYPE));
assertEquals(7979, fileMetadata.get(AssetUtils.CONTENT_LENGTH));
// get asset and check size
InputStream is = pathResource(getOrgAppPath("foos/" + assetId)).getAssetAsStream();
byte[] foundData = IOUtils.toByteArray(is);
assertEquals(7979, foundData.length);
// upload new asset to entity, then check that it was updated
ApiResponse putResponse2 = pathResource(getOrgAppPath("foos/" + assetId)).put(form);
entity = putResponse2.getEntities().get(0);
fileMetadata = (Map<String, Object>) entity.get("file-metadata");
long justModified = Long.parseLong(fileMetadata.get(AssetUtils.LAST_MODIFIED).toString());
assertNotEquals(lastModified, justModified);
}
@Test
public void largeFile() throws Exception {
this.waitForQueueDrainAndRefreshIndex();
// upload file larger than 5MB
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/file-bigger-than-5M"));
FormDataMultiPart form = new FormDataMultiPart().field("file", data, MediaType.MULTIPART_FORM_DATA_TYPE);
ApiResponse postResponse = pathResource(getOrgAppPath("foos")).post(form);
UUID assetId = postResponse.getEntities().get(0).getUuid();
logger.info("Waiting for upload to finish...");
Thread.sleep(5000);
// check that entire file was uploaded
ApiResponse getResponse = pathResource(getOrgAppPath("foos/" + assetId)).get(ApiResponse.class);
logger.info("Upload complete!");
InputStream is = pathResource(getOrgAppPath("foos/" + assetId)).getAssetAsStream();
byte[] foundData = IOUtils.toByteArray(is);
assertEquals(data.length, foundData.length);
// delete file
pathResource(getOrgAppPath("foos/" + assetId)).delete();
}
@Test
public void fileTooLargeShouldResultInError() throws Exception {
this.waitForQueueDrainAndRefreshIndex();
// set max file size down to 5mb
setTestProperty("usergrid.binary.max-size-mb", "5");
try {
// upload a file larger than 6mb
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/ship-larger-than-6mb.gif"));
FormDataMultiPart form = new FormDataMultiPart().field("file", data, MediaType.MULTIPART_FORM_DATA_TYPE);
ApiResponse postResponse = pathResource(getOrgAppPath("bars")).post(form);
UUID assetId = postResponse.getEntities().get(0).getUuid();
String errorMessage = null;
logger.info("Waiting for upload to finish...");
Thread.sleep(1000);
// attempt to get asset entity, it should contain error
waitForQueueDrainAndRefreshIndex();
ApiResponse getResponse = pathResource(getOrgAppPath("bars/" + assetId)).get(ApiResponse.class);
Map<String, Object> fileMetadata = (Map<String, Object>) getResponse.getEntities().get(0).get("file-metadata");
assertNotNull(fileMetadata);
assertNotNull(fileMetadata.get("error"));
assertTrue(fileMetadata.get("error").toString().startsWith("Asset size "));
} finally {
// set max upload size back to default 25mb
setTestProperties(originalProperties);
}
}
/**
* Deleting a connection to an asset should not delete the asset or the asset's data
*/
@Test
public void deleteConnectionToAsset() throws IOException {
this.waitForQueueDrainAndRefreshIndex();
// create the entity that will be the asset, an image
Map<String, String> payload = hashMap("name", "cassandra_eye.jpg");
ApiResponse postReponse = pathResource(getOrgAppPath("foos")).post(payload);
final UUID uuid = postReponse.getEntities().get(0).getUuid();
// post image data to the asset entity
byte[] data = IOUtils.toByteArray(this.getClass().getResourceAsStream("/cassandra_eye.jpg"));
pathResource(getOrgAppPath("foos/" + uuid)).put(data, MediaType.APPLICATION_OCTET_STREAM_TYPE);
// create an imagegallery entity
Map<String, String> imageGalleryPayload = hashMap("name", "my image gallery");
ApiResponse postResponse2 = pathResource(getOrgAppPath("imagegalleries")).post(imageGalleryPayload);
UUID imageGalleryId = postResponse2.getEntities().get(0).getUuid();
// connect imagegallery to asset
ApiResponse connectResponse = pathResource(
getOrgAppPath("imagegalleries/" + imageGalleryId + "/contains/" + uuid)).post(ApiResponse.class);
this.waitForQueueDrainAndRefreshIndex();
// verify connection from imagegallery to asset
ApiResponse containsResponse = pathResource(
getOrgAppPath("imagegalleries/" + imageGalleryId + "/contains/")).get(ApiResponse.class);
assertEquals(uuid, containsResponse.getEntities().get(0).getUuid());
// delete the connection
pathResource(getOrgAppPath("imagegalleries/" + imageGalleryId + "/contains/" + uuid)).delete();
this.waitForQueueDrainAndRefreshIndex();
// verify that connection is gone
ApiResponse listResponse = pathResource(
getOrgAppPath("imagegalleries/" + imageGalleryId + "/contains/")).get(ApiResponse.class);
assertEquals(0, listResponse.getEntityCount());
// asset should still be there
ApiResponse getResponse2 = pathResource(getOrgAppPath("foos/" + uuid)).get(ApiResponse.class);
Entity entity = getResponse2.getEntities().get(0);
Map<String, Object> fileMetadata = (Map<String, Object>) entity.get("file-metadata");
Assert.assertEquals("image/jpeg", fileMetadata.get(AssetUtils.CONTENT_TYPE));
Assert.assertEquals(7979, fileMetadata.get(AssetUtils.CONTENT_LENGTH));
assertEquals(uuid, entity.getUuid());
// asset data should still be there
InputStream assetIs = pathResource(getOrgAppPath("foos/" + uuid)).getAssetAsStream();
byte[] foundData = IOUtils.toByteArray(assetIs);
assertEquals(7979, foundData.length);
}
}