Skip to content

Commit

Permalink
Minimalistic REST interface for adding items to inventory
Browse files Browse the repository at this point in the history
  • Loading branch information
testinfected committed Aug 31, 2012
1 parent f8dc7df commit 3641d4f
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 9 deletions.
@@ -1,31 +1,56 @@
package com.pyxis.petstore.controller;

import com.pyxis.petstore.Maybe;
import com.pyxis.petstore.domain.product.Item;
import com.pyxis.petstore.domain.product.ItemInventory;
import com.pyxis.petstore.domain.product.Product;
import com.pyxis.petstore.domain.product.ProductCatalog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.beans.PropertyEditorSupport;
import java.util.List;

@Controller
@RequestMapping(value = "/products/{productNumber}")
public class ItemsController {

private final ItemInventory itemInventory;
private final ProductCatalog productCatalog;
private final ItemInventory itemInventory;

@Autowired
public ItemsController(ItemInventory itemInventory) {
this.itemInventory = itemInventory;
}
public ItemsController(ProductCatalog productCatalog, ItemInventory itemInventory) {
this.productCatalog = productCatalog;
this.itemInventory = itemInventory;
}

@InitBinder
public void initBinder(DataBinder binder) {
binder.registerCustomEditor(Product.class, "product", new PropertyEditorSupport() {
public void setAsText(String text) throws IllegalArgumentException {
Maybe<Product> product = productCatalog.findByNumber(text);
if (product.exists()) setValue(product.bare());
}
});
}

@RequestMapping(value = "/items", method = RequestMethod.GET)
public String index(@PathVariable("productNumber") String productNumber, Model model) {
@RequestMapping(value = "/products/{productNumber}/items", method = RequestMethod.GET)
public String index(@PathVariable("productNumber") String productNumber, Model model) {
List<Item> items = itemInventory.findByProductNumber(productNumber);
model.addAttribute(items);
return "items";
}

@RequestMapping(value = "/items", method = RequestMethod.POST)
public void create(@Valid Item item, HttpServletResponse response) {
itemInventory.add(item);
response.setStatus(HttpServletResponse.SC_CREATED);
}
}
Expand Up @@ -3,39 +3,90 @@
import com.pyxis.petstore.controller.ItemsController;
import com.pyxis.petstore.domain.product.Item;
import com.pyxis.petstore.domain.product.ItemInventory;
import com.pyxis.petstore.domain.product.Product;
import com.pyxis.petstore.domain.product.ProductCatalog;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.validation.DataBinder;

import java.util.Arrays;
import java.util.List;

import static com.pyxis.petstore.Maybe.some;
import static org.hamcrest.Matchers.sameInstance;
import static org.testinfected.hamcrest.spring.SpringMatchers.hasAttribute;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static test.support.com.pyxis.petstore.builders.ItemBuilder.anItem;
import static test.support.com.pyxis.petstore.builders.ProductBuilder.aProduct;

@RunWith(JMock.class)
public class ItemsControllerTest {

Mockery context = new JUnit4Mockery();
ProductCatalog productCatalog = context.mock(ProductCatalog.class);
ItemInventory itemInventory = context.mock(ItemInventory.class);
ItemsController itemController = new ItemsController(itemInventory);
ItemsController itemsController = new ItemsController(productCatalog, itemInventory);
Model model = new ExtendedModelMap();

int CREATED = 201;

@Test public void
retrievesItemsByProductNumberAndMakeThemAvailableToView() {
final List<Item> anItemList = Arrays.asList(anItem().build());
context.checking(new Expectations(){{
oneOf(itemInventory).findByProductNumber("LAB-1234"); will(returnValue(anItemList));
}});
String view = itemController.index("LAB-1234", model);
String view = itemsController.index("LAB-1234", model);
assertThat("view", view, equalTo("items"));
assertThat("model", model, hasAttribute("itemList", anItemList));
}

@Test public void
addsItemToInventory() {
final Item item = anItem().build();
context.checking(new Expectations() {{
oneOf(itemInventory).add(with(same(item)));
}});
MockHttpServletResponse response = new MockHttpServletResponse();

itemsController.create(item, response);

assertThat("status code", response.getStatus(), equalTo(CREATED));
}

@Test public void
automaticallyConvertsProductNumbersToProductsWhenAddingItemsToInventory() {
final Product product = aProduct("PRD-0001").build();
context.checking(new Expectations() {{
oneOf(productCatalog).findByNumber("PRD-0001"); will(returnValue(some(product)));
}});

final Item item = anItem().withoutAProduct().build();
DataBinder binder = new DataBinder(item);
itemsController.initBinder(binder);

binder.bind(new MutablePropertyValues() {{
addPropertyValue("product", "PRD-0001");
}});
assertThat("item", item, itemOfProduct(sameInstance(product)));
}

private Matcher<Item> itemOfProduct(Matcher<Product> product) {
return new FeatureMatcher<Item, Product>(product, "item of product", "product") {
protected Product featureValueOf(Item actual) {
return actual.getProduct();
}
};
}
}
Expand Up @@ -29,6 +29,14 @@ public Item(ItemNumber number, Product product, BigDecimal price) {
this.price = price;
}

public void setNumber(String number) {
this.number = new ItemNumber(number);
}

public void setProduct(Product product) {
this.product = product;
}

public String getNumber() {
return number.getNumber();
}
Expand All @@ -49,6 +57,14 @@ public void setPrice(BigDecimal price) {
this.price = price;
}

public Product getProduct() {
return product;
}

public String getProductNumber() {
return product.getNumber();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down

0 comments on commit 3641d4f

Please sign in to comment.