├── .travis.yml
├── .settings
├── org.eclipse.m2e.core.prefs
├── org.eclipse.wst.common.project.facet.core.xml
├── org.eclipse.core.resources.prefs
└── org.eclipse.jdt.core.prefs
├── src
├── main
│ └── java
│ │ └── com
│ │ └── dzone
│ │ └── albanoj2
│ │ └── example
│ │ └── rest
│ │ ├── domain
│ │ ├── Identifiable.java
│ │ └── Order.java
│ │ ├── resource
│ │ ├── ResourceAssembler.java
│ │ ├── OrderResource.java
│ │ └── OrderResourceAssembler.java
│ │ ├── repository
│ │ ├── OrderRepository.java
│ │ ├── IdGenerator.java
│ │ └── InMemoryRepository.java
│ │ ├── Application.java
│ │ └── controller
│ │ └── OrderController.java
└── test
│ ├── java
│ └── com
│ │ └── dzone
│ │ └── albanoj2
│ │ └── example
│ │ └── rest
│ │ └── test
│ │ ├── acceptance
│ │ ├── OrderAcceptanceTests.java
│ │ ├── step
│ │ │ └── OrderSteps.java
│ │ └── util
│ │ │ └── AbstractSteps.java
│ │ ├── integration
│ │ └── controller
│ │ │ ├── util
│ │ │ ├── CompositeResultMatcher.java
│ │ │ ├── ControllerTestUtils.java
│ │ │ └── OrderControllerTestUtils.java
│ │ │ ├── ControllerIntegrationTest.java
│ │ │ └── OrderControllerTest.java
│ │ ├── util
│ │ └── OrderTestUtils.java
│ │ └── unit
│ │ └── repository
│ │ ├── IdGeneratorTest.java
│ │ └── OrderRepositoryTest.java
│ └── resources
│ └── acceptance
│ └── order.feature
├── .gitignore
├── .project
├── .classpath
├── pom.xml
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding//src/main/resources=UTF-8
4 | encoding//src/test/java=UTF-8
5 | encoding//src/test/resources=UTF-8
6 | encoding/=UTF-8
7 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/domain/Identifiable.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.domain;
2 |
3 | public interface Identifiable extends org.springframework.hateoas.Identifiable {
4 | public void setId(Long id);
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | .mvn/timing.properties
10 |
11 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
12 | !/.mvn/wrapper/maven-wrapper.jar
13 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/acceptance/OrderAcceptanceTests.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.acceptance;
2 |
3 | import org.junit.runner.RunWith;
4 | import org.springframework.test.context.web.WebAppConfiguration;
5 |
6 | import cucumber.api.CucumberOptions;
7 | import cucumber.api.junit.Cucumber;
8 |
9 | @RunWith(Cucumber.class)
10 | @CucumberOptions(features = "src/test/resources/acceptance")
11 | @WebAppConfiguration
12 | public class OrderAcceptanceTests {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/resource/ResourceAssembler.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.resource;
2 |
3 | import java.util.Collection;
4 | import java.util.stream.Collectors;
5 |
6 | public abstract class ResourceAssembler {
7 |
8 | public abstract ResourceType toResource(DomainType domainObject);
9 |
10 | public Collection toResourceCollection(Collection domainObjects) {
11 | return domainObjects.stream().map(o -> toResource(o)).collect(Collectors.toList());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/repository/OrderRepository.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.repository;
2 |
3 | import org.springframework.stereotype.Repository;
4 |
5 | import com.dzone.albanoj2.example.rest.domain.Order;
6 |
7 | @Repository
8 | public class OrderRepository extends InMemoryRepository {
9 |
10 | protected void updateIfExists(Order original, Order updated) {
11 | original.setDescription(updated.getDescription());
12 | original.setCostInCents(updated.getCostInCents());
13 | original.setComplete(updated.isComplete());
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/repository/IdGenerator.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.repository;
2 |
3 | import java.util.concurrent.atomic.AtomicLong;
4 |
5 | import org.springframework.beans.factory.config.BeanDefinition;
6 | import org.springframework.context.annotation.Scope;
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | @Scope(BeanDefinition.SCOPE_PROTOTYPE)
11 | public class IdGenerator {
12 |
13 | private AtomicLong nextId = new AtomicLong(1);
14 |
15 | public long getNextId() {
16 | return nextId.getAndIncrement();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
12 | org.eclipse.jdt.core.compiler.source=1.8
13 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/Application.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.hateoas.config.EnableEntityLinks;
6 | import org.springframework.hateoas.config.EnableHypermediaSupport;
7 | import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
8 |
9 | @EnableEntityLinks
10 | @EnableHypermediaSupport(type = HypermediaType.HAL)
11 | @SpringBootApplication
12 | public class Application {
13 |
14 | public static void main(String[] args) {
15 | SpringApplication.run(Application.class, args);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/integration/controller/util/CompositeResultMatcher.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.integration.controller.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.springframework.test.web.servlet.MvcResult;
7 | import org.springframework.test.web.servlet.ResultMatcher;
8 |
9 | public class CompositeResultMatcher implements ResultMatcher {
10 |
11 | private List matchers = new ArrayList<>();
12 |
13 | @Override
14 | public void match(MvcResult result) throws Exception {
15 |
16 | for (ResultMatcher matcher: matchers) {
17 | matcher.match(result);
18 | }
19 | }
20 |
21 | public CompositeResultMatcher addMatcher(ResultMatcher matcher) {
22 | matchers.add(matcher);
23 | return this;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/resources/acceptance/order.feature:
--------------------------------------------------------------------------------
1 | Feature: User can successfully get, create, delete, and update orders
2 |
3 | Scenario: User gets a created order
4 | When the user creates an order
5 | And the order is successfully created
6 | And the user gets the created order
7 | Then the user receives status code of 200
8 | And the retrieved order is correct
9 |
10 | Scenario: User gets an existing order
11 | Given an order exists
12 | When the user gets the created order
13 | Then the user receives status code of 200
14 | And the retrieved order is correct
15 |
16 | Scenario: User deletes a created order
17 | Given an order exists
18 | And the user deletes the created order
19 | And the user receives status code of 204
20 | When the user gets the created order
21 | Then the user receives status code of 404
22 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/resource/OrderResource.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.resource;
2 |
3 | import org.springframework.hateoas.ResourceSupport;
4 |
5 | import com.dzone.albanoj2.example.rest.domain.Order;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 |
8 | public class OrderResource extends ResourceSupport {
9 |
10 | private final long id;
11 | private final String description;
12 | private final long costInCents;
13 | private final boolean isComplete;
14 |
15 | public OrderResource(Order order) {
16 | id = order.getId();
17 | description = order.getDescription();
18 | costInCents = order.getCostInCents();
19 | isComplete = order.isComplete();
20 | }
21 |
22 | @JsonProperty("id")
23 | public Long getResourceId() {
24 | return id;
25 | }
26 |
27 | public String getDescription() {
28 | return description;
29 | }
30 |
31 | public long getCostInCents() {
32 | return costInCents;
33 | }
34 |
35 | public boolean isComplete() {
36 | return isComplete;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/resource/OrderResourceAssembler.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.resource;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.hateoas.EntityLinks;
5 | import org.springframework.hateoas.Link;
6 | import org.springframework.stereotype.Component;
7 |
8 | import com.dzone.albanoj2.example.rest.domain.Order;
9 |
10 | @Component
11 | public class OrderResourceAssembler extends ResourceAssembler {
12 |
13 | @Autowired
14 | protected EntityLinks entityLinks;
15 |
16 | private static final String UPDATE_REL = "update";
17 | private static final String DELETE_REL = "delete";
18 |
19 | @Override
20 | public OrderResource toResource(Order order) {
21 |
22 | OrderResource resource = new OrderResource(order);
23 |
24 | final Link selfLink = entityLinks.linkToSingleResource(order);
25 |
26 | resource.add(selfLink.withSelfRel());
27 | resource.add(selfLink.withRel(UPDATE_REL));
28 | resource.add(selfLink.withRel(DELETE_REL));
29 |
30 | return resource;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/domain/Order.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.domain;
2 |
3 | public class Order implements Identifiable {
4 |
5 | private Long id;
6 | private String description;
7 | private long costInCents;
8 | private boolean isComplete;
9 |
10 | @Override
11 | public Long getId() {
12 | return id;
13 | }
14 |
15 | @Override
16 | public void setId(Long id) {
17 | this.id = id;
18 | }
19 |
20 | public String getDescription() {
21 | return description;
22 | }
23 |
24 | public void setDescription(String description) {
25 | this.description = description;
26 | }
27 |
28 | public void setCostInCents(long cost) {
29 | costInCents = cost;
30 | }
31 |
32 | public long getCostInCents() {
33 | return costInCents;
34 | }
35 |
36 | public void setComplete(boolean isComplete) {
37 | this.isComplete = isComplete;
38 | }
39 |
40 | public void markComplete() {
41 | setComplete(true);
42 | }
43 |
44 | public void markIncomplete() {
45 | setComplete(false);
46 | }
47 |
48 | public boolean isComplete() {
49 | return isComplete;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/util/OrderTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.util;
2 |
3 | import org.junit.Assert;
4 |
5 | import com.dzone.albanoj2.example.rest.domain.Order;
6 |
7 | public class OrderTestUtils {
8 |
9 | public static void assertAllButIdsMatchBetweenOrders(Order expected, Order actual) {
10 | Assert.assertEquals(expected.getDescription(), actual.getDescription());
11 | Assert.assertEquals(expected.getCostInCents(), actual.getCostInCents());
12 | Assert.assertEquals(expected.isComplete(), actual.isComplete());
13 | }
14 |
15 | public static Order generateTestOrder() {
16 | Order order = new Order();
17 | order.setDescription("test description");
18 | order.setCostInCents(150L);
19 | order.markIncomplete();
20 | return order;
21 | }
22 |
23 | public static Order generateUpdatedOrder(Order original) {
24 | Order updated = new Order();
25 | updated.setDescription(original.getDescription() + " updated");
26 | updated.setCostInCents(original.getCostInCents() + 100);
27 | updated.markComplete();
28 | return updated;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/unit/repository/IdGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.unit.repository;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.test.annotation.DirtiesContext;
9 | import org.springframework.test.annotation.DirtiesContext.ClassMode;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | import com.dzone.albanoj2.example.rest.repository.IdGenerator;
13 |
14 |
15 | @RunWith(SpringRunner.class)
16 | @SpringBootTest
17 | @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
18 | public class IdGeneratorTest {
19 |
20 | @Autowired
21 | private IdGenerator generator1;
22 |
23 | @Autowired
24 | private IdGenerator generator2;
25 |
26 | @Test
27 | public void testMultipleGeneratorsEnsureGeneratorsDoNotInterfere() throws Exception {
28 | Assert.assertEquals(1, generator1.getNextId());
29 | Assert.assertEquals(2, generator1.getNextId());
30 | Assert.assertEquals(1, generator2.getNextId());
31 | Assert.assertEquals(2, generator2.getNextId());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | order-rest-backend
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.springframework.ide.eclipse.core.springbuilder
20 |
21 |
22 |
23 |
24 | org.springframework.ide.eclipse.boot.validation.springbootbuilder
25 |
26 |
27 |
28 |
29 | org.eclipse.m2e.core.maven2Builder
30 |
31 |
32 |
33 |
34 |
35 | org.springframework.ide.eclipse.core.springnature
36 | org.eclipse.jdt.core.javanature
37 | org.eclipse.m2e.core.maven2Nature
38 | org.eclipse.wst.common.project.facet.core.nature
39 |
40 |
41 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/main/java/com/dzone/albanoj2/example/rest/repository/InMemoryRepository.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.repository;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 | import java.util.Optional;
7 |
8 | import org.springframework.beans.factory.annotation.Autowired;
9 |
10 | import com.dzone.albanoj2.example.rest.domain.Identifiable;
11 |
12 |
13 | public abstract class InMemoryRepository {
14 |
15 | @Autowired
16 | private IdGenerator idGenerator;
17 |
18 | private List elements = Collections.synchronizedList(new ArrayList<>());
19 |
20 | public T create(T element) {
21 | elements.add(element);
22 | element.setId(idGenerator.getNextId());
23 | return element;
24 | }
25 |
26 | public boolean delete(Long id) {
27 | return elements.removeIf(element -> element.getId().equals(id));
28 | }
29 |
30 | public List findAll() {
31 | return elements;
32 | }
33 |
34 | public Optional findById(Long id) {
35 | return elements.stream().filter(e -> e.getId().equals(id)).findFirst();
36 | }
37 |
38 | public int getCount() {
39 | return elements.size();
40 | }
41 |
42 | public void clear() {
43 | elements.clear();
44 | }
45 |
46 | public boolean update(Long id, T updated) {
47 |
48 | if (updated == null) {
49 | return false;
50 | }
51 | else {
52 | Optional element = findById(id);
53 | element.ifPresent(original -> updateIfExists(original, updated));
54 | return element.isPresent();
55 | }
56 | }
57 |
58 | protected abstract void updateIfExists(T original, T desired);
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/integration/controller/util/ControllerTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.integration.controller.util;
2 |
3 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
4 |
5 | import org.springframework.test.web.servlet.ResultMatcher;
6 |
7 | public class ControllerTestUtils {
8 |
9 | private static final String SELF_REL = "self";
10 | private static final String UPDATE_REL = "update";
11 | private static final String DELETE_REL = "delete";
12 |
13 | public static ResultMatcher selfLinkAtIndexIs(int index, String expected) {
14 | return linkAtIndexIs(index, SELF_REL, expected);
15 | }
16 |
17 | private static ResultMatcher linkAtIndexIs(int index, String linkKey, String expected) {
18 | return jsonPath("$.[" + index + "]._links." + linkKey + ".href").value(expected);
19 | }
20 |
21 | public static ResultMatcher selfLinkIs(String expected) {
22 | return linkIs(SELF_REL, expected);
23 | }
24 |
25 | public static ResultMatcher linkIs(String linkKey, String expected) {
26 | return jsonPath("$._links." + linkKey + ".href").value(expected);
27 | }
28 |
29 | public static ResultMatcher updateLinkAtIndexIs(int index, String expected) {
30 | return linkAtIndexIs(index, UPDATE_REL, expected);
31 | }
32 |
33 | public static ResultMatcher updateLinkIs(String expected) {
34 | return linkIs(UPDATE_REL, expected);
35 | }
36 |
37 | public static ResultMatcher deleteLinkAtIndexIs(int index, String expected) {
38 | return linkAtIndexIs(index, DELETE_REL, expected);
39 | }
40 |
41 | public static ResultMatcher deleteLinkIs(String expected) {
42 | return linkIs(DELETE_REL, expected);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/integration/controller/ControllerIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.integration.controller;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
5 | import org.springframework.http.MediaType;
6 | import org.springframework.test.web.servlet.MockMvc;
7 | import org.springframework.test.web.servlet.ResultActions;
8 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
9 |
10 | import com.fasterxml.jackson.core.JsonProcessingException;
11 | import com.fasterxml.jackson.databind.ObjectMapper;
12 |
13 |
14 | @AutoConfigureMockMvc
15 | public class ControllerIntegrationTest {
16 |
17 | @Autowired
18 | private MockMvc mvc;
19 |
20 | protected ResultActions get(String url, Object... urlVariables) throws Exception {
21 | return mvc.perform(MockMvcRequestBuilders.get(url, urlVariables).accept(MediaType.APPLICATION_JSON));
22 | }
23 |
24 | protected ResultActions post(String url, String content, Object... urlVariables) throws Exception {
25 | return mvc.perform(MockMvcRequestBuilders.post(url, urlVariables)
26 | .accept(MediaType.APPLICATION_JSON)
27 | .contentType(MediaType.APPLICATION_JSON)
28 | .content(content)
29 | );
30 | }
31 |
32 | protected ResultActions delete(String url, Object... urlVariables) throws Exception {
33 | return mvc.perform(MockMvcRequestBuilders.delete(url, urlVariables).accept(MediaType.APPLICATION_JSON));
34 | }
35 |
36 | protected ResultActions put(String url, Object content, Object... urlVariables) throws Exception {
37 | return mvc.perform(MockMvcRequestBuilders.put(url, urlVariables)
38 | .accept(MediaType.APPLICATION_JSON)
39 | .contentType(MediaType.APPLICATION_JSON)
40 | .content(toJsonString(content))
41 | );
42 | }
43 |
44 | protected static String toJsonString(final Object obj) throws JsonProcessingException {
45 | ObjectMapper mapper = new ObjectMapper();
46 | return mapper.writeValueAsString(obj);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.dzone.albanoj2.example.rest
7 | order-rest-backend
8 | 0.0.1-SNAPSHOT
9 |
10 |
11 | org.springframework.boot
12 | spring-boot-starter-parent
13 | 1.4.7.RELEASE
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-starter-web
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-test
24 | test
25 |
26 |
27 | com.jayway.jsonpath
28 | json-path
29 | test
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-hateoas
34 |
35 |
36 | junit
37 | junit
38 | test
39 |
40 |
41 | info.cukes
42 | cucumber-junit
43 | 1.2.5
44 | test
45 |
46 |
47 | info.cukes
48 | cucumber-spring
49 | 1.2.5
50 | test
51 |
52 |
53 | commons-io
54 | commons-io
55 | 2.5
56 |
57 |
58 |
59 |
60 |
61 | 1.8
62 |
63 |
64 |
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-maven-plugin
70 |
71 |
72 |
73 |
74 |
75 |
76 | spring-releases
77 | https://repo.spring.io/libs-release
78 |
79 |
80 |
81 |
82 | spring-releases
83 | https://repo.spring.io/libs-release
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/integration/controller/util/OrderControllerTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.integration.controller.util;
2 |
3 | import static com.dzone.albanoj2.example.rest.test.integration.controller.util.ControllerTestUtils.*;
4 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
5 |
6 | import org.springframework.hateoas.EntityLinks;
7 | import org.springframework.test.web.servlet.ResultMatcher;
8 |
9 | import com.dzone.albanoj2.example.rest.domain.Order;
10 |
11 | public class OrderControllerTestUtils {
12 |
13 | public static ResultMatcher orderAtIndexIsCorrect(int index, Order expected) {
14 | return new CompositeResultMatcher()
15 | .addMatcher(jsonPath("$.[" + index + "].id").value(expected.getId()))
16 | .addMatcher(jsonPath("$.[" + index + "].description").value(expected.getDescription()))
17 | .addMatcher(jsonPath("$.[" + index + "].costInCents").value(expected.getCostInCents()))
18 | .addMatcher(jsonPath("$.[" + index + "].complete").value(expected.isComplete()));
19 | }
20 |
21 | public static ResultMatcher orderIsCorrect(Order expected) {
22 | return orderIsCorrect(expected.getId(), expected);
23 | }
24 |
25 | private static ResultMatcher orderIsCorrect(Long expectedId, Order expected) {
26 | return new CompositeResultMatcher().addMatcher(jsonPath("$.id").value(expectedId))
27 | .addMatcher(jsonPath("$.description").value(expected.getDescription()))
28 | .addMatcher(jsonPath("$.costInCents").value(expected.getCostInCents()))
29 | .addMatcher(jsonPath("$.complete").value(expected.isComplete()));
30 | }
31 |
32 | public static ResultMatcher updatedOrderIsCorrect(Long originalId, Order expected) {
33 | return orderIsCorrect(originalId, expected);
34 | }
35 |
36 | public static ResultMatcher orderLinksAtIndexAreCorrect(int index, Order expected, EntityLinks entityLinks) {
37 | final String selfReference = entityLinks.linkForSingleResource(expected).toString();
38 |
39 | return new CompositeResultMatcher()
40 | .addMatcher(selfLinkAtIndexIs(index, selfReference))
41 | .addMatcher(updateLinkAtIndexIs(index, selfReference))
42 | .addMatcher(deleteLinkAtIndexIs(index, selfReference));
43 | }
44 |
45 | public static ResultMatcher orderLinksAreCorrect(Order expected, EntityLinks entityLinks) {
46 | final String selfReference = entityLinks.linkForSingleResource(expected).toString();
47 |
48 | return new CompositeResultMatcher()
49 | .addMatcher(selfLinkIs(selfReference))
50 | .addMatcher(updateLinkIs(selfReference))
51 | .addMatcher(deleteLinkIs(selfReference));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Order Management REST API 
2 |
3 | Demonstrates a simple RESTful web service using Spring MVC and Java. This web service provides an in-memory order management service, with the capability to get a single order, get all orders, create an order, delete an order, and update an order. After exploring this project, the reader will understand the three common layers of a RESTful web service (namely domain, data source, and presentation), the common design decisions used when creating a web service, and the functionality provided by the Spring framework for creating a web service (as well as supplemental functionality, such as creating [HATEOAS](http://projects.spring.io/spring-hateoas/) links). In particular, the reader will learn
4 |
5 | - How to create a domain model
6 | - How to store domain objects in a persistence layer
7 | - How to wrap domain objects within resource objects
8 | - How to add HATEOAS links to a resource
9 | - How to serve up resources to a client over HTTP
10 | - How to provide RESTful Create, Read, Update, and Delete (CRUD) operations to change domain objects
11 | - How to create unit, integration, and acceptance tests that exercise a REST API
12 |
13 | ## Starting the Order Management System
14 | To start this web service, install [Maven](https://maven.apache.org/install.html) and execute the following command
15 |
16 | mvn spring-boot:run
17 |
18 | Once the web service is started, it can be reached at
19 |
20 | http://localhost:8080/order
21 |
22 | ## REST Endpoints
23 | The following REST endpoints are available upon deployment of the order management system:
24 |
25 | | HTTP Verb | URL | Description | Status Codes |
26 | | ------------- |-------------|:-----| ----|
27 | | `GET` | `http://localhost:8080/order` | Obtains a list of all existing orders | |
28 | | `GET` | `http://localhost:8080/order/{id}` | Obtains the order corresponding to the supplied order ID | - `200 OK` if order exists
- `404 Not Found` if order does not exist
|
29 | | `POST` | `http://localhost:8080/order` | Creates a new order based on the payload contained in the request body | - `201 Created` if order successfully created
|
30 | | `PUT` | `http://localhost:8080/order/{id}` | Updated an existing order with the data contained in the request body | - `200 OK` if order succesfully updated
- `404 Not Found` if order does not exist
|
31 | | `DELETE` | `http://localhost:8080/order/{id}` | Deletes an existing order that corresponds to the supplied order ID | - `204 No Content` if order succesfully deleted
- `404 Not Found` if order does not exist
|
32 |
--------------------------------------------------------------------------------
/src/test/java/com/dzone/albanoj2/example/rest/test/acceptance/step/OrderSteps.java:
--------------------------------------------------------------------------------
1 | package com.dzone.albanoj2.example.rest.test.acceptance.step;
2 |
3 | import java.util.Map;
4 |
5 | import org.junit.Assert;
6 |
7 | import com.dzone.albanoj2.example.rest.test.acceptance.util.AbstractSteps;
8 | import com.fasterxml.jackson.core.type.TypeReference;
9 |
10 | import cucumber.api.java.en.And;
11 | import cucumber.api.java.en.Given;
12 | import cucumber.api.java.en.Then;
13 | import cucumber.api.java.en.When;
14 |
15 | public class OrderSteps extends AbstractSteps {
16 |
17 | private static final String TEST_ORDER = "{\"description\": \"some test order\", \"lineItems\": [{\"name\": \"test item 1\", \"description\": \"some test item 1\", \"costInCents\": 100}, {\"name\": \"test item 2\", \"description\": \"some test item 2\", \"costInCents\": 200}]}";
18 | private static final TypeReference