Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
rhbz988202 - remove interface from zanata api and make REST service beta
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Huang committed Mar 10, 2014
1 parent bc91d20 commit d3ede01
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 49 deletions.
@@ -1,6 +1,5 @@
package org.zanata.feature.misc;

import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
Expand All @@ -11,6 +10,7 @@
import javax.ws.rs.core.Response;

import org.hamcrest.Matchers;
import org.jboss.resteasy.client.ClientRequest;
import org.jboss.resteasy.client.core.BaseClientResponse;
import org.jboss.resteasy.util.GenericType;
import org.junit.Rule;
Expand All @@ -19,9 +19,10 @@
import org.zanata.feature.DetailedTest;
import org.zanata.page.administration.AdministrationPage;
import org.zanata.page.administration.ServerConfigurationPage;
import org.zanata.rest.client.IServerConfigurationResource;
import org.zanata.rest.dto.Configuration;
import org.zanata.util.AddUsersRule;
import org.zanata.util.Constants;
import org.zanata.util.PropertiesHolder;
import org.zanata.util.RetryRule;
import org.zanata.util.ZanataRestCaller;
import org.zanata.workflow.BasicWorkFlow;
Expand Down Expand Up @@ -55,6 +56,7 @@ public class RateLimitTest {
private static final String TRANSLATOR = "translator";
private static final String TRANSLATOR_API =
"d83882201764f7d339e97c4b087f0806";
private String rateLimitingPathParam = "c/" + KEY_RATE_LIMIT_PER_SECOND;

@Test
public void canConfigureRateLimitByWebUI() {
Expand All @@ -79,17 +81,16 @@ public void canConfigureRateLimitByWebUI() {
}

@Test
public void canCallServerConfigurationRestService() {
IServerConfigurationResource resource =
getServerConfigurationResource();

public void canCallServerConfigurationRestService() throws Exception {
ClientRequest clientRequest = getClientRequest(rateLimitingPathParam);
clientRequest.body("text/plain", "1");
// can put
Response putResponse = resource.put(KEY_RATE_LIMIT_PER_SECOND, "1");
Response putResponse = clientRequest.put();

assertThat(getStatusAndReleaseConnection(putResponse), Matchers.is(201));

// can get single configuration
Response getResponse = resource.get(KEY_RATE_LIMIT_PER_SECOND);
Response getResponse = getClientRequest(rateLimitingPathParam).get();

assertThat(getResponse.getStatus(), Matchers.is(200));
Configuration rateLimitConfig =
Expand All @@ -100,65 +101,76 @@ public void canCallServerConfigurationRestService() {
assertThat(rateLimitConfig.getValue(), Matchers.equalTo("1"));

// can get all configurations
Response getAllResponse = resource.get();
Response getAllResponse = getClientRequest("").get();
BaseClientResponse baseClientResponse =
(BaseClientResponse) getAllResponse;
Type genericType = new GenericType<List<Configuration>>() {
}.getGenericType();
GenericType<List<Configuration>> listGenericType =
new GenericType<List<Configuration>>() {
};

List<Configuration> configurations =
(List<Configuration>) baseClientResponse.getEntity(List.class,
genericType);
(List<Configuration>) baseClientResponse.getEntity(listGenericType);
log.info("result {}", configurations);

assertThat(getStatusAndReleaseConnection(getAllResponse),
Matchers.is(200));
assertThat(configurations, Matchers.hasItem(rateLimitConfig));
}

private static IServerConfigurationResource
getServerConfigurationResource() {
return new ZanataRestCaller().getZanataProxyFactory().createProxy(
IServerConfigurationResource.class);
private static ClientRequest getClientRequest(String pathParam) {
ClientRequest clientRequest = new ClientRequest(
PropertiesHolder.getProperty(Constants.zanataInstance.value()) +
"rest/configurations/" + pathParam);
clientRequest.header("X-Auth-User", "admin");
clientRequest.header("X-Auth-Token", PropertiesHolder.getProperty(Constants.zanataApiKey.value()));
clientRequest.header("Content-Type", "application/xml");
return clientRequest;
}

@Test
public void serverConfigurationRestServiceOnlyAvailableToAdmin() {
// invoke service as translator user
IServerConfigurationResource resource =
new ZanataRestCaller(TRANSLATOR, TRANSLATOR_API)
.getZanataProxyFactory().createProxy(
IServerConfigurationResource.class);

public void serverConfigurationRestServiceOnlyAvailableToAdmin()
throws Exception {
// all request should be rejected
Response response = resource.get();
Response response = createRequestAsTranslator("").get();
assertThat(getStatusAndReleaseConnection(response), Matchers.is(401));

Response response1 = resource.get(KEY_ADMIN_EMAIL);
Response response1 = createRequestAsTranslator("c/" + KEY_ADMIN_EMAIL).get();
assertThat(getStatusAndReleaseConnection(response1), Matchers.is(401));

Response response2 = resource.put(KEY_ADMIN_EMAIL, "admin@email.com");
ClientRequest request =
createRequestAsTranslator("c/" + KEY_ADMIN_EMAIL);
request.body("text/plain", "admin@email.com");
Response response2 = request.put();
assertThat(getStatusAndReleaseConnection(response2), Matchers.is(401));
}

private static ClientRequest createRequestAsTranslator(Object pathParam) {
ClientRequest clientRequest = new ClientRequest(
PropertiesHolder.getProperty(Constants.zanataInstance.value()) +
"rest/configurations/" + pathParam);
clientRequest.header("X-Auth-User", TRANSLATOR);
clientRequest.header("X-Auth-Token", TRANSLATOR_API);
clientRequest.header("Content-Type", "application/xml");
return clientRequest;
}

@Test
public void canOnlyDealWithKnownConfiguration() {
IServerConfigurationResource resource =
getServerConfigurationResource();
public void canOnlyDealWithKnownConfiguration() throws Exception {
ClientRequest clientRequest = getClientRequest("c/arbitrary");

Response putResponse = resource.put("arbitrary", "value");
Response putResponse = clientRequest.put();
assertThat(getStatusAndReleaseConnection(putResponse), Matchers.is(400));

Response getResponse = resource.get("arbitrary");
Response getResponse = getClientRequest("c/arbitrary").get();
assertThat(getStatusAndReleaseConnection(getResponse), Matchers.is(404));
}

@Test
public void canRateLimitRestRequestsPerAPIKey() throws InterruptedException {
IServerConfigurationResource resource =
getServerConfigurationResource();
public void canRateLimitRestRequestsPerAPIKey() throws Exception {
ClientRequest clientRequest = getClientRequest(rateLimitingPathParam);
clientRequest.body("text/plain", "3");

Response putResponse = resource.put(KEY_RATE_LIMIT_PER_SECOND, "3");
Response putResponse = clientRequest.put();
checkStatusAndReleaseConnection(putResponse);

// prepare to fire multiple REST requests
Expand Down Expand Up @@ -215,10 +227,11 @@ ImmutableList.<Callable<Integer>> builder()

@Test
public void rateLimitChangeTakesEffectImmediately()
throws InterruptedException {
throws Exception {
// we start allowing 10 request per second
checkStatusAndReleaseConnection(getServerConfigurationResource().put(
KEY_RATE_LIMIT_PER_SECOND, "10"));
ClientRequest clientRequest = getClientRequest(rateLimitingPathParam);
clientRequest.body("text/plain", "10");
checkStatusAndReleaseConnection(clientRequest.put());

// prepare to fire multiple REST requests
final AtomicInteger atomicInteger = new AtomicInteger(1);
Expand Down Expand Up @@ -248,8 +261,9 @@ public Integer call() {
assertThat(result, Matchers.contains(201, 201));

// we now change rate limit to 1
checkStatusAndReleaseConnection(getServerConfigurationResource()
.put(KEY_RATE_LIMIT_PER_SECOND, "1"));
clientRequest = getClientRequest(rateLimitingPathParam);
clientRequest.body("text/plain", "1");
checkStatusAndReleaseConnection(clientRequest.put());

// new requests is rate limited
List<Integer> resultAfter = getResultStatusCodes(
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.zanata.rest.dto.Link;
import org.zanata.seam.interceptor.RateLimitingInterceptor;
import org.zanata.util.Introspectable;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
Expand All @@ -35,6 +36,8 @@
import lombok.extern.slf4j.Slf4j;

/**
* This API is experimental only and subject to change or even removal.
*
* @author Patrick Huang <a
* href="mailto:pahuang@redhat.com">pahuang@redhat.com</a>
*/
Expand All @@ -45,6 +48,7 @@
@Transactional
@Restrict("#{s:hasRole('admin')}")
@Slf4j
@Beta
public class IntrospectableObjectMonitorService {
// TODO check http://code.google.com/p/reflections/ and re-implement this
private static List<Introspectable> introspectables = ImmutableList
Expand Down
Expand Up @@ -6,43 +6,55 @@
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
import org.jboss.resteasy.util.GenericType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.core.Events;
import org.zanata.ApplicationConfiguration;
import org.zanata.common.Namespaces;
import org.zanata.dao.ApplicationConfigurationDAO;
import org.zanata.model.HApplicationConfiguration;
import org.zanata.rest.MediaTypes;
import org.zanata.rest.dto.Configuration;
import org.zanata.rest.dto.Link;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
* This API is experimental only and subject to change or even removal.
*
* @author Patrick Huang <a
* href="mailto:pahuang@redhat.com">pahuang@redhat.com</a>
*/
@Name("serverConfigurationResource")
@Path(ServerConfigurationResource.SERVICE_PATH)
@Path("/configurations")
@Produces("application/xml")
@Consumes("application/xml")
@Transactional
@Restrict("#{s:hasRole('admin')}")
@Slf4j
public class ServerConfigurationService implements ServerConfigurationResource {
@Beta
public class ServerConfigurationService {

private static List<String> availableKeys;

Expand All @@ -54,7 +66,17 @@ public class ServerConfigurationService implements ServerConfigurationResource {
@In
private ApplicationConfigurationDAO applicationConfigurationDAO;

@Override
/**
* Retrieves all existing server configurations.
*
* @return The following response status codes will be returned from this
* operation:<br>
* OK(200) - Response containing value for the config key.<br>
* INTERNAL SERVER ERROR(500) - If there is an unexpected error in
* the server while performing this operation.
*/
@GET
@Wrapped(element = "configurations", namespace = Namespaces.ZANATA_API)
public Response get() {
List<HApplicationConfiguration> all =
applicationConfigurationDAO.findAll();
Expand All @@ -68,8 +90,19 @@ public Response get() {

}

@Override
public Response get(@Nonnull String configKey) {
/**
* Retrieves a specific server configuration.
*
* @return The following response status codes will be returned from this
* operation:<br>
* OK(200) - Response containing value for the config key.<br>
* NOT_FOUND(404) - If server does not have given configuration set.<br>
* INTERNAL SERVER ERROR(500) - If there is an unexpected error in
* the server while performing this operation.
*/
@GET
@Path("c/{configKey}")
public Response get(@PathParam("configKey") @Nonnull String configKey) {
HApplicationConfiguration config =
applicationConfigurationDAO.findByKey(configKey);
if (config == null) {
Expand All @@ -80,8 +113,25 @@ public Response get(@Nonnull String configKey) {
return Response.ok().entity(configuration).build();
}

@Override
public Response put(@Nonnull String configKey, String configValue) {
/**
* Creates or updates a server configuration. If a configuration with the
* given key already exists, the value will be overwritten with the provided
* data. Otherwise, a new config will be created.
*
* @param configKey
* The configuration item to be created/updated.
* @return The following response status codes will be returned from this
* operation:<br>
* OK(200) - If an existing configuration was modified.<br>
* CREATED(201) - If a new configuration was created.<br>
* UNAUTHORIZED(401) - If the user does not have the proper
* permissions to perform this operation.<br>
* INTERNAL SERVER ERROR(500) - If there is an unexpected error in
* the server while performing this operation.
*/
@PUT
@Path("/c/{configKey}")
public Response put(@PathParam("configKey") @Nonnull String configKey, String configValue) {
if (!isConfigKeyValid(configKey)) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("config key not supported: " + configKey).build();
Expand Down

0 comments on commit d3ede01

Please sign in to comment.