blob: 1b3ee65b87c0220a0b67dd29ccb6d23a0e9243e7 [file] [log] [blame]
/*
* 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;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.WebAppDescriptor;
import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.usergrid.java.client.Client;
import org.apache.usergrid.management.ApplicationInfo;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
import org.apache.usergrid.persistence.exceptions.ApplicationAlreadyExistsException;
import org.junit.AfterClass;
import org.junit.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static org.apache.usergrid.utils.JsonUtils.mapToFormattedJsonString;
import static org.apache.usergrid.utils.MapUtils.hashMap;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Base class for testing Usergrid Jersey-based REST API. Implementations should model the
* paths mapped, not the method names. For example, to test the the "password" mapping on
* applications.users.UserResource for a PUT method, the test method(s) should following the
* following naming convention: test_[HTTP verb]_[action mapping]_[ok|fail][_[specific
* failure condition if multiple]
*/
//@ArquillianSuiteDeployment
//@RunWith(Arquillian.class)
public abstract class AbstractRestIT extends JerseyTest {
private static final Logger LOG = LoggerFactory.getLogger( AbstractRestIT.class );
private static boolean usersSetup = false;
protected static TomcatRuntime tomcatRuntime = TomcatRuntime.getInstance();
protected static final ITSetup setup = ITSetup.getInstance();
private static ClientConfig clientConfig = new DefaultClientConfig();
protected static String access_token;
protected static String adminAccessToken;
protected static Client client;
protected static final AppDescriptor descriptor;
//private static final URI baseURI = setup.getBaseURI();
protected ObjectMapper mapper = new ObjectMapper();
public AbstractRestIT() {
super( descriptor );
}
static {
clientConfig.getFeatures().put( JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE );
descriptor = new WebAppDescriptor.Builder( "org.apache.usergrid.rest" )
.clientConfig( clientConfig ).build();
dumpClasspath( AbstractRestIT.class.getClassLoader() );
}
// // We set testable = false so we deploy the archive to the server and test it locally
// @org.jboss.arquillian.container.test.api.Deployment( testable = false )
// public static WebArchive createTestArchive() {
//
// // we use the MavenImporter from shrinkwrap to just produce whatever maven would build then test with it
//
// // set maven to be in offline mode
//
// System.setProperty( "org.apache.maven.offline", "true" );
// return ShrinkWrap.create(MavenImporter.class)
// .loadPomFromFile( "pom.xml", "arquillian-tomcat" )
// .importBuildOutput()
// .as( WebArchive.class );
// }
@AfterClass
public static void teardown() {
access_token = null;
usersSetup = false;
adminAccessToken = null;
}
public ApplicationInfo appInfo = null;
public OrganizationInfo orgInfo = null;
public String orgAppPath = null;
public String username = null;
public String userEmail = null;
/** Quick fix to get old style test working again. We need them! */
@Before
public void setupOrgApp() throws Exception {
setup.getMgmtSvc().setup();
String rand = RandomStringUtils.randomAlphanumeric(5);
orgInfo = setup.getMgmtSvc().getOrganizationByName("test-organization");
if ( orgInfo == null ) {
OrganizationOwnerInfo orgOwnerInfo = setup.getMgmtSvc().createOwnerAndOrganization(
"test-organization" + rand, "test", "test", "test@usergrid.org", rand, false, false);
orgInfo = orgOwnerInfo.getOrganization();
}
String appname = "app-" + rand;
try {
appInfo = setup.getMgmtSvc().createApplication(orgInfo.getUuid(),appname);
}catch(ApplicationAlreadyExistsException e){
LOG.error("Failed to create application"+appname+", maybe this is ok", e);
}
refreshIndex( orgInfo.getName(), appInfo.getName() );
orgAppPath = appInfo.getName() + "/";
adminToken();
setupUsers();
refreshIndex( orgInfo.getName(), appInfo.getName() );
loginClient();
refreshIndex( orgInfo.getName(), appInfo.getName() );
LOG.info( "acquiring token" );
access_token = userToken( userEmail, "sesame" );
LOG.info( "with token: {}", access_token );
}
public static void dumpClasspath( ClassLoader loader ) {
System.out.println( "Classloader " + loader + ":" );
if ( loader instanceof URLClassLoader ) {
URLClassLoader ucl = ( URLClassLoader ) loader;
System.out.println( "\t" + Arrays.toString( ucl.getURLs() ) );
}
else {
System.out.println( "\t(cannot display components as not a URLClassLoader)" );
}
if ( loader.getParent() != null ) {
dumpClasspath( loader.getParent() );
}
}
protected void setupUsers() throws Exception {
LOG.info("Entering setupUsers");
// if ( usersSetup ) {
// LOG.info("Leaving setupUsers: already setup");
// return;
// }
String rand = RandomStringUtils.randomAlphanumeric(5);
username = "user-" + rand;
userEmail = username + "@example.com";
createUser( username, userEmail, "sesame", "User named " + rand);
//usersSetup = true;
LOG.info("Leaving setupUsers, setup user: " + userEmail );
}
public void loginClient() throws Exception {
String appNameOnly = appInfo.getName().split("/")[1];
client = new Client( "test-organization", appNameOnly ).withApiUrl(
UriBuilder.fromUri( "http://localhost/" ).port(tomcatRuntime.getPort() ).build().toString() );
org.apache.usergrid.java.client.response.ApiResponse response =
client.authorizeAppUser( userEmail, "sesame" );
assertTrue( response != null && response.getError() == null );
}
@Override
protected TestContainerFactory getTestContainerFactory() {
// return new
// com.sun.jersey.test.framework.spi.container.grizzly2.web.GrizzlyWebTestContainerFactory();
return new com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory();
}
@Override
protected URI getBaseURI() {
try {
return new URI("http://localhost:" + tomcatRuntime.getPort());
} catch (URISyntaxException e) {
throw new RuntimeException("Error determining baseURI", e);
}
}
public static void logNode( JsonNode node ) {
if ( LOG.isInfoEnabled() ) // - protect against unnecessary call to formatter
{
LOG.info("Node: " + mapToFormattedJsonString( node ) );
}
}
protected String userToken( String name, String password ) throws Exception {
try {
JsonNode node = mapper.readTree( resource().path( orgAppPath + "token" )
.queryParam("grant_type", "password")
.queryParam( "username", name )
.queryParam( "password", password ).accept( MediaType.APPLICATION_JSON )
.get( String.class ));
String userToken = node.get( "access_token" ).textValue();
LOG.info( "returning user token: {}", userToken );
return userToken;
} catch ( Exception e ) {
LOG.debug("Error getting user token", e);
throw e;
}
}
public void createUser( String username, String email, String password, String name ) {
try {
JsonNode node = mapper.readTree( resource().path( orgAppPath + "token" )
.queryParam( "grant_type", "password" )
.queryParam( "username", username )
.queryParam( "password", password )
.accept( MediaType.APPLICATION_JSON )
.get( String.class ));
if ( getError( node ) == null ) {
return;
}
}
catch ( Exception ex ) {
LOG.error( "Miss on user. Creating." );
}
adminToken();
Map<String, Object> payload = (Map<String, Object>)
hashMap( "email", (Object)email )
.map( "username", username )
.map( "name", name )
.map( "password", password )
.map( "pin", "1234" );
resource().path( orgAppPath + "users" )
.queryParam( "access_token", adminAccessToken )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.post( payload );
}
public void setUserPassword( String username, String password ) throws IOException {
Map<String, String> data = new HashMap<String, String>();
data.put( "newpassword", password );
adminToken();
// change the password as admin. The old password isn't required
JsonNode node = mapper.readTree(
resource().path( String.format( orgAppPath + "users/%s/password", username ) )
.queryParam("access_token", adminAccessToken)
.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON_TYPE)
.post(String.class, data));
}
/** Acquire the management token for the test@usergrid.com user with the default password */
protected String adminToken() {
adminAccessToken = mgmtToken( "test@usergrid.com", "test" );
return adminAccessToken;
}
/** Get the super user's access token */
protected String superAdminToken() {
return mgmtToken( "superuser", "test" );
}
/** Acquire the management token for the test@usergrid.com user with the given password */
protected String mgmtToken( String user, String password ) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node;
try {
node = mapper.readTree( resource().path( "/management/token" )
.queryParam( "grant_type", "password" )
.queryParam( "username", user )
.queryParam( "password", password )
.accept( MediaType.APPLICATION_JSON )
.get( String.class ));
} catch (IOException ex) {
throw new RuntimeException("Unable to parse response", ex);
}
String mgmToken = node.get( "access_token" ).textValue();
LOG.info( "got mgmt token: {}", mgmToken );
return mgmToken;
}
/** Get the entity from the entity array in the response */
protected JsonNode getEntity( JsonNode response, int index ) {
if ( response == null ) {
return null;
}
JsonNode entities = response.get( "entities" );
if ( entities == null ) {
return null;
}
int size = entities.size();
if ( size <= index ) {
return null;
}
return entities.get( index );
}
/** Get the entity from the entity array in the response */
protected JsonNode getEntity( JsonNode response, String name ) {
return response.get( "entities" ).get( name );
}
/** Get the uuid from the entity at the specified index */
protected UUID getEntityId( JsonNode response, int index ) {
return UUID.fromString( getEntity( response, index ).get( "uuid" ).asText() );
}
/**
* Get the property "name" from the entity at the specified index
* @param response
* @param index
* @return
*/
protected String getEntityName(JsonNode response, int index){
return getEntity(response, index).get( "name" ).asText();
}
/** Get the error response */
protected JsonNode getError( JsonNode response ) {
return response.get( "error" );
}
/** convenience to return a ready WebResource.Builder in a single call */
protected WebResource.Builder appPath( String path ) {
return resource().path( orgAppPath + "" + path )
.queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON_TYPE );
}
/** convenience to return a ready WebResource.Builder in a single call */
protected WebResource.Builder path( String path ) {
return resource().path( path )
.queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON_TYPE );
}
/** Sets a management service property locally and remotely. */
public void setTestProperty( String key, String value ) {
// set the value locally (in the Usergrid instance here in the JUnit classloader
setup.getMgmtSvc().getProperties().setProperty( key, value );
// set the value remotely (in the Usergrid instance running in Tomcat classloader)
Map<String, String> props = new HashMap<String, String>();
props.put( key, value );
resource().path( "/testproperties" ).queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON_TYPE ).post( props );
}
/** Set a management service properties locally and remotely. */
public void setTestProperties( Map<String, String> props ) {
// set the values locally (in the Usergrid instance here in the JUnit classloader
for ( String key : props.keySet() ) {
setup.getMgmtSvc().getProperties().setProperty( key, props.get( key ) );
}
// set the values remotely (in the Usergrid instance running in Tomcat classloader)
resource().path( "/testproperties" ).queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON_TYPE ).post( props );
}
/** Get all management service properties from the Tomcat instance of the service. */
public Map<String, String> getRemoteTestProperties() {
return resource().path( "/testproperties" ).queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON_TYPE ).get( Map.class );
}
protected void refreshIndex( UUID appId ) {
LOG.debug("Refreshing index for appId {}", appId );
try {
resource().path( "/refreshindex" )
.queryParam( "app_id", appId.toString() )
.accept( MediaType.APPLICATION_JSON )
.post();
} catch ( Exception e) {
LOG.debug("Error refreshing index", e);
return;
}
LOG.debug("Refreshed index for appId {}", appId );
}
//TODO: move refresh index into context so that we automatically refresh the indexs without needing to call
//different values of context.
public void refreshIndex( String orgName, String appName ) {
LOG.debug("Refreshing index for app {}/{}", orgName, appName );
// be nice if somebody accidentally passed in orgName/appName
appName = appName.contains("/") ? appInfo.getName().split("/")[1] : appName;
try {
resource().path( "/refreshindex" )
.queryParam( "org_name", orgName )
.queryParam( "app_name", appName )
.accept( MediaType.APPLICATION_JSON )
.post();
} catch ( Exception e) {
LOG.debug("Error refreshing index", e);
return;
}
LOG.debug("Refreshed index for app {}/{}", orgName, appName );
}
}