Skip to content

Commit

Permalink
Merge pull request #72 from pith/fix-62-hal-builder
Browse files Browse the repository at this point in the history
Add possibility to extend HalResource
  • Loading branch information
pith committed Jul 16, 2015
2 parents 3067bd9 + c594ac6 commit 575a160
Show file tree
Hide file tree
Showing 41 changed files with 2,056 additions and 481 deletions.
Expand Up @@ -22,5 +22,5 @@ WRONG_DATASOURCE_PROVIDER.fix = Make sure to specify a correct DataSourceProvide
WRONG_DATASOURCE_CONTEXT.message = No datasource could be found in the configured JNDI context
WRONG_DATASOURCE_CONTEXT.fix = Make sure the JNDI context is correctly initialized and you configured it properly.
MISSING_DATASOURCE_CONFIG.message = Missing configuration for the jdbc datasource ${name}.
MISSING_DATASOURCE_CONFIG.fix = Please add the datasource properties:\n [org.seedstack.seed.persistence.jdbc.datasource.${name}]\n...
MISSING_DATASOURCE_CONFIG.fix = Please add the datasource properties:\n[org.seedstack.seed.persistence.jdbc.datasource.${name}]\n...
MISSING_DATASOURCE_CONFIG.url = http://www.seedstack.org/docs/seed/manual/persistence/jdbc/#configuration
@@ -0,0 +1,79 @@
/**
* Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
*
* This file is part of SeedStack, An enterprise-oriented full development stack.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest;

import com.jayway.restassured.response.Response;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.json.JSONException;
import org.junit.Test;
import org.seedstack.seed.it.AbstractSeedWebIT;
import org.skyscreamer.jsonassert.JSONAssert;

import java.net.URL;

import static com.jayway.restassured.RestAssured.expect;

/**
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
public class HalResourceIT extends AbstractSeedWebIT {

private static final String order1 = "{\"currency\":\"USD\",\"status\":\"shipped\",\"total\":10.2," +
"\"_links\":{\"invoice\":{\"href\":\"/invoices/873\"},\"self\":{\"href\":\"/orders/1\"},\"warehouse\":{\"href\":\"/warehouse/56\"}}}";


private static final String order2 = "{\"currency\":\"USD\",\"status\":\"shipped\",\"total\":10.2," +
"\"_links\":{\"invoice\":{\"href\":\"/invoices/873\"},\"self\":{\"href\":\"/orders/v2/1\"},\"warehouse\":{\"href\":\"/warehouse/56\"}}}";

private static final String orders = "{\"currentlyProcessing\":14,\"shippedToday\":20," +
"\"_links\":{\"next\":{\"href\":\"/orders?page=2\"},\"self\":{\"href\":\"/orders\"},\"find\":{\"href\":\"/orders{?id}\",\"templated\":true}}," +
"\"_embedded\":{\"orders\":[" +
"{\"total\":30.0,\"currency\":\"USD\",\"status\":\"shipped\",\"_links\":{\"basket\":{\"href\":\"/baskets/98712\"},\"self\":{\"href\":\"/order/123\"},\"customer\":{\"href\":\"/customers/7809\"}}}," +
"{\"total\":20.0,\"currency\":\"USD\",\"status\":\"processing\",\"_links\":{\"basket\":{\"href\":\"/baskets/97213\"},\"self\":{\"href\":\"/order/124\"},\"customer\":{\"href\":\"/customers/12369\"}}}]}}";

@ArquillianResource
private URL baseURL;

@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class).setWebXML("WEB-INF/web.xml");
}

@RunAsClient
@Test
public void hal_builder() throws JSONException {
Response response = expect().statusCode(200).given().header("Content-Type", "application/hal+json")
.get(baseURL.toString() + "rest/orders");

JSONAssert.assertEquals(orders, response.asString(), true);
}

@RunAsClient
@Test
public void hal_representation() throws JSONException {
Response response = expect().statusCode(200).given().header("Content-Type", "application/hal+json")
.get(baseURL.toString() + "rest/orders/1");

JSONAssert.assertEquals(order1, response.asString(), true);
}

@RunAsClient
@Test
public void hal_representation2() throws JSONException {
Response response = expect().statusCode(200).given().header("Content-Type", "application/hal+json")
.get(baseURL.toString() + "rest/orders/v2/1");

JSONAssert.assertEquals(order2, response.asString(), true);
}
}
Expand Up @@ -43,13 +43,7 @@ public void expose_json_home() throws JSONException {
Response response = expect().statusCode(200).given().header("Content-Type", "application/json-home")
.get(baseURL.toString() + "rest/");

String expectedBody = "{\"resources\":{" +
"\"http://example.org/rel/product\":{\"hints\":{\"allow\":[\"GET\"],\"formats\":{\"application/json\":\"\",\"application/json+hal\":\"\",\"application/xml\":\"\"}},\"href\":\"/product\"}," +
"\"http://example.org/rel/widget\":{\"href-template\":\"/widgets/{widgetName}\",\"hints\":{\"allow\":[\"GET\",\"PUT\"]},\"href-vars\":{\"widgetName\":\"widgetName\",\"pageSize\":\"pageSize\"}}," +
"\"http://example.org/rel/ValidResource1\":{\"href\":\"/JsonHomeValidResource1\"}," +
"\"http://example.org/rel/widgets\":{\"hints\":{\"allow\":[\"GET\"]},\"href\":\"/widgets\"}}}";

//response.body().prettyPrint();
String expectedBody = "{\"resources\":{\"http://example.org/rel/product\":{\"hints\":{\"allow\":[\"GET\"],\"formats\":{\"application/json\":\"\",\"application/hal+json\":\"\",\"application/xml\":\"\"}},\"href\":\"/product\"},\"http://example.org/rel/order\":{\"href-template\":\"/orders/{id}\",\"hints\":{\"allow\":[\"GET\"],\"formats\":{\"application/hal+json\":\"\"}},\"href-vars\":{\"id\":\"id\"}},\"http://example.org/rel/order2\":{\"href-template\":\"/orders/v2/{id}\",\"hints\":{\"allow\":[\"GET\"],\"formats\":{\"application/hal+json\":\"\"}},\"href-vars\":{\"id\":\"id\"}},\"http://example.org/rel/widget\":{\"href-template\":\"/widgets/{widgetName}\",\"hints\":{\"allow\":[\"GET\",\"PUT\"]},\"href-vars\":{\"widgetName\":\"widgetName\",\"pageSize\":\"pageSize\"}},\"http://example.org/rel/ValidResource1\":{\"href\":\"/JsonHomeValidResource1\"}}}";

JSONAssert.assertEquals(expectedBody, response.asString(), false);
}
Expand Down
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
*
* This file is part of SeedStack, An enterprise-oriented full development stack.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest;

import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.seedstack.seed.it.SeedITRunner;
import org.seedstack.seed.rest.api.RelRegistry;

import javax.inject.Inject;

/**
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
@RunWith(SeedITRunner.class)
public class RelRegistryIT {

@Inject
private RelRegistry relRegistry;

@Test
public void test_injection() {
Assertions.assertThat(relRegistry).isNotNull();
}
}
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
*
* This file is part of SeedStack, An enterprise-oriented full development stack.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest.fixtures;

import org.seedstack.seed.rest.api.Rel;
import org.seedstack.seed.rest.api.RelRegistry;
import org.seedstack.seed.rest.api.hal.HalBuilder;
import org.seedstack.seed.rest.internal.hal.fixture.OrderHal;
import org.seedstack.seed.rest.internal.hal.fixture.OrderRepresentation;
import org.seedstack.seed.rest.internal.hal.fixture.RepresentationFactory;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

/**
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
@Path("orders")
public class HalResource {

public static final String ORDER_REL = "order";
public static final String ORDER_REL2 = "order2";

@Inject
private RelRegistry relRegistry;

@GET
@Produces("application/hal+json")
public Response getOrders() {
return Response.ok(new RepresentationFactory().createOrders()).build();
}

@Rel(value = ORDER_REL, expose = true)
@GET
@Path("{id}")
@Produces("application/hal+json")
public Response getOrders(@PathParam("id") String id) {
return Response.ok(
new OrderHal(id, "USD", "shipped", 10.20f)
.link("warehouse", "/warehouse/" + 56)
.link("invoice", "/invoices/873"))
.build();
}

@Rel(value = ORDER_REL2, expose = true)
@GET
@Path("v2/{id}")
@Produces("application/hal+json")
public Response getOrders2(@PathParam("id") String id) {
return Response.ok(HalBuilder.create(new OrderRepresentation(10.20f, "USD", "shipped"))
.self(relRegistry.uri(ORDER_REL2).set("id", id).expand())
.link("warehouse", "/warehouse/56")
.link("invoice", "/invoices/873"))
.build();
}
}
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2013-2015 by The SeedStack authors. All rights reserved.
*
* This file is part of SeedStack, An enterprise-oriented full development stack.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.rest.internal;

import org.kametic.specifications.AbstractSpecification;

import javax.ws.rs.HttpMethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
class HttpMethodSpecification extends AbstractSpecification<Method> {

@Override
public boolean isSatisfiedBy(Method candidate) {
for (Annotation annotation : candidate.getDeclaredAnnotations()) {
if (annotation.annotationType().getAnnotation(HttpMethod.class) != null) {
return true;
}
}
return false;
}
}
Expand Up @@ -20,7 +20,7 @@
/**
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
public class JaxRsProviderSpecification extends AbstractSpecification<Class<?>> {
class JaxRsProviderSpecification extends AbstractSpecification<Class<?>> {
@Override
public boolean isSatisfiedBy(Class<?> candidate) {
//noinspection unchecked
Expand Down
Expand Up @@ -15,16 +15,28 @@
import java.lang.reflect.Method;

/**
* The specification matches the method which should be exposed as JSON-HOME resources.
* In order to be exposed the method should be annotated by {@link org.seedstack.seed.rest.api.Rel}
* with {@code expose=true}. If the annotated is not found the declaring class is checked.
* The specification matches HTTP methods which should be exposed as JSON-HOME resources.
* <p>
* In order to be exposed the method should be:
* </p>
* <ol>
* <li>meta annotated by {@link javax.ws.rs.HttpMethod};</li>
* <li>annotated by {@link org.seedstack.seed.rest.api.Rel} with {@code expose=true};</li>
* <li>If the annotated is not found on the method, the declaring class is checked.</li>
* </ol>
*
* @author pierre.thirouin@ext.mpsa.com (Pierre Thirouin)
*/
public class JsonHomeSpecification extends AbstractSpecification<Method> {
class JsonHomeSpecification extends AbstractSpecification<Method> {

private static final HttpMethodSpecification HTTP_METHOD_SPECIFICATION = new HttpMethodSpecification();

@Override
public boolean isSatisfiedBy(Method method) {
if (!HTTP_METHOD_SPECIFICATION.isSatisfiedBy(method)) {
return false;
}

Rel rootRel = method.getDeclaringClass().getAnnotation(Rel.class);
Rel rel = method.getAnnotation(Rel.class);
if (rel != null) {
Expand Down

0 comments on commit 575a160

Please sign in to comment.