/* * 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.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.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.MultiPart; import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.MediaType; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; 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 AssetResourceIT extends AbstractRestIT { private String access_token; private static final Logger logger = LoggerFactory.getLogger( AssetResourceIT.class ); private Map<String, Object> originalProperties; @Before public void setup(){ originalProperties = getRemoteTestProperties(); setTestProperty(PROPERTIES_USERGRID_BINARY_UPLOADER, "local"); access_token = this.getAdminToken().getAccessToken(); } @After public void teardown(){ setTestProperties(originalProperties); } @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 verifyMetadataChanged() throws Exception { this.waitForQueueDrainAndRefreshIndex(); // post an entity Map<String, String> payload = hashMap( "name", "verifed" ); 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() //TODO:Fix name field in assets, i mean what are they supposed to do? .field( "name", "verifyMetadataChangedTest" ) .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( "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 form = new FormDataMultiPart() .field( "name", "verifyMetadataChangedTest" ) .field( "file", this.getClass().getResourceAsStream( "/test.txt" ) ,MediaType.TEXT_PLAIN_TYPE); pathResource( getOrgAppPath( "foos/" + assetId ) ).put( form ); //re-get to verify data was saved to backend getResponse = pathResource( getOrgAppPath( "foos/" + assetId ) ).get( ApiResponse.class ); entity = getResponse.getEntities().get( 0 ); fileMetadata = (Map<String, Object>)entity.get("file-metadata"); long justModified = Long.parseLong( fileMetadata.get( AssetUtils.LAST_MODIFIED ).toString() ); assertNotEquals( lastModified, justModified ); assertEquals( assetId, entity.getUuid() ); assertNotNull(entity.get( "file" )); assertEquals( "text/plain", fileMetadata.get( AssetUtils.CONTENT_TYPE ) ); assertEquals( 879, fileMetadata.get( AssetUtils.CONTENT_LENGTH )); //now change it back to the picture asset data = IOUtils.toByteArray( this.getClass().getResourceAsStream( "/cassandra_eye.jpg" ) ); form = new FormDataMultiPart() .field( "name", "verifyMetadataChangedTest" ) .field( "file", data, MediaType.MULTIPART_FORM_DATA_TYPE ); putResponse = pathResource( getOrgAppPath( "foos/" + assetId ) ).put( form ); this.waitForQueueDrainAndRefreshIndex(); //verify that data was correctly written to backend getResponse = pathResource( getOrgAppPath( "foos/" + assetId ) ).get( ApiResponse.class ); entity = getResponse.getEntities().get( 0 ); fileMetadata = (Map<String, Object>)entity.get("file-metadata"); Long.parseLong( fileMetadata.get( AssetUtils.LAST_MODIFIED ).toString() ); assertNull(entity.get( "file" )); assertEquals( assetId, entity.getUuid() ); assertEquals( "image/jpeg", fileMetadata.get( AssetUtils.CONTENT_TYPE ) ); assertEquals( 7979, fileMetadata.get( AssetUtils.CONTENT_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( 2000 ); // 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 6mb Map<String, String> props = new HashMap<String, String>(); props.put( "usergrid.binary.max-size-mb", "6" ); pathResource( "testproperties" ).post( props ); try { // upload a file larger than 6mb final StreamDataBodyPart part = new StreamDataBodyPart( "file", getClass().getResourceAsStream( "/ship-larger-than-6mb.gif" ), "ship"); final MultiPart multipart = new FormDataMultiPart().bodyPart( part ); ApiResponse postResponse = pathResource( getOrgAppPath( "bars" ) ).post( multipart ); UUID assetId = postResponse.getEntities().get(0).getUuid(); String errorMessage = null; logger.info( "Waiting for upload to finish..." ); Thread.sleep( 2000 ); // attempt to get asset entity, it should contain error ApiResponse getResponse = pathResource( getOrgAppPath( "bars/" +assetId ) ).get( ApiResponse.class ); Map<String, Object> fileMetadata = (Map<String, Object>)getResponse.getEntities().get(0).get("file-metadata"); assertTrue( fileMetadata.get( "error" ).toString().startsWith( "Asset size " ) ); } finally { // set max upload size back to default 25mb props.put( "usergrid.binary.max-size-mb", "25" ); pathResource( "testproperties" ).post( props ); } } /** * 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); } }