Adds rest tests and fixes bugs with POST/PUT re-ordering
Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/82814303 Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/82814303 Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/82814303 Branch: refs/heads/USERGRID-933 Commit: 82814303135e47cc976fea373396aad85afb07f5 Parents: 079ba97 Author: Todd Nine <tn...@apigee.com> Authored: Wed Sep 23 11:32:11 2015 -0600 Committer: Todd Nine <tn...@apigee.com> Committed: Wed Sep 23 11:32:11 2015 -0600 ---------------------------------------------------------------------- .../corepersistence/CpRelationManager.java | 7 +- .../users/ConnectionResourceTest.java | 302 ++++++++++++++----- .../services/AbstractConnectionsService.java | 43 ++- 3 files changed, 275 insertions(+), 77 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/82814303/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpRelationManager.java ---------------------------------------------------------------------- diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpRelationManager.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpRelationManager.java index 21a2ee7..de687b3 100644 --- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpRelationManager.java +++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpRelationManager.java @@ -713,15 +713,16 @@ public class CpRelationManager implements RelationManager { if ( logger.isDebugEnabled() ) { logger.debug( "Marking edge {} for deletion", edgeToDelete ); } - return gm.markEdge( edge ); + return gm.markEdge( edgeToDelete ); } ).lastOrDefault( null ).doOnNext( lastEdge -> { //no op if we hit our default if(lastEdge == null){ return; } - //queue up async processing - indexService.queueDeleteEdge( applicationScope, lastEdge ); + //don't queue delete b/c that de-indexes, we need to delete the edges only since we have a version still existing to index. + + gm.deleteEdge( lastEdge ).subscribe(); }).subscribe(); http://git-wip-us.apache.org/repos/asf/usergrid/blob/82814303/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java index 22a6165..777b04e 100644 --- a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java +++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java @@ -17,19 +17,26 @@ package org.apache.usergrid.rest.applications.collection.users; -import com.sun.jersey.api.client.UniformInterfaceException; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.apache.usergrid.rest.test.resource.AbstractRestIT; import org.apache.usergrid.rest.test.resource.endpoints.CollectionEndpoint; +import org.apache.usergrid.rest.test.resource.model.ApiResponse; import org.apache.usergrid.rest.test.resource.model.Collection; import org.apache.usergrid.rest.test.resource.model.Entity; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.usergrid.rest.test.resource.model.QueryParameters; -import java.io.IOException; -import java.util.Map; +import com.sun.jersey.api.client.UniformInterfaceException; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; /** @@ -39,44 +46,46 @@ import static org.junit.Assert.*; * @since 4.0 */ public class ConnectionResourceTest extends AbstractRestIT { - private static Logger log = LoggerFactory.getLogger(ConnectionResourceTest.class); + private static Logger log = LoggerFactory.getLogger( ConnectionResourceTest.class ); + @Test public void connectionsQueryTest() throws IOException { //create a peep Entity peep = new Entity(); - peep.put("type", "chicken"); + peep.put( "type", "chicken" ); - peep = this.app().collection("peeps").post(peep); + peep = this.app().collection( "peeps" ).post( peep ); Entity todd = new Entity(); - todd.put("username", "todd"); - todd = this.app().collection("users").post(todd); + todd.put( "username", "todd" ); + todd = this.app().collection( "users" ).post( todd ); Entity scott = new Entity(); - scott.put("username", "scott"); - scott = this.app().collection("users").post(scott); + scott.put( "username", "scott" ); + scott = this.app().collection( "users" ).post( scott ); Entity objectOfDesire = new Entity(); - objectOfDesire.put("codingmunchies", "doritoes"); - objectOfDesire = this.app().collection("snacks").post(objectOfDesire); + objectOfDesire.put( "codingmunchies", "doritoes" ); + objectOfDesire = this.app().collection( "snacks" ).post( objectOfDesire ); refreshIndex(); - Entity toddWant = this.app().collection("users").entity(todd).collection("likes").collection("snacks").entity(objectOfDesire).post(); - assertNotNull(toddWant); + Entity toddWant = this.app().collection( "users" ).entity( todd ).collection( "likes" ).collection( "snacks" ) + .entity( objectOfDesire ).post(); + assertNotNull( toddWant ); try { - this.app().collection("users").entity(scott).collection("likes").collection("peeps").entity(peep).get(); - fail("This should throw an exception"); - } catch (UniformInterfaceException uie) { + this.app().collection( "users" ).entity( scott ).collection( "likes" ).collection( "peeps" ).entity( peep ) + .get(); + fail( "This should throw an exception" ); + } + catch ( UniformInterfaceException uie ) { // Should return a 404 Not Found - assertEquals(404, uie.getResponse().getStatus()); + assertEquals( 404, uie.getResponse().getStatus() ); } - - } @@ -85,125 +94,274 @@ public class ConnectionResourceTest extends AbstractRestIT { // create entities thing1 and thing2 Entity thing1 = new Entity(); - thing1.put("name", "thing1"); - thing1 = this.app().collection("things").post(thing1); + thing1.put( "name", "thing1" ); + thing1 = this.app().collection( "things" ).post( thing1 ); Entity thing2 = new Entity(); - thing2.put("name", "thing2"); - thing2 = this.app().collection("things").post(thing2); + thing2.put( "name", "thing2" ); + thing2 = this.app().collection( "things" ).post( thing2 ); refreshIndex(); //create the connection: thing1 likes thing2 - this.app().collection("things").entity(thing1).connection("likes").collection("things").entity(thing2).post(); + this.app().collection( "things" ).entity( thing1 ).connection( "likes" ).collection( "things" ).entity( thing2 ) + .post(); refreshIndex(); //test we have the "likes" in our connection meta data response - thing1 = this.app().collection("things").entity(thing1).get(); + thing1 = this.app().collection( "things" ).entity( thing1 ).get(); //TODO this is ugly. revisit. - String url = (String) ((Map<String, Object>) ((Map<String, Object>) thing1.get("metadata")).get("connections")).get("likes"); - assertNotNull("Connection url returned with entity", url); + String url = ( String ) ( ( Map<String, Object> ) ( ( Map<String, Object> ) thing1.get( "metadata" ) ) + .get( "connections" ) ).get( "likes" ); + assertNotNull( "Connection url returned with entity", url ); //now that we know the URl is correct, follow it - CollectionEndpoint likesEndpoint = new CollectionEndpoint(url, this.context(), this.app()); + CollectionEndpoint likesEndpoint = new CollectionEndpoint( url, this.context(), this.app() ); Collection likes = likesEndpoint.get(); - assertNotNull(likes); + assertNotNull( likes ); Entity likedEntity = likes.next(); - assertNotNull(likedEntity); + assertNotNull( likedEntity ); //make sure the returned entity is thing2 - assertEquals(thing2.getUuid(), likedEntity.getUuid()); + assertEquals( thing2.getUuid(), likedEntity.getUuid() ); //now follow the loopback, which should be pointers to the other entity - thing2 = this.app().collection("things").entity(thing2).get(); + thing2 = this.app().collection( "things" ).entity( thing2 ).get(); //TODO this is ugly. revisit. - url = (String) ((Map<String, Object>) ((Map<String, Object>) thing2.get("metadata")).get("connecting")).get("likes"); - assertNotNull("Connecting url returned with entity", url); + url = ( String ) ( ( Map<String, Object> ) ( ( Map<String, Object> ) thing2.get( "metadata" ) ) + .get( "connecting" ) ).get( "likes" ); + assertNotNull( "Connecting url returned with entity", url ); - CollectionEndpoint likedByEndpoint = new CollectionEndpoint(url, this.context(), this.app()); + CollectionEndpoint likedByEndpoint = new CollectionEndpoint( url, this.context(), this.app() ); Collection likedBy = likedByEndpoint.get(); - assertNotNull(likedBy); + assertNotNull( likedBy ); Entity likedByEntity = likedBy.next(); - assertNotNull(likedByEntity); + assertNotNull( likedByEntity ); //make sure the returned entity is thing1 - assertEquals(thing1.getUuid(), likedByEntity.getUuid()); - + assertEquals( thing1.getUuid(), likedByEntity.getUuid() ); } /** - * Ensure that the connected entity can be deleted - * properly after it has been connected to another entity - * - * @throws IOException + * Ensure that the connected entity can be deleted properly after it has been connected to another entity */ @Test //USERGRID-3011 public void connectionsDeleteSecondEntityInConnectionTest() throws IOException { //Create 2 entities, thing1 and thing2 Entity thing1 = new Entity(); - thing1.put("name", "thing1"); - thing1 = this.app().collection("things").post(thing1); + thing1.put( "name", "thing1" ); + thing1 = this.app().collection( "things" ).post( thing1 ); Entity thing2 = new Entity(); - thing2.put("name", "thing2"); - thing2 = this.app().collection("things").post(thing2); + thing2.put( "name", "thing2" ); + thing2 = this.app().collection( "things" ).post( thing2 ); refreshIndex(); //create the connection: thing1 likes thing2 - this.app().collection("things").entity(thing1).connection("likes").collection("things").entity(thing2).post(); + this.app().collection( "things" ).entity( thing1 ).connection( "likes" ).collection( "things" ).entity( thing2 ) + .post(); //delete thing2 - this.app().collection("things").entity(thing2).delete(); + this.app().collection( "things" ).entity( thing2 ).delete(); refreshIndex(); try { //attempt to retrieve thing1 - thing2 = this.app().collection("things").entity(thing2).get(); - fail("This should throw an exception"); - } catch (UniformInterfaceException uie) { + thing2 = this.app().collection( "things" ).entity( thing2 ).get(); + fail( "This should throw an exception" ); + } + catch ( UniformInterfaceException uie ) { // Should return a 404 Not Found - assertEquals(404, uie.getResponse().getStatus()); + assertEquals( 404, uie.getResponse().getStatus() ); } } + /** - * Ensure that the connecting entity can be deleted - * properly after a connection has been added - * - * @throws IOException + * Ensure that the connecting entity can be deleted properly after a connection has been added */ @Test //USERGRID-3011 public void connectionsDeleteFirstEntityInConnectionTest() throws IOException { //Create 2 entities, thing1 and thing2 Entity thing1 = new Entity(); - thing1.put("name", "thing1"); - thing1 = this.app().collection("things").post(thing1); + thing1.put( "name", "thing1" ); + thing1 = this.app().collection( "things" ).post( thing1 ); Entity thing2 = new Entity(); - thing2.put("name", "thing2"); - thing2 = this.app().collection("things").post(thing2); + thing2.put( "name", "thing2" ); + thing2 = this.app().collection( "things" ).post( thing2 ); refreshIndex(); //create the connection: thing1 likes thing2 - this.app().collection("things").entity(thing1).connection("likes").collection("things").entity(thing2).post(); + this.app().collection( "things" ).entity( thing1 ).connection( "likes" ).collection( "things" ).entity( thing2 ) + .post(); //delete thing1 - this.app().collection("things").entity(thing1).delete(); + this.app().collection( "things" ).entity( thing1 ).delete(); refreshIndex(); try { //attempt to retrieve thing1 - thing1 = this.app().collection("things").entity(thing1).get(); - fail("This should throw an exception"); - } catch (UniformInterfaceException uie) { + thing1 = this.app().collection( "things" ).entity( thing1 ).get(); + fail( "This should throw an exception" ); + } + catch ( UniformInterfaceException uie ) { // Should return a 404 Not Found - assertEquals(404, uie.getResponse().getStatus()); + assertEquals( 404, uie.getResponse().getStatus() ); } + } + + + /** + * UERGRID-1018 + */ + @Test + public void testRePostOrder() { + + Entity thing1 = new Entity(); + thing1.put( "name", "thing1" ); + + final CollectionEndpoint collection = this.app().collection( "things" ); + + thing1 = collection.post( thing1 ); + + Entity thing2 = new Entity(); + thing2.put( "name", "thing2" ); + thing2 = collection.post( thing2 ); + + Entity thing3 = new Entity(); + thing3.put( "name", "thing3" ); + thing3 = collection.post( thing3 ); + + //now connect them + + //connect thing1 -> thing2 + + final CollectionEndpoint connectionEndpoint = collection.entity( thing1 ).connection( "connectorder" ); + connectionEndpoint.entity( thing2 ).post(); + //connect thing1 -> thing3 + connectionEndpoint.entity( thing3 ).post(); + + refreshIndex(); + + //now do a GET, we should see thing2 then thing3 + + final ApiResponse order1 = connectionEndpoint.get().getResponse(); + + //now verify order + verifyOrder( order1, thing3, thing2 ); + + final QueryParameters queryParameters = new QueryParameters(); + queryParameters.setQuery( "select * order by modified desc" ); + + final ApiResponse order1Query = connectionEndpoint.get( queryParameters ).getResponse(); + + //now verify order + verifyOrder( order1Query, thing3, thing2 ); + + + //now re-post thing 2 it should appear second + connectionEndpoint.entity( thing2 ).post(); + + refreshIndex(); + + + final ApiResponse order2 = connectionEndpoint.get().getResponse(); + + //now verify order + verifyOrder( order2, thing2, thing3 ); + + final ApiResponse order2Query = connectionEndpoint.get( queryParameters ).getResponse(); + + //now verify order + verifyOrder( order2Query, thing3, thing2 ); } + /** + * UERGRID-1018 + */ + @Test + public void testRePutOrder() { + + Entity thing1 = new Entity(); + thing1.put( "name", "thing1" ); + + final CollectionEndpoint collection = this.app().collection( "things" ); + + thing1 = collection.post( thing1 ); + + Entity thing2 = new Entity(); + thing2.put( "name", "thing2" ); + thing2 = collection.post( thing2 ); + + + Entity thing3 = new Entity(); + thing3.put( "name", "thing3" ); + thing3 = collection.post( thing3 ); + + //now connect them + + //connect thing1 -> thing2 + + final CollectionEndpoint connectionEndpoint = collection.entity( thing1 ).connection( "connectorder" ); + connectionEndpoint.entity( thing2 ).put( thing2 ); + + //connect thing1 -> thing3 + connectionEndpoint.entity( thing3 ).put( thing3 ); + + refreshIndex(); + + //now do a GET, we should see thing2 then thing3 + + final ApiResponse order1 = connectionEndpoint.get().getResponse(); + + //now verify order + verifyOrder( order1, thing3, thing2 ); + + final QueryParameters queryParameters = new QueryParameters(); + queryParameters.setQuery( "select * order by modified desc" ); + + final ApiResponse order1Query = connectionEndpoint.get( queryParameters ).getResponse(); + + //now verify order + verifyOrder( order1Query, thing3, thing2 ); + + + //now re-post thing 2 it should appear second + connectionEndpoint.entity( thing2 ).put( thing2 ); + + refreshIndex(); + + final ApiResponse order2 = connectionEndpoint.get().getResponse(); + + + //now verify order + verifyOrder( order2, thing2, thing3 ); + } + + + /** + * Verify our response + */ + private void verifyOrder( final ApiResponse apiResponse, final Entity... verifyOrder ) { + + final List<Entity> responseEntities = apiResponse.getEntities(); + + assertEquals( "Size should be equals", verifyOrder.length, responseEntities.size() ); + + + for ( int i = 0; i < verifyOrder.length; i++ ) { + + final Entity verifyEntity = verifyOrder[i]; + + final Entity returned = responseEntities.get( i ); + + assertEquals( "Should be the same entity", verifyEntity.getUuid(), returned.getUuid() ); + } + } } http://git-wip-us.apache.org/repos/asf/usergrid/blob/82814303/stack/services/src/main/java/org/apache/usergrid/services/AbstractConnectionsService.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/services/AbstractConnectionsService.java b/stack/services/src/main/java/org/apache/usergrid/services/AbstractConnectionsService.java index c4142a0..ee322d2 100644 --- a/stack/services/src/main/java/org/apache/usergrid/services/AbstractConnectionsService.java +++ b/stack/services/src/main/java/org/apache/usergrid/services/AbstractConnectionsService.java @@ -23,20 +23,25 @@ import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.apache.usergrid.persistence.ConnectionRef; import org.apache.usergrid.persistence.Entity; import org.apache.usergrid.persistence.EntityRef; -import org.apache.usergrid.persistence.index.query.Identifier; import org.apache.usergrid.persistence.Query; +import org.apache.usergrid.persistence.Query.Level; import org.apache.usergrid.persistence.Results; import org.apache.usergrid.persistence.Schema; import org.apache.usergrid.persistence.SimpleEntityRef; -import org.apache.usergrid.persistence.Query.Level; +import org.apache.usergrid.persistence.index.query.Identifier; import org.apache.usergrid.services.ServiceParameter.IdParameter; import org.apache.usergrid.services.ServiceParameter.NameParameter; import org.apache.usergrid.services.ServiceParameter.QueryParameter; import org.apache.usergrid.services.ServiceResults.Type; import org.apache.usergrid.services.exceptions.ServiceResourceNotFoundException; + +import rx.Observable; +import rx.schedulers.Schedulers; + import static org.apache.usergrid.services.ServiceParameter.filter; import static org.apache.usergrid.services.ServiceParameter.firstParameterIsName; import static org.apache.usergrid.utils.ClassUtils.cast; @@ -398,6 +403,11 @@ public class AbstractConnectionsService extends AbstractService { String entityType = getEntityType(); item = em.create( id, entityType, context.getPayload().getProperties() ); } + + //create the connection + createConnection( context.getOwner(), context.getCollectionName(), item ); + + return new ServiceResults( this, context, Type.CONNECTION, Results.fromEntity( item ), null, null ); } @@ -426,6 +436,35 @@ public class AbstractConnectionsService extends AbstractService { updateEntities( context, r ); + //create the connection + + //TODO wire the RX scheduler in here and use our parallelism system + + + /** + * Create all the connections for all the entities + */ + final List<Entity> entities = r.getEntities(); + if ( entities != null ) { + + /** + * Save up to 10 connections in parallel + */ + Observable.from(entities).flatMap( emittedEntity -> { + return Observable.just( emittedEntity ).doOnNext( toSave -> { + try { + createConnection( context.getOwner(), context.getCollectionName(), toSave ); + } + catch ( Exception e ) { + throw new RuntimeException( "Unable to save connection", e ); + } + }).subscribeOn( Schedulers.io() ); + }, 10).subscribe(); + + + } + + return new ServiceResults( this, context, Type.CONNECTION, r, null, null ); }