You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2015/03/30 21:58:13 UTC
[09/50] incubator-usergrid git commit: Many fixes to application
delete and restore logic, ApplicationDeleteTests now complete and all passing.
Many fixes to application delete and restore logic, ApplicationDeleteTests now complete and all passing.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/14f45ed1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/14f45ed1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/14f45ed1
Branch: refs/heads/two-dot-o-dev
Commit: 14f45ed1455abea62b534da8662f246dcbb8d6e9
Parents: b7e41f2
Author: Dave Johnson <dm...@apigee.com>
Authored: Fri Mar 6 16:35:52 2015 -0500
Committer: Dave Johnson <dm...@apigee.com>
Committed: Fri Mar 6 16:35:52 2015 -0500
----------------------------------------------------------------------
.../corepersistence/CpEntityManagerFactory.java | 124 +++++------
.../corepersistence/util/CpNamingUtils.java | 3 -
.../persistence/EntityManagerFactory.java | 7 +-
.../rest/applications/ApplicationResource.java | 21 +-
.../exceptions/ConflictExceptionMapper.java | 35 +++
.../applications/ApplicationsResource.java | 34 +--
.../organizations/OrganizationResource.java | 11 +-
.../applications/ApplicationDeleteTest.java | 214 ++++++++++++++++---
.../endpoints/mgmt/ApplicationResource.java | 46 +++-
.../usergrid/management/ManagementService.java | 6 +
.../cassandra/ManagementServiceImpl.java | 110 ++++++++--
.../exceptions/ConflictException.java | 36 ++++
12 files changed, 492 insertions(+), 155 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManagerFactory.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManagerFactory.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManagerFactory.java
index 0144ea8..e1a7c60 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManagerFactory.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManagerFactory.java
@@ -31,6 +31,7 @@ import org.apache.usergrid.corepersistence.rx.AllEntitiesInSystemObservable;
import org.apache.usergrid.corepersistence.util.CpNamingUtils;
import org.apache.usergrid.persistence.*;
+import static org.apache.usergrid.persistence.Schema.PROPERTY_PATH;
import static org.apache.usergrid.persistence.Schema.PROPERTY_NAME;
import static org.apache.usergrid.persistence.Schema.PROPERTY_UUID;
import static org.apache.usergrid.persistence.Schema.TYPE_APPLICATION;
@@ -221,41 +222,20 @@ public class CpEntityManagerFactory implements EntityManagerFactory, Application
public UUID initializeApplication( String organizationName, UUID applicationId, String name,
Map<String, Object> properties ) throws Exception {
-
EntityManager em = getEntityManager( CpNamingUtils.MANAGEMENT_APPLICATION_ID);
final String appName = buildAppName( organizationName, name );
// check for pre-existing application
+
if ( lookupApplication( appName ) != null ) {
throw new ApplicationAlreadyExistsException( appName );
}
- getSetup().setupApplicationKeyspace( applicationId, appName );
-
- UUID orgUuid = lookupOrganization( organizationName );
- if ( orgUuid == null ) {
-
- // create new org because the specified one does not exist
- final String orgName = organizationName;
-
- final Entity orgInfo;
+ // create application info entity in the management app
- try {
- orgInfo = em.create( "organization", new HashMap<String, Object>() {{
- put( PROPERTY_NAME, orgName );
- }} );
- }
- catch ( DuplicateUniquePropertyExistsException e ) {
- throw new OrganizationAlreadyExistsException( orgName );
- }
-
- em.refreshIndex();
- }
-
- // create appinfo entry in the management app
+ getSetup().setupApplicationKeyspace( applicationId, appName );
final UUID appId = applicationId;
- final UUID orgId = orgUuid;
Map<String, Object> appInfoMap = new HashMap<String, Object>() {{
put( PROPERTY_NAME, appName );
put( PROPERTY_UUID, appId );
@@ -268,13 +248,13 @@ public class CpEntityManagerFactory implements EntityManagerFactory, Application
}
em.refreshIndex();
- // create application oldAppInfo
+ // create application info entity
+
if ( properties == null ) {
properties = new TreeMap<String, Object>( CASE_INSENSITIVE_ORDER );
}
properties.put( PROPERTY_NAME, appName );
EntityManager appEm = getEntityManager( applicationId );
-
appEm.create( applicationId, TYPE_APPLICATION, properties );
appEm.resetRoles();
appEm.refreshIndex();
@@ -297,46 +277,76 @@ public class CpEntityManagerFactory implements EntityManagerFactory, Application
@Override
public void deleteApplication(UUID applicationId) throws Exception {
- // remove old application info Entity, which is in the Management App's application infos collection
+ // make a copy of the app to delete application_info entity
+
EntityManager em = getEntityManager( this.getManagementAppId() );
- Entity applicationInfo = em.get(new SimpleEntityRef(CpNamingUtils.APPLICATION_INFO, applicationId));
- em.delete( applicationInfo );
+ Entity appToDelete = em.get(new SimpleEntityRef(CpNamingUtils.APPLICATION_INFO, applicationId));
+
+ // and put it in a deleted_application_info collection
+
+ Entity deletedApp = em.create(
+ applicationId, CpNamingUtils.DELETED_APPLICATION_INFO, appToDelete.getProperties() );
+
+ // copy its connections too
+
+ final Set<String> connectionTypes = em.getConnectionTypes(appToDelete);
+ for ( String connType : connectionTypes ) {
+ final Results results =
+ em.getConnectedEntities(appToDelete, connType, null, Query.Level.ALL_PROPERTIES);
+ for ( Entity entity : results.getEntities() ) {
+ em.createConnection( deletedApp, connType, entity );
+ }
+ }
- // create new Entity in deleted_application_infos collection,
- // with same UUID and properties as deleted application info
- em.create( applicationId, CpNamingUtils.DELETED_APPLICATION_INFO, applicationInfo.getProperties() );
+ // delete the app from the application_info collection and delete its index
+ em.delete( appToDelete);
em.refreshIndex();
- // delete the application's index
- managerCache.getEntityIndex( new ApplicationScopeImpl(new SimpleId(applicationId, TYPE_APPLICATION)));
+ final EntityIndex entityIndex = managerCache.getEntityIndex(
+ new ApplicationScopeImpl(new SimpleId(applicationId, TYPE_APPLICATION)));
+ entityIndex.deleteIndex();
}
@Override
public void restoreApplication(UUID applicationId) throws Exception {
- // remove old delete_appinfos Entity
+ // get the deleted_application_info for the deleted app
+
EntityManager em = getEntityManager(CpNamingUtils.MANAGEMENT_APPLICATION_ID);
- Entity appToRestore = em.get( new SimpleEntityRef( CpNamingUtils.DELETED_APPLICATION_INFO, applicationId ));
+ Entity deletedApp = em.get(
+ new SimpleEntityRef( CpNamingUtils.DELETED_APPLICATION_INFO, applicationId ));
- if ( appToRestore == null ) {
+ if ( deletedApp == null ) {
throw new EntityNotFoundException("Cannot restore. Deleted Application not found: " + applicationId );
}
- em.delete( appToRestore );
+ // create application_info for restored app
- // restore oldAppInfo in appinfo collection
- em.create( appToRestore.getUuid(), CpNamingUtils.APPLICATION_INFO, appToRestore.getProperties());
- em.refreshIndex();
+ Entity restoredApp = em.create(
+ deletedApp.getUuid(), CpNamingUtils.APPLICATION_INFO, deletedApp.getProperties());
+
+ // copy connections from deleted app entity
+
+ final Set<String> connectionTypes = em.getConnectionTypes(deletedApp);
+ for ( String connType : connectionTypes ) {
+ final Results results =
+ em.getConnectedEntities(deletedApp, connType, null, Query.Level.ALL_PROPERTIES);
+ for ( Entity entity : results.getEntities() ) {
+ em.createConnection( restoredApp, connType, entity );
+ }
+ }
+
+ // delete the deleted app entity rebuild the app index
+
+ em.delete( deletedApp );
- // rebuild the apps index
this.rebuildApplicationIndexes(applicationId, new ProgressObserver() {
@Override
public void onProgress(EntityRef entity) {
- logger.info("Restored oldAppInfo {}:{}", entity.getType(), entity.getUuid());
+ logger.debug("Restored oldAppInfo {}:{}", entity.getType(), entity.getUuid());
}
-
});
}
@@ -350,33 +360,11 @@ public class CpEntityManagerFactory implements EntityManagerFactory, Application
}
- public UUID lookupOrganization( String name ) throws Exception {
- init();
-
- // Query q = Query.fromQL(PROPERTY_NAME + " = '" + name + "'");
- EntityManager em = getEntityManager( CpNamingUtils.MANAGEMENT_APPLICATION_ID );
-
- final EntityRef alias = em.getAlias( "organizations", name );
-
- if ( alias == null ) {
- return null;
- }
-
- final Entity entity = em.get( alias );
-
- if ( entity == null ) {
- return null;
- }
-
- return entity.getUuid();
- }
-
-
@Override
public UUID lookupApplication( final String name ) throws Exception {
init();
- // attempt to look up APPLICATION_INFO oldAppInfo by name
+ // attempt to look up APPLICATION_INFO by name
UUID applicationId = null;
EntityManager em = getEntityManager( CpNamingUtils.MANAGEMENT_APPLICATION_ID );
@@ -440,7 +428,7 @@ public class CpEntityManagerFactory implements EntityManagerFactory, Application
@Override
@Metered(group = "core", name = "EntityManagerFactory_getApplication")
public Map<String, UUID> getApplications() throws Exception {
- return getApplications( false );
+ return getApplications(false);
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/core/src/main/java/org/apache/usergrid/corepersistence/util/CpNamingUtils.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/util/CpNamingUtils.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/util/CpNamingUtils.java
index 8f3327d..0adf9d7 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/util/CpNamingUtils.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/util/CpNamingUtils.java
@@ -38,9 +38,6 @@ import org.apache.usergrid.persistence.model.entity.SimpleId;
*/
public class CpNamingUtils {
- // TODO: Why do we have three internal apps? Can we merge this into a single management app?
- // It would make administration much easier and cleaner on the ES side
-
/** Edge types for collection suffix */
public static final String EDGE_COLL_SUFFIX = "zzzcollzzz";
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/core/src/main/java/org/apache/usergrid/persistence/EntityManagerFactory.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/persistence/EntityManagerFactory.java b/stack/core/src/main/java/org/apache/usergrid/persistence/EntityManagerFactory.java
index a077ea0..42caf02 100644
--- a/stack/core/src/main/java/org/apache/usergrid/persistence/EntityManagerFactory.java
+++ b/stack/core/src/main/java/org/apache/usergrid/persistence/EntityManagerFactory.java
@@ -76,6 +76,11 @@ public interface EntityManagerFactory {
*/
public abstract void deleteApplication( UUID applicationId ) throws Exception;
+ /**
+ * Restore deleted application.
+ */
+ public void restoreApplication( UUID applicationId) throws Exception;
+
public abstract UUID importApplication( String organization, UUID applicationId, String name,
Map<String, Object> properties ) throws Exception;
@@ -169,8 +174,6 @@ public interface EntityManagerFactory {
public Health getEntityStoreHealth();
- void restoreApplication(UUID applicationId) throws Exception;
-
public interface ProgressObserver {
public void onProgress( EntityRef entity);
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/main/java/org/apache/usergrid/rest/applications/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/applications/ApplicationResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/applications/ApplicationResource.java
index 4f8002b..e62a9cb 100644
--- a/stack/rest/src/main/java/org/apache/usergrid/rest/applications/ApplicationResource.java
+++ b/stack/rest/src/main/java/org/apache/usergrid/rest/applications/ApplicationResource.java
@@ -30,6 +30,7 @@ import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import com.sun.jersey.api.ConflictException;
import org.apache.usergrid.persistence.exceptions.EntityNotFoundException;
import org.apache.usergrid.rest.ApiResponse;
import org.apache.usergrid.rest.security.annotations.RequireOrganizationAccess;
@@ -488,6 +489,9 @@ public class ApplicationResource extends ServiceResource {
}
+ /**
+ * Put on application URL will restore application if it was deleted.
+ */
@PUT
@RequireOrganizationAccess
@Override
@@ -498,12 +502,7 @@ public class ApplicationResource extends ServiceResource {
throw new IllegalArgumentException("Application ID not specified in request");
}
- ApplicationInfo app = management.getApplicationInfo( applicationId );
- if ( app == null ) {
- throw new EntityNotFoundException("Application ID " + applicationId + " not found");
- }
-
- emf.restoreApplication( applicationId );
+ management.restoreApplication( applicationId );
ApiResponse response = createApiResponse();
response.setAction( "restore" );
@@ -532,15 +531,9 @@ public class ApplicationResource extends ServiceResource {
throw new IllegalArgumentException("Application ID not specified in request");
}
- ApplicationInfo app = management.getApplicationInfo( applicationId );
- if ( app == null ) {
- throw new EntityNotFoundException("Application ID " + applicationId + " not found");
- }
-
- emf.deleteApplication( applicationId );
+ management.deleteApplication( applicationId );
- LOG.debug( "ApplicationResource.delete() deleted appId = {} appName = {}",
- applicationId, app.getName() );
+ LOG.debug( "ApplicationResource.delete() deleted appId = {}", applicationId);
ApiResponse response = createApiResponse();
response.setAction( "delete" );
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/main/java/org/apache/usergrid/rest/exceptions/ConflictExceptionMapper.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/exceptions/ConflictExceptionMapper.java b/stack/rest/src/main/java/org/apache/usergrid/rest/exceptions/ConflictExceptionMapper.java
new file mode 100644
index 0000000..34a813d
--- /dev/null
+++ b/stack/rest/src/main/java/org/apache/usergrid/rest/exceptions/ConflictExceptionMapper.java
@@ -0,0 +1,35 @@
+/*
+ * 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.exceptions;
+
+
+import org.apache.usergrid.management.exceptions.ConflictException;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import static javax.ws.rs.core.Response.Status.CONFLICT;
+
+
+@Provider
+public class ConflictExceptionMapper extends AbstractExceptionMapper<ConflictException> {
+
+ @Override
+ public Response toResponse( ConflictException e ) {
+ return toResponse( CONFLICT, e );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/applications/ApplicationsResource.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/applications/ApplicationsResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/applications/ApplicationsResource.java
index 1fe9745..5691e2e 100644
--- a/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/applications/ApplicationsResource.java
+++ b/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/applications/ApplicationsResource.java
@@ -132,31 +132,31 @@ public class ApplicationsResource extends AbstractContextResource {
@RequireOrganizationAccess
@Path(RootResource.APPLICATION_ID_PATH)
- public ApplicationResource applicationFromOrganizationByApplicationId( @Context UriInfo ui,
- @PathParam( "applicationId" )
- String applicationIdStr ) throws Exception {
+ public ApplicationResource applicationFromOrganizationByApplicationId(
+ @Context UriInfo ui, @PathParam( "applicationId" ) String applicationIdStr ) throws Exception {
- return getSubResource( ApplicationResource.class ).init( organization, UUID.fromString( applicationIdStr ) );
+ return getSubResource( ApplicationResource.class )
+ .init( organization, UUID.fromString( applicationIdStr ) );
}
-// @RequireOrganizationAccess
-// @Path(RootResource.APPLICATION_ID_PATH)
-// @PUT
-// public ApplicationResource restoreApplicationFromOrganizationByApplicationId(
-// @Context UriInfo ui,
-// @PathParam( "applicationId" )
-// String applicationIdStr ) throws Exception {
-//
-// return getSubResource( ApplicationResource.class ).init( organization, UUID.fromString( applicationIdStr ) );
-// }
+ @RequireOrganizationAccess
+ @Path(RootResource.APPLICATION_ID_PATH)
+ @PUT
+ public ApplicationResource restoreApplicationFromOrganizationByApplicationId(
+ @Context UriInfo ui,
+ @PathParam( "applicationId" )
+ String applicationIdStr ) throws Exception {
+
+ return getSubResource( ApplicationResource.class )
+ .init( organization, UUID.fromString( applicationIdStr ) );
+ }
@RequireOrganizationAccess
@Path( "{applicationName}" )
- public ApplicationResource applicationFromOrganizationByApplicationName( @Context UriInfo ui,
- @PathParam( "applicationName" )
- String applicationName ) throws Exception {
+ public ApplicationResource applicationFromOrganizationByApplicationName(
+ @Context UriInfo ui, @PathParam( "applicationName" ) String applicationName ) throws Exception {
String appName =
applicationName.contains( "/" ) ? applicationName : organization.getName() + "/" + applicationName;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/main/java/org/apache/usergrid/rest/organizations/OrganizationResource.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/organizations/OrganizationResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/organizations/OrganizationResource.java
index 8aee185..695becc 100644
--- a/stack/rest/src/main/java/org/apache/usergrid/rest/organizations/OrganizationResource.java
+++ b/stack/rest/src/main/java/org/apache/usergrid/rest/organizations/OrganizationResource.java
@@ -100,9 +100,14 @@ public class OrganizationResource extends AbstractContextResource {
if (organizationId == null) {
return null;
}
- BiMap<UUID, String> apps = management.getApplicationsForOrganization( organizationId );
- if ( apps.get( applicationId ) == null ) {
- return null;
+
+ // don't look up app if request is a PUT because a PUT can be used to restore a deleted app
+ if ( !hc.getRequest().getMethod().equalsIgnoreCase("PUT") ) {
+
+ BiMap<UUID, String> apps = management.getApplicationsForOrganization(organizationId);
+ if (apps.get(applicationId) == null) {
+ return null;
+ }
}
return appResourceFor( applicationId );
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/test/java/org/apache/usergrid/rest/applications/ApplicationDeleteTest.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/ApplicationDeleteTest.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/ApplicationDeleteTest.java
index 8ac449a..1826d59 100644
--- a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/ApplicationDeleteTest.java
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/ApplicationDeleteTest.java
@@ -36,6 +36,16 @@ public class ApplicationDeleteTest extends AbstractRestIT {
private static final Logger logger = LoggerFactory.getLogger(ApplicationDeleteTest.class);
+ /**
+ * Test most common use cases.
+ * <pre>
+ * - create app with collection of things
+ * - delete the app
+ * - test that attempts to get the app, its collections and entities throw 400 with message
+ * - test that we cannot delete the app a second time
+ * - test that we can create a new app with the same name as the deleted app
+ * </pre>
+ */
@Test
public void testBasicOperation() throws Exception {
@@ -45,30 +55,9 @@ public class ApplicationDeleteTest extends AbstractRestIT {
String appToDeleteName = clientSetup.getAppName() + "_appToDelete";
Token orgAdminToken = getAdminToken( clientSetup.getUsername(), clientSetup.getUsername());
- ApiResponse appCreateResponse = clientSetup.getRestClient()
- .management().orgs().organization( orgName ).app().getResource()
- .queryParam( "access_token", orgAdminToken.getAccessToken() )
- .type( MediaType.APPLICATION_JSON )
- .post( ApiResponse.class, new Application( appToDeleteName ) );
- UUID appToDeleteId = appCreateResponse.getEntities().get(0).getUuid();
-
List<Entity> entities = new ArrayList<>();
- for ( int i=0; i<10; i++ ) {
- final String entityName = "entity" + i;
- Entity entity = new Entity();
- entity.setProperties(new HashMap<String, Object>() {{
- put("name", entityName );
- }});
-
- ApiResponse createResponse = clientSetup.getRestClient()
- .org(orgName).app( appToDeleteName ).collection("things").getResource()
- .queryParam("access_token", orgAdminToken.getAccessToken())
- .type(MediaType.APPLICATION_JSON)
- .post( ApiResponse.class, entity );
-
- entities.add( createResponse.getEntities().get(0) );
- }
+ UUID appToDeleteId = createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
// delete the app
@@ -130,7 +119,8 @@ public class ApplicationDeleteTest extends AbstractRestIT {
Assert.assertEquals("organization_application_not_found", node.get("error").textValue());
}
- // test that we cannot see the application in the list of applications
+ // test that we cannot see the application in the list of applications returned
+ // by the management resource's get organization's applications end-point
refreshIndex();
@@ -171,33 +161,195 @@ public class ApplicationDeleteTest extends AbstractRestIT {
}
+ /**
+ * Test restore of deleted app.
+ * <pre>
+ * - create app with collection of things
+ * - delete the app
+ * - restore the app
+ * - test that we can get the app, its collections and an entity
+ * </pre>
+ */
@Test
public void testAppRestore() throws Exception {
- // create and delete app
+ // create app with a collection of "things"
+
+ String orgName = clientSetup.getOrganization().getName();
+ String appToDeleteName = clientSetup.getAppName() + "_appToDelete";
+ Token orgAdminToken = getAdminToken( clientSetup.getUsername(), clientSetup.getUsername());
+
+ List<Entity> entities = new ArrayList<>();
+
+ UUID appToDeleteId = createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
+
+ // delete the app
+
+ clientSetup.getRestClient()
+ .org(orgName).app(appToDeleteId.toString() ).getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken() )
+ .delete();
// restore the app
- // test that app appears in list of apps
+ clientSetup.getRestClient()
+ .org(orgName).app(appToDeleteId.toString() ).getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken() )
+ .put();
+
- // test that application's collection exists
- }
+ // test that we can see the application in the list of applications
+
+ ManagementResponse orgAppResponse = clientSetup.getRestClient()
+ .management().orgs().organization( orgName ).apps().getOrganizationApplications();
+
+ boolean found = false;
+ for ( String appName : orgAppResponse.getData().keySet() ) {
+ if ( orgAppResponse.getData().get( appName ).equals( appToDeleteId.toString() )) {
+ found = true;
+ break;
+ }
+ }
+
+ Assert.assertTrue( found );
+ // test that we can get an app entity
+ UUID entityId = entities.get(0).getUuid();
+ ApiResponse entityResponse = clientSetup.getRestClient()
+ .org(orgName).app(appToDeleteName).collection("things").entity( entityId ).getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken())
+ .type(MediaType.APPLICATION_JSON)
+ .get(ApiResponse.class);
+ Assert.assertEquals( entityId, entityResponse.getEntities().get(0).getUuid() );
+ // test that we can get deleted app's collection
+
+ ApiResponse collectionReponse = clientSetup.getRestClient()
+ .org(orgName).app(appToDeleteName).collection("things").getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken())
+ .type(MediaType.APPLICATION_JSON)
+ .get(ApiResponse.class);
+ Assert.assertEquals( entities.size(), collectionReponse.getEntityCount() );
+ }
+
+
+ /**
+ * Test that we cannot restore deleted app with same name as
+ */
@Test
public void testAppRestoreConflict() throws Exception {
- // create and delete app
+ // create app with a collection of "things"
+
+ String orgName = clientSetup.getOrganization().getName();
+ String appToDeleteName = clientSetup.getAppName() + "_appToDelete";
+ Token orgAdminToken = getAdminToken( clientSetup.getUsername(), clientSetup.getUsername());
+
+ List<Entity> entities = new ArrayList<>();
+
+ UUID appToDeleteId = createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
+
+ // delete the app
+
+ clientSetup.getRestClient()
+ .org( orgName ).app(appToDeleteId.toString() ).getResource()
+ .queryParam( "access_token", orgAdminToken.getAccessToken() )
+ .delete();
+
+ // create new app with same name
+
+ createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
+
+ // attempt to restore original app, should get 409
+
+ try {
+
+ clientSetup.getRestClient()
+ .org(orgName).app(appToDeleteId.toString()).getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken())
+ .put();
+
+ Assert.fail("Must fail to restore app with same name as existing app");
+
+ } catch ( UniformInterfaceException e ) {
+ Assert.assertEquals(409, e.getResponse().getStatus());
+ }
+ }
+
+
+ /**
+ * Test that we cannot delete an app with same name as an app that is already deleted.
+ * TODO: investigate way to support this, there should be no such restriction.
+ */
+ @Test
+ public void testAppDeleteConflict() throws Exception {
+
+ // create app with a collection of "things"
+
+ String orgName = clientSetup.getOrganization().getName();
+ String appToDeleteName = clientSetup.getAppName() + "_appToDelete";
+ Token orgAdminToken = getAdminToken( clientSetup.getUsername(), clientSetup.getUsername());
+
+ List<Entity> entities = new ArrayList<>();
+
+ UUID appToDeleteId = createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
+
+ // delete the app
+
+ clientSetup.getRestClient()
+ .org( orgName ).app(appToDeleteId.toString() ).getResource()
+ .queryParam( "access_token", orgAdminToken.getAccessToken() )
+ .delete();
// create new app with same name
- // attempt to restore original app
+ UUID newAppId = createAppWithCollection(orgName, appToDeleteName, orgAdminToken, entities);
+
+ // attempt to delete new app, it should fail
+
+ try {
+
+ clientSetup.getRestClient()
+ .org(orgName).app( newAppId.toString() ).getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken())
+ .delete();
+
+ Assert.fail("Must fail to delete app with same name as deleted app");
+
+ } catch ( UniformInterfaceException e ) {
+ Assert.assertEquals( 409, e.getResponse().getStatus() );
+ }
+ }
+
+
+ private UUID createAppWithCollection(
+ String orgName, String appName, Token orgAdminToken, List<Entity> entities) {
+
+ ApiResponse appCreateResponse = clientSetup.getRestClient()
+ .management().orgs().organization( orgName ).app().getResource()
+ .queryParam( "access_token", orgAdminToken.getAccessToken() )
+ .type( MediaType.APPLICATION_JSON )
+ .post( ApiResponse.class, new Application( appName ) );
+ UUID appId = appCreateResponse.getEntities().get(0).getUuid();
+
+ for ( int i=0; i<10; i++ ) {
- // test that HTTP 409 CONFLICT and informative error message is received
+ final String entityName = "entity" + i;
+ Entity entity = new Entity();
+ entity.setProperties(new HashMap<String, Object>() {{
+ put("name", entityName );
+ }});
- // create a collection with two entities
+ ApiResponse createResponse = clientSetup.getRestClient()
+ .org(orgName).app( appName ).collection("things").getResource()
+ .queryParam("access_token", orgAdminToken.getAccessToken())
+ .type(MediaType.APPLICATION_JSON)
+ .post( ApiResponse.class, entity );
+ entities.add( createResponse.getEntities().get(0) );
+ }
+ return appId;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/rest/src/test/java/org/apache/usergrid/rest/test/resource2point0/endpoints/mgmt/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/test/resource2point0/endpoints/mgmt/ApplicationResource.java b/stack/rest/src/test/java/org/apache/usergrid/rest/test/resource2point0/endpoints/mgmt/ApplicationResource.java
index 6b7742f..034c302 100644
--- a/stack/rest/src/test/java/org/apache/usergrid/rest/test/resource2point0/endpoints/mgmt/ApplicationResource.java
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/test/resource2point0/endpoints/mgmt/ApplicationResource.java
@@ -22,17 +22,28 @@ package org.apache.usergrid.rest.test.resource2point0.endpoints.mgmt;
import javax.ws.rs.core.MediaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.usergrid.rest.test.resource2point0.endpoints.NamedResource;
import org.apache.usergrid.rest.test.resource2point0.endpoints.UrlResource;
import org.apache.usergrid.rest.test.resource2point0.model.Application;
import org.apache.usergrid.rest.test.resource2point0.model.*;
import org.apache.usergrid.rest.test.resource2point0.state.ClientContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.StringReader;
/**
* Classy class class.
*/
public class ApplicationResource extends NamedResource {
+
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationResource.class);
+
+ ObjectMapper mapper = new ObjectMapper();
+
public ApplicationResource(ClientContext context, UrlResource parent) {
super("applications", context, parent);
}
@@ -51,16 +62,41 @@ public class ApplicationResource extends NamedResource {
.accept(MediaType.APPLICATION_JSON).post(application);
}
- public Entity post(Entity payload){
- ApiResponse response = getResource(true).type( MediaType.APPLICATION_JSON_TYPE ).accept(MediaType.APPLICATION_JSON)
- .post(ApiResponse.class, payload);
+ public Entity post(Entity payload) {
+
+ String responseString = getResource(true)
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .accept(MediaType.APPLICATION_JSON)
+ .post(String.class, payload);
+
+ logger.debug("Response from post: " + responseString);
+
+ ApiResponse response;
+ try {
+ response = mapper.readValue(new StringReader(responseString), ApiResponse.class);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing response", e);
+ }
+
return new Entity(response);
}
public Entity get() {
- ApiResponse response = getResource(true).type(MediaType.APPLICATION_JSON_TYPE ).accept(MediaType.APPLICATION_JSON)
- .get(ApiResponse.class);
+
+ String responseString = getResource(true)
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .accept(MediaType.APPLICATION_JSON)
+ .get(String.class);
+
+ logger.debug("Response from post: " + responseString);
+
+ ApiResponse response;
+ try {
+ response = mapper.readValue(new StringReader(responseString), ApiResponse.class);
+ } catch (IOException e) {
+ throw new RuntimeException("Error parsing response", e);
+ }
return new Entity(response);
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java b/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
index 97d23c2..aff9e15 100644
--- a/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
+++ b/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
@@ -149,6 +149,8 @@ public interface ManagementService {
public ApplicationInfo getApplicationInfo( UUID applicationId ) throws Exception;
+ ApplicationInfo getDeletedApplicationInfo(UUID applicationId) throws Exception;
+
public ApplicationInfo getApplicationInfo( Identifier id ) throws Exception;
public ApplicationInfo getApplicationInfoFromAccessToken( String token ) throws Exception;
@@ -318,4 +320,8 @@ public interface ManagementService {
/** For testing purposes only */
public Properties getProperties();
+
+ public void deleteApplication(UUID applicationId) throws Exception;
+
+ public ApplicationInfo restoreApplication(UUID applicationId) throws Exception;
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java b/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
index d8fc0f5..a33c324 100644
--- a/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
+++ b/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
@@ -29,7 +29,10 @@ import java.util.Set;
import java.util.UUID;
import org.apache.usergrid.corepersistence.util.CpNamingUtils;
+import org.apache.usergrid.management.exceptions.*;
+import org.apache.usergrid.persistence.core.scope.ApplicationScopeImpl;
import org.apache.usergrid.persistence.index.query.Query;
+import org.apache.usergrid.persistence.model.entity.SimpleId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,15 +48,6 @@ import org.apache.usergrid.management.ManagementService;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
import org.apache.usergrid.management.UserInfo;
-import org.apache.usergrid.management.exceptions.DisabledAdminUserException;
-import org.apache.usergrid.management.exceptions.DisabledAppUserException;
-import org.apache.usergrid.management.exceptions.IncorrectPasswordException;
-import org.apache.usergrid.management.exceptions.ManagementException;
-import org.apache.usergrid.management.exceptions.RecentlyUsedPasswordException;
-import org.apache.usergrid.management.exceptions.UnableToLeaveOrganizationException;
-import org.apache.usergrid.management.exceptions.UnactivatedAdminUserException;
-import org.apache.usergrid.management.exceptions.UnactivatedAppUserException;
-import org.apache.usergrid.management.exceptions.UnconfirmedAdminUserException;
import org.apache.usergrid.persistence.CredentialsInfo;
import org.apache.usergrid.persistence.Entity;
import org.apache.usergrid.persistence.EntityManager;
@@ -250,8 +244,7 @@ public class ManagementServiceImpl implements ManagementService {
return properties.properties;
}
-
- @Autowired
+ @Autowired
public void setTokenService( TokenService tokens ) {
this.tokens = tokens;
}
@@ -1662,7 +1655,7 @@ public class ManagementServiceImpl implements ManagementService {
}
catch ( UnavailableSecurityManagerException e ) {
// occurs in the rare case that this is called before the full stack is initialized
- logger.warn("Error getting user, organization created activity will not be created", e);
+ logger.warn("Error getting user, application created activity will not be created", e);
}
if ( ( user != null ) && user.isAdminUser() ) {
postOrganizationActivity( organizationId, user, "create", appInfo, "Application", applicationName,
@@ -1677,6 +1670,82 @@ public class ManagementServiceImpl implements ManagementService {
@Override
+ public void deleteApplication(UUID applicationId) throws Exception {
+
+ // make sure there is not already a delete app with the same name
+
+ EntityManager em = emf.getEntityManager(CpNamingUtils.MANAGEMENT_APPLICATION_ID);
+ Entity appToDelete = em.get(
+ new SimpleEntityRef( CpNamingUtils.APPLICATION_INFO, applicationId ));
+
+ final EntityRef alias = em.getAlias(
+ CpNamingUtils.DELETED_APPLICATION_INFO, appToDelete.getName() );
+
+ if ( alias != null ) {
+ throw new ConflictException("Cannot delete app with same name as already deleted app");
+ }
+
+ emf.deleteApplication( applicationId );
+ }
+
+
+ @Override
+ public ApplicationInfo restoreApplication(UUID applicationId) throws Exception {
+
+ ApplicationInfo app = getDeletedApplicationInfo( applicationId );
+ if ( app == null ) {
+ throw new EntityNotFoundException("Deleted application ID " + applicationId + " not found");
+ }
+
+ if ( emf.lookupApplication( app.getName() ) != null ) {
+ throw new ConflictException("Cannot restore application, one with that name already exists.");
+ }
+
+ // restore application_info entity
+
+ EntityManager em = emf.getEntityManager( CpNamingUtils.MANAGEMENT_APPLICATION_ID );
+ emf.restoreApplication(applicationId);
+
+ // restore token
+
+ Entity appInfo = em.get(
+ new SimpleEntityRef( CpNamingUtils.APPLICATION_INFO, applicationId ));
+
+ writeUserToken( smf.getManagementAppId(), appInfo,
+ encryptionService.plainTextCredentials(
+ generateOAuthSecretKey( AuthPrincipalType.APPLICATION ),
+ null,
+ smf.getManagementAppId() ) );
+
+ String orgName = appInfo.getName().split("/")[0];
+ EntityRef alias = em.getAlias( Group.ENTITY_TYPE, orgName );
+ Entity orgEntity = em.get( alias );
+
+ addApplicationToOrganization( orgEntity.getUuid(), applicationId, appInfo );
+
+ // create activity
+
+ UserInfo user = null;
+ try {
+ user = SubjectUtils.getUser();
+ }
+ catch ( UnavailableSecurityManagerException e ) {
+ // occurs in the rare case that this is called before the full stack is initialized
+ logger.warn("Error getting user, application restored created activity will not be created", e);
+ }
+ if ( ( user != null ) && user.isAdminUser() ) {
+ postOrganizationActivity( orgEntity.getUuid(), user, "restore", appInfo, "Application", appInfo.getName(),
+ "<a href=\"mailto:" + user.getEmail() + "\">" + user.getName() + " (" + user.getEmail()
+ + ")</a> restored an application named " + appInfo.getName(), null );
+ }
+
+ em.refreshIndex();
+
+ return new ApplicationInfo( applicationId, appInfo.getName() );
+ }
+
+
+ @Override
public OrganizationInfo getOrganizationForApplication( UUID applicationInfoId ) throws Exception {
if ( applicationInfoId == null ) {
@@ -1804,6 +1873,22 @@ public class ManagementServiceImpl implements ManagementService {
@Override
+ public ApplicationInfo getDeletedApplicationInfo(UUID applicationId) throws Exception {
+ if ( applicationId == null ) {
+ return null;
+ }
+ EntityManager em = emf.getEntityManager( smf.getManagementAppId() );
+ Entity entity = em.get( new SimpleEntityRef(
+ CpNamingUtils.DELETED_APPLICATION_INFO, applicationId ));
+
+ if ( entity != null ) {
+ return new ApplicationInfo( applicationId, entity.getName() );
+ }
+ return null;
+ }
+
+
+ @Override
public ApplicationInfo getApplicationInfo( Identifier id ) throws Exception {
if ( id == null ) {
return null;
@@ -2979,4 +3064,5 @@ public class ManagementServiceImpl implements ManagementService {
else
return Boolean.parseBoolean(obj);
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/14f45ed1/stack/services/src/main/java/org/apache/usergrid/management/exceptions/ConflictException.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/management/exceptions/ConflictException.java b/stack/services/src/main/java/org/apache/usergrid/management/exceptions/ConflictException.java
new file mode 100644
index 0000000..2ec14c8
--- /dev/null
+++ b/stack/services/src/main/java/org/apache/usergrid/management/exceptions/ConflictException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 The Apache Software Foundation.
+ *
+ * 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 org.apache.usergrid.management.exceptions;
+
+public class ConflictException extends ManagementException {
+
+ public ConflictException() {
+ super();
+ }
+
+ public ConflictException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+ public ConflictException(String arg0) {
+ super(arg0);
+ }
+
+ public ConflictException(Throwable arg0) {
+ super(arg0);
+ }
+}