diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/Address.java b/core-java-8/src/main/java/com/baeldung/java_8_features/Address.java new file mode 100644 index 0000000000..1f89503288 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/Address.java @@ -0,0 +1,14 @@ +package com.baeldung.java_8_features; + +public class Address { + + private String street; + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/CustomException.java b/core-java-8/src/main/java/com/baeldung/java_8_features/CustomException.java new file mode 100644 index 0000000000..ff9be6ab06 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/CustomException.java @@ -0,0 +1,4 @@ +package com.baeldung.java_8_features; + +public class CustomException extends RuntimeException { +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/Detail.java b/core-java-8/src/main/java/com/baeldung/java_8_features/Detail.java new file mode 100644 index 0000000000..811937dba7 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/Detail.java @@ -0,0 +1,13 @@ +package com.baeldung.java_8_features; + +import java.util.Arrays; +import java.util.List; + +public class Detail { + + private static final List PARTS = Arrays.asList("turbine", "pump"); + + public List getParts() { + return PARTS; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalAddress.java b/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalAddress.java new file mode 100644 index 0000000000..8d6c517ac5 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalAddress.java @@ -0,0 +1,16 @@ +package com.baeldung.java_8_features; + +import java.util.Optional; + +public class OptionalAddress { + + private String street; + + public Optional getStreet() { + return Optional.ofNullable(street); + } + + public void setStreet(String street) { + this.street = street; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalUser.java b/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalUser.java new file mode 100644 index 0000000000..ff06cd21d6 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/OptionalUser.java @@ -0,0 +1,16 @@ +package com.baeldung.java_8_features; + +import java.util.Optional; + +public class OptionalUser { + + private OptionalAddress address; + + public Optional getAddress() { + return Optional.of(address); + } + + public void setAddress(OptionalAddress address) { + this.address = address; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/User.java b/core-java-8/src/main/java/com/baeldung/java_8_features/User.java new file mode 100644 index 0000000000..3708d276c8 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/User.java @@ -0,0 +1,40 @@ +package com.baeldung.java_8_features; + +import java.util.Optional; + +public class User { + + private String name; + + private Address address; + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public User() { + } + + public User(String name) { + this.name = name; + } + + public static boolean isRealUser(User user) { + return true; + } + + public String getOrThrow() { + String value = null; + Optional valueOpt = Optional.ofNullable(value); + String result = valueOpt.orElseThrow(CustomException::new).toUpperCase(); + return result; + } + + public boolean isLegalName(String name) { + return name.length() > 3 && name.length() < 16; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/Vehicle.java b/core-java-8/src/main/java/com/baeldung/java_8_features/Vehicle.java new file mode 100644 index 0000000000..011173bcaf --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/Vehicle.java @@ -0,0 +1,18 @@ +package com.baeldung.java_8_features; + +public interface Vehicle { + + void moveTo(long altitude, long longitude); + + static String producer() { + return "N&F Vehicles"; + } + + default long[] startPosition() { + return new long[]{23, 15}; + } + + default String getOverview() { + return "ATV made by " + producer(); + } +} diff --git a/core-java-8/src/main/java/com/baeldung/java_8_features/VehicleImpl.java b/core-java-8/src/main/java/com/baeldung/java_8_features/VehicleImpl.java new file mode 100644 index 0000000000..83e55f5f4d --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/java_8_features/VehicleImpl.java @@ -0,0 +1,9 @@ +package com.baeldung.java_8_features; + +public class VehicleImpl implements Vehicle { + + @Override + public void moveTo(long altitude, long longitude) { + //do nothing + } +} diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8DefaultStaticIntefaceMethodsTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8DefaultStaticIntefaceMethodsTest.java new file mode 100644 index 0000000000..21a5e34b9b --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/java8/Java8DefaultStaticIntefaceMethodsTest.java @@ -0,0 +1,27 @@ +package com.baeldung.java8; + +import com.baeldung.java_8_features.Vehicle; +import com.baeldung.java_8_features.VehicleImpl; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Java8DefaultStaticIntefaceMethodsTest { + + @Test + public void callStaticInterfaceMethdosMethods_whenExpectedResults_thenCorrect() { + Vehicle vehicle = new VehicleImpl(); + String overview = vehicle.getOverview(); + long[] startPosition = vehicle.startPosition(); + + assertEquals(overview, "ATV made by N&F Vehicles"); + assertEquals(startPosition[0], 23); + assertEquals(startPosition[1], 15); + } + + @Test + public void callDefaultInterfaceMethods_whenExpectedResults_thenCorrect() { + String producer = Vehicle.producer(); + assertEquals(producer, "N&F Vehicles"); + } +} diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8MethodReferenceTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8MethodReferenceTest.java new file mode 100644 index 0000000000..d9d88c5052 --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/java8/Java8MethodReferenceTest.java @@ -0,0 +1,67 @@ +package com.baeldung.java8; + +import com.baeldung.java_8_features.User; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class Java8MethodReferenceTest { + + private List list; + + @Before + public void init() { + list = new ArrayList<>(); + list.add("One"); + list.add("OneAndOnly"); + list.add("Derek"); + list.add("Change"); + list.add("factory"); + list.add("justBefore"); + list.add("Italy"); + list.add("Italy"); + list.add("Thursday"); + list.add(""); + list.add(""); + } + + @Test + public void checkStaticMethodReferences_whenWork_thenCorrect() { + + List users = new ArrayList<>(); + users.add(new User()); + users.add(new User()); + boolean isReal = users.stream().anyMatch(u -> User.isRealUser(u)); + boolean isRealRef = users.stream().anyMatch(User::isRealUser); + assertTrue(isReal); + assertTrue(isRealRef); + } + + @Test + public void checkInstanceMethodReferences_whenWork_thenCorrect() { + User user = new User(); + boolean isLegalName = list.stream().anyMatch(user::isLegalName); + assertTrue(isLegalName); + } + + @Test + public void checkParticularTypeReferences_whenWork_thenCorrect() { + long count = list.stream().filter(String::isEmpty).count(); + assertEquals(count, 2); + } + + @Test + public void checkConstructorReferences_whenWork_thenCorrect() { + Stream stream = list.stream().map(User::new); + List userList = stream.collect(Collectors.toList()); + assertEquals(userList.size(), list.size()); + assertTrue(userList.get(0) instanceof User); + } +} diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8OptionalTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8OptionalTest.java new file mode 100644 index 0000000000..26de39bc0e --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/java8/Java8OptionalTest.java @@ -0,0 +1,118 @@ +package com.baeldung.java8; + +import com.baeldung.java_8_features.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class Java8OptionalTest { + + private List list; + + @Before + public void init() { + list = new ArrayList<>(); + list.add("One"); + list.add("OneAndOnly"); + list.add("Derek"); + list.add("Change"); + list.add("factory"); + list.add("justBefore"); + list.add("Italy"); + list.add("Italy"); + list.add("Thursday"); + list.add(""); + list.add(""); + } + + @Test + public void checkOptional_whenAsExpected_thenCorrect() { + + Optional optionalEmpty = Optional.empty(); + assertFalse(optionalEmpty.isPresent()); + + String str = "value"; + Optional optional = Optional.of(str); + assertEquals(optional.get(), "value"); + + Optional optionalNullable = Optional.ofNullable(str); + Optional optionalNull = Optional.ofNullable(null); + assertEquals(optionalNullable.get(), "value"); + assertFalse(optionalNull.isPresent()); + + List listOpt = Optional.of(list).orElse(new ArrayList<>()); + List listNull = null; + List listOptNull = Optional.ofNullable(listNull).orElse(new ArrayList<>()); + assertTrue(listOpt == list); + assertTrue(listOptNull.isEmpty()); + + Optional user = Optional.ofNullable(getUser()); + String result = user.map(User::getAddress) + .map(Address::getStreet) + .orElse("not specified"); + assertEquals(result, "1st Avenue"); + + Optional optionalUser = Optional.ofNullable(getOptionalUser()); + String resultOpt = optionalUser.flatMap(OptionalUser::getAddress) + .flatMap(OptionalAddress::getStreet) + .orElse("not specified"); + assertEquals(resultOpt, "1st Avenue"); + + Optional userNull = Optional.ofNullable(getUserNull()); + String resultNull = userNull.map(User::getAddress) + .map(Address::getStreet) + .orElse("not specified"); + assertEquals(resultNull, "not specified"); + + Optional optionalUserNull = Optional.ofNullable(getOptionalUserNull()); + String resultOptNull = optionalUserNull.flatMap(OptionalUser::getAddress) + .flatMap(OptionalAddress::getStreet) + .orElse("not specified"); + assertEquals(resultOptNull, "not specified"); + + } + + @Test(expected = CustomException.class) + public void callMethod_whenCustomException_thenCorrect() { + User user = new User(); + String result = user.getOrThrow(); + } + + private User getUser() { + User user = new User(); + Address address = new Address(); + address.setStreet("1st Avenue"); + user.setAddress(address); + return user; + } + + private OptionalUser getOptionalUser() { + OptionalUser user = new OptionalUser(); + OptionalAddress address = new OptionalAddress(); + address.setStreet("1st Avenue"); + user.setAddress(address); + return user; + } + + private OptionalUser getOptionalUserNull() { + OptionalUser user = new OptionalUser(); + OptionalAddress address = new OptionalAddress(); + address.setStreet(null); + user.setAddress(address); + return user; + } + + private User getUserNull() { + User user = new User(); + Address address = new Address(); + address.setStreet(null); + user.setAddress(address); + return user; + } + +} diff --git a/core-java-8/src/test/java/com/baeldung/java8/Java8StreamsTest.java b/core-java-8/src/test/java/com/baeldung/java8/Java8StreamsTest.java new file mode 100644 index 0000000000..1f1dda49ce --- /dev/null +++ b/core-java-8/src/test/java/com/baeldung/java8/Java8StreamsTest.java @@ -0,0 +1,113 @@ +package com.baeldung.java8; + +import com.baeldung.java_8_features.Detail; +import org.junit.Before; +import org.junit.Test; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.*; + +public class Java8StreamsTest { + + private List list; + + @Before + public void init() { + list = new ArrayList<>(); + list.add("One"); + list.add("OneAndOnly"); + list.add("Derek"); + list.add("Change"); + list.add("factory"); + list.add("justBefore"); + list.add("Italy"); + list.add("Italy"); + list.add("Thursday"); + list.add(""); + list.add(""); + } + + @Test + public void checkStreamCount_whenCreating_givenDifferentSources() { + String[] arr = new String[]{"a", "b", "c"}; + Stream streamArr = Arrays.stream(arr); + assertEquals(streamArr.count(), 3); + + Stream streamOf = Stream.of("a", "b", "c"); + assertEquals(streamOf.count(), 3); + + long count = list.stream().distinct().count(); + assertEquals(count, 9); + } + + + @Test + public void checkStreamCount_whenOperationFilter_thanCorrect() { + Stream streamFilter = list.stream().filter(element -> element.isEmpty()); + assertEquals(streamFilter.count(), 2); + } + + + @Test + public void checkStreamCount_whenOperationMap_thanCorrect() { + List uris = new ArrayList<>(); + uris.add("C:\\My.txt"); + Stream streamMap = uris.stream().map(uri -> Paths.get(uri)); + assertEquals(streamMap.count(), 1); + + List details = new ArrayList<>(); + details.add(new Detail()); + details.add(new Detail()); + Stream streamFlatMap = details.stream() + .flatMap(detail -> detail.getParts().stream()); + assertEquals(streamFlatMap.count(), 4); + } + + + @Test + public void checkStreamCount_whenOperationMatch_thenCorrect() { + boolean isValid = list.stream().anyMatch(element -> element.contains("h")); + boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); + boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); + assertTrue(isValid); + assertFalse(isValidOne); + assertFalse(isValidTwo); + } + + + @Test + public void checkStreamReducedValue_whenOperationReduce_thenCorrect() { + List integers = new ArrayList<>(); + integers.add(1); + integers.add(1); + integers.add(1); + Integer reduced = integers.stream().reduce(23, (a, b) -> a + b); + assertTrue(reduced == 26); + } + + @Test + public void checkStreamContains_whenOperationCollect_thenCorrect() { + List resultList = list.stream() + .map(element -> element.toUpperCase()) + .collect(Collectors.toList()); + assertEquals(resultList.size(), list.size()); + assertTrue(resultList.contains("")); + } + + + @Test + public void checkParallelStream_whenDoWork() { + list.parallelStream().forEach(element -> doWork(element)); + } + + private void doWork(String string) { + assertTrue(true); //just imitate an amount of work + } +} diff --git a/jackson/pom.xml b/jackson/pom.xml index f63ec08b48..c7cd172757 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung jackson @@ -22,6 +22,12 @@ commons-io 2.4 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.7.4 + org.apache.commons @@ -55,12 +61,12 @@ com.fasterxml.jackson.datatype jackson-datatype-joda ${jackson.version} - + - - com.fasterxml.jackson.module - jackson-module-jsonSchema - 2.7.2 + + com.fasterxml.jackson.module + jackson-module-jsonSchema + 2.7.2 diff --git a/jackson/src/test/java/com/baeldung/jackson/xml/TestXMLSerializeDeserialize.java b/jackson/src/test/java/com/baeldung/jackson/xml/TestXMLSerializeDeserialize.java new file mode 100644 index 0000000000..92d0bd13d4 --- /dev/null +++ b/jackson/src/test/java/com/baeldung/jackson/xml/TestXMLSerializeDeserialize.java @@ -0,0 +1,86 @@ +package com.baeldung.jackson.xml; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; + +public class TestXMLSerializeDeserialize { + + @Test + public void whenJavaSerializedToXmlStr_thenCorrect() throws JsonProcessingException { + XmlMapper xmlMapper = new XmlMapper(); + String xml = xmlMapper.writeValueAsString(new SimpleBean()); + assertNotNull(xml); + } + + @Test + public void whenJavaSerializedToXmlFile_thenCorrect() throws IOException { + XmlMapper xmlMapper = new XmlMapper(); + xmlMapper.writeValue(new File("target/simple_bean.xml"), new SimpleBean()); + File file = new File("target/simple_bean.xml"); + assertNotNull(file); + } + + @Test + public void whenJavaGotFromXmlStr_thenCorrect() throws IOException { + XmlMapper xmlMapper = new XmlMapper(); + SimpleBean value = xmlMapper.readValue( + "12", SimpleBean.class); + assertTrue(value.getX() == 1 && value.getY() == 2); + } + + @Test + public void whenJavaGotFromXmlFile_thenCorrect() throws IOException { + File file = new File("src/test/resources/simple_bean.xml"); + XmlMapper xmlMapper = new XmlMapper(); + String xml = inputStreamToString(new FileInputStream(file)); + SimpleBean value = xmlMapper.readValue(xml, SimpleBean.class); + assertTrue(value.getX() == 1 && value.getY() == 2); + } + + private static String inputStreamToString(InputStream is) throws IOException { + BufferedReader br; + StringBuilder sb = new StringBuilder(); + + String line; + br = new BufferedReader(new InputStreamReader(is)); + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + return sb.toString(); + } +} + +class SimpleBean { + private int x = 1; + private int y = 2; + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + +} diff --git a/jackson/src/test/resources/simple_bean.xml b/jackson/src/test/resources/simple_bean.xml new file mode 100644 index 0000000000..7829ea35a4 --- /dev/null +++ b/jackson/src/test/resources/simple_bean.xml @@ -0,0 +1,4 @@ + + 1 + 2 + \ No newline at end of file diff --git a/junit5/pom.xml b/junit5/pom.xml new file mode 100644 index 0000000000..5a2ea61668 --- /dev/null +++ b/junit5/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.baeldung + junit5 + 1.0-SNAPSHOT + + junit5 + Intro to JUnit 5 + + + UTF-8 + 1.8 + + 5.0.0-SNAPSHOT + + + + + snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + always + true + + + + + + + snapshots-repo + https://oss.sonatype.org/content/repositories/snapshots + + false + + + + always + true + + + + + + + + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 2.19 + + + org.junit + surefire-junit5 + ${junit.gen5.version} + + + + + + + + + org.junit + junit5-api + ${junit.gen5.version} + test + + + \ No newline at end of file diff --git a/junit5/src/test/java/com/baeldung/AssumptionTest.java b/junit5/src/test/java/com/baeldung/AssumptionTest.java new file mode 100644 index 0000000000..e4c2b56124 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/AssumptionTest.java @@ -0,0 +1,30 @@ +package com.baeldung; + +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assertions.assertEquals; +import static org.junit.gen5.api.Assumptions.*; + +public class AssumptionTest { + + @Test + void trueAssumption() { + assumeTrue(5 > 1); + assertEquals(5 + 2, 7); + } + + @Test + void falseAssumption() { + assumeFalse(5 < 1); + assertEquals(5 + 2, 7); + } + + @Test + void assumptionThat() { + String someString = "Just a string"; + assumingThat( + someString.equals("Just a string"), + () -> assertEquals(2 + 2, 4) + ); + } +} diff --git a/junit5/src/test/java/com/baeldung/ExceptionTest.java b/junit5/src/test/java/com/baeldung/ExceptionTest.java new file mode 100644 index 0000000000..5c30ad5b44 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/ExceptionTest.java @@ -0,0 +1,18 @@ +package com.baeldung; + +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assertions.assertEquals; +import static org.junit.gen5.api.Assertions.expectThrows; + +public class ExceptionTest { + + @Test + void shouldThrowException() { + Throwable exception = expectThrows(UnsupportedOperationException.class, + () -> { + throw new UnsupportedOperationException("Not supported"); + }); + assertEquals(exception.getMessage(), "Not supported"); + } +} diff --git a/junit5/src/test/java/com/baeldung/FirstTest.java b/junit5/src/test/java/com/baeldung/FirstTest.java new file mode 100644 index 0000000000..4fc6037174 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/FirstTest.java @@ -0,0 +1,37 @@ +package com.baeldung; + +import org.junit.gen5.api.Disabled; +import org.junit.gen5.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.gen5.api.Assertions.*; + +class FirstTest { + + @Test + void lambdaExpressions() { + List numbers = Arrays.asList(1, 2, 3); + assertTrue(numbers + .stream() + .mapToInt(i -> i) + .sum() > 5, "Sum should be greater than 5"); + } + + @Test + void groupAssertions() { + int[] numbers = {0, 1, 2, 3, 4}; + assertAll("numbers", + () -> assertEquals(numbers[0], 1), + () -> assertEquals(numbers[3], 3), + () -> assertEquals(numbers[4], 1) + ); + } + + @Test + @Disabled + void disabledTest() { + assertTrue(false); + } +} diff --git a/junit5/src/test/java/com/baeldung/NestedTest.java b/junit5/src/test/java/com/baeldung/NestedTest.java new file mode 100644 index 0000000000..3fbe4f8644 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/NestedTest.java @@ -0,0 +1,77 @@ +package com.baeldung; + +import org.junit.gen5.api.*; + +import java.util.EmptyStackException; +import java.util.Stack; + +public class NestedTest { + Stack stack; + boolean isRun = false; + + @Test + @DisplayName("is instantiated with new Stack()") + void isInstantiatedWithNew() { + new Stack(); + } + + @Nested + @DisplayName("when new") + class WhenNew { + + @BeforeEach + void init() { + stack = new Stack(); + } + + @Test + @DisplayName("is empty") + void isEmpty() { + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + @DisplayName("throws EmptyStackException when popped") + void throwsExceptionWhenPopped() { + Assertions.expectThrows(EmptyStackException.class, () -> stack.pop()); + } + + @Test + @DisplayName("throws EmptyStackException when peeked") + void throwsExceptionWhenPeeked() { + Assertions.expectThrows(EmptyStackException.class, () -> stack.peek()); + } + + @Nested + @DisplayName("after pushing an element") + class AfterPushing { + + String anElement = "an element"; + + @BeforeEach + void init() { + stack.push(anElement); + } + + @Test + @DisplayName("it is no longer empty") + void isEmpty() { + Assertions.assertFalse(stack.isEmpty()); + } + + @Test + @DisplayName("returns the element when popped and is empty") + void returnElementWhenPopped() { + Assertions.assertEquals(anElement, stack.pop()); + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + @DisplayName("returns the element when peeked but remains not empty") + void returnElementWhenPeeked() { + Assertions.assertEquals(anElement, stack.peek()); + Assertions.assertFalse(stack.isEmpty()); + } + } + } +} diff --git a/junit5/src/test/java/com/baeldung/TaggedTest.java b/junit5/src/test/java/com/baeldung/TaggedTest.java new file mode 100644 index 0000000000..dbc82f4022 --- /dev/null +++ b/junit5/src/test/java/com/baeldung/TaggedTest.java @@ -0,0 +1,16 @@ +package com.baeldung; + +import org.junit.gen5.api.Tag; +import org.junit.gen5.api.Test; + +import static org.junit.gen5.api.Assertions.assertEquals; + +@Tag("Test case") +public class TaggedTest { + + @Test + @Tag("Method") + void testMethod() { + assertEquals(2 + 2, 4); + } +} diff --git a/spring-autowire/pom.xml b/spring-autowire/pom.xml new file mode 100644 index 0000000000..e28efdae61 --- /dev/null +++ b/spring-autowire/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + + com.baeldung + spring-autowire + 0.0.1-SNAPSHOT + jar + + spring-autowire + http://maven.apache.org + + + UTF-8 + 4.2.5.RELEASE + 3.5.1 + 2.19.1 + + + + + junit + junit + 4.11 + + + org.springframework + spring-core + ${org.springframework.version} + + + org.springframework + spring-context + ${org.springframework.version} + + + org.springframework + spring-test + ${org.springframework.version} + test + + + + + spring-autowire + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java new file mode 100644 index 0000000000..725a9b8406 --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/App.java @@ -0,0 +1,11 @@ +package com.baeldung.autowire.sample; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class App { + public static void main(String[] args) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); + FooService fooService = ctx.getBean(FooService.class); + fooService.doStuff(); + } +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java new file mode 100644 index 0000000000..f948e2bf8e --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/AppConfig.java @@ -0,0 +1,10 @@ +package com.baeldung.autowire.sample; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan("com.baeldung.autowire.sample") +public class AppConfig { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java new file mode 100644 index 0000000000..7aa820adef --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Bar.java @@ -0,0 +1,5 @@ +package com.baeldung.autowire.sample; + +public class Bar { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java new file mode 100644 index 0000000000..fb704453fc --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/BarFormatter.java @@ -0,0 +1,13 @@ +package com.baeldung.autowire.sample; + +import org.springframework.stereotype.Component; + +@FormatterType("Bar") +@Component +public class BarFormatter implements Formatter { + + public String format() { + return "bar"; + } + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java new file mode 100644 index 0000000000..b587ab38b8 --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Foo.java @@ -0,0 +1,5 @@ +package com.baeldung.autowire.sample; + +public class Foo { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java new file mode 100644 index 0000000000..aec26202ab --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooDAO.java @@ -0,0 +1,5 @@ +package com.baeldung.autowire.sample; + +public class FooDAO { + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java new file mode 100644 index 0000000000..73966e9e43 --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooFormatter.java @@ -0,0 +1,13 @@ +package com.baeldung.autowire.sample; + +import org.springframework.stereotype.Component; + +@FormatterType("Foo") +@Component +public class FooFormatter implements Formatter { + + public String format() { + return "foo"; + } + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java new file mode 100644 index 0000000000..eccea4351a --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FooService.java @@ -0,0 +1,17 @@ +package com.baeldung.autowire.sample; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FooService { + + @Autowired + @FormatterType("Foo") + private Formatter formatter; + + public String doStuff(){ + return formatter.format(); + } + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java new file mode 100644 index 0000000000..83716d6e92 --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/Formatter.java @@ -0,0 +1,7 @@ +package com.baeldung.autowire.sample; + +public interface Formatter { + + String format(); + +} diff --git a/spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java new file mode 100644 index 0000000000..7ffc308c9a --- /dev/null +++ b/spring-autowire/src/main/java/com/baeldung/autowire/sample/FormatterType.java @@ -0,0 +1,17 @@ +package com.baeldung.autowire.sample; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Qualifier; + +@Qualifier +@Target({ElementType.FIELD, ElementType.METHOD,ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface FormatterType { + + String value(); + +} diff --git a/spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceTest.java b/spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceTest.java new file mode 100644 index 0000000000..4607f527d9 --- /dev/null +++ b/spring-autowire/src/test/java/com/baeldung/autowire/sample/FooServiceTest.java @@ -0,0 +1,22 @@ +package com.baeldung.autowire.sample; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes=AppConfig.class, loader=AnnotationConfigContextLoader.class) +public class FooServiceTest { + + @Autowired + FooService fooService; + + @Test + public void whenFooFormatterType_thenReturnFoo(){ + Assert.assertEquals("foo", fooService.doStuff()); + } +} diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java index c9e846905a..ef8d1214df 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java @@ -12,6 +12,7 @@ import org.springframework.web.context.support.ServletContextResource; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -41,17 +42,13 @@ public class ImageController { } @RequestMapping(value = "/image-response-entity", method = RequestMethod.GET) - public ResponseEntity getImageAsResponseEntity() { + public ResponseEntity getImageAsResponseEntity() throws IOException { ResponseEntity responseEntity; final HttpHeaders headers = new HttpHeaders(); - try { - final InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg"); - byte[] media = IOUtils.toByteArray(in); - headers.setCacheControl(CacheControl.noCache().getHeaderValue()); - responseEntity = new ResponseEntity<>(media, headers, HttpStatus.OK); - } catch (IOException ioe) { - responseEntity = new ResponseEntity<>(null, headers, HttpStatus.INTERNAL_SERVER_ERROR); - } + final InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg"); + byte[] media = IOUtils.toByteArray(in); + headers.setCacheControl(CacheControl.noCache().getHeaderValue()); + responseEntity = new ResponseEntity<>(media, headers, HttpStatus.OK); return responseEntity; } diff --git a/spring-rest-docs/pom.xml b/spring-rest-docs/pom.xml index decdd3a5df..04ee11d0de 100644 --- a/spring-rest-docs/pom.xml +++ b/spring-rest-docs/pom.xml @@ -21,6 +21,7 @@ UTF-8 1.8 + ${project.build.directory}/generated-snippets @@ -44,16 +45,53 @@ 1.0.1.RELEASE test + + com.jayway.jsonpath + json-path + 2.0.0 + - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*Documentation.java + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.2 + + + generate-docs + package + + process-asciidoc + + + html + book + + ${snippetsDirectory} + + src/docs/asciidocs + target/generated-docs + + + + + + diff --git a/spring-rest-docs/src/docs/asciidocs/api-guide.adoc b/spring-rest-docs/src/docs/asciidocs/api-guide.adoc new file mode 100644 index 0000000000..9fbe74c072 --- /dev/null +++ b/spring-rest-docs/src/docs/asciidocs/api-guide.adoc @@ -0,0 +1,203 @@ += RESTful Notes API Guide +Baeldung; +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 4 +:sectlinks: + +[[overview]] += Overview + +[[overview-http-verbs]] +== HTTP verbs + +RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its +use of HTTP verbs. + +|=== +| Verb | Usage + +| `GET` +| Used to retrieve a resource + +| `POST` +| Used to create a new resource + +| `PATCH` +| Used to update an existing resource, including partial updates + +| `DELETE` +| Used to delete an existing resource +|=== + +RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its +use of HTTP status codes. + +|=== +| Status code | Usage + +| `200 OK` +| The request completed successfully + +| `201 Created` +| A new resource has been created successfully. The resource's URI is available from the response's +`Location` header + +| `204 No Content` +| An update to an existing resource has been applied successfully + +| `400 Bad Request` +| The request was malformed. The response body will include an error providing further information + +| `404 Not Found` +| The requested resource did not exist +|=== + +[[overview-headers]] +== Headers + +Every response has the following header(s): + +include::{snippets}/headers-example/response-headers.adoc[] + +[[overview-hypermedia]] +== Hypermedia + +RESTful Notes uses hypermedia and resources include links to other resources in their +responses. Responses are in http://stateless.co/hal_specification.html[Hypertext Application +from resource to resource. +Language (HAL)] format. Links can be found beneath the `_links` key. Users of the API should +not create URIs themselves, instead they should use the above-described links to navigate + +[[resources]] += Resources + + + +[[resources-index]] +== Index + +The index provides the entry point into the service. + +[[resources-index-access]] +=== Accessing the index + +A `GET` request is used to access the index + +==== Response structure + +include::{snippets}/index-example/http-response.adoc[] + +==== Example response + +include::{snippets}/index-example/http-response.adoc[] + +==== Example request + +include::{snippets}/index-example/http-request.adoc[] + +==== CURL request + +include::{snippets}/index-example/curl-request.adoc[] + +[[resources-index-links]] +==== Links + +include::{snippets}/index-example/links.adoc[] + + +[[resources-CRUD]] +== CRUD REST Service + +The CRUD provides the entry point into the service. + +[[resources-crud-access]] +=== Accessing the crud GET + +A `GET` request is used to access the CRUD read + +==== Response structure + +include::{snippets}/crud-get-example/http-request.adoc[] + +==== Example response + +include::{snippets}/crud-get-example/http-response.adoc[] + +==== CURL request + +include::{snippets}/crud-get-example/curl-request.adoc[] + +[[resources-crud-access]] +=== Accessing the crud POST + +A `POST` request is used to access the CRUD create + +==== Response structure + +include::{snippets}/crud-create-example/http-request.adoc[] + +==== Example response + +include::{snippets}/crud-create-example/http-response.adoc[] + +==== CURL request + +include::{snippets}/crud-create-example/curl-request.adoc[] + +[[resources-crud-access]] +=== Accessing the crud DELETE + +A `DELETE` request is used to access the CRUD create + +==== Response structure + +include::{snippets}/crud-delete-example/http-request.adoc[] + +==== Example response + +include::{snippets}/crud-delete-example/http-response.adoc[] + +==== CURL request + +include::{snippets}/crud-delete-example/curl-request.adoc[] + +[[resources-crud-access]] +=== Accessing the crud PATCH + +A `PATCH` request is used to access the CRUD create + +==== Response structure + +include::{snippets}/crud-patch-example/http-request.adoc[] + +==== Example response + +include::{snippets}/crud-patch-example/http-response.adoc[] + +==== CURL request + +include::{snippets}/crud-patch-example/curl-request.adoc[] + +[[resources-crud-access]] +=== Accessing the crud PUT + +A `PUT` request is used to access the CRUD create + +==== Response structure + +include::{snippets}/crud-put-example/http-request.adoc[] + +==== Example response + +include::{snippets}/crud-put-example/http-response.adoc[] + +==== CURL request + +include::{snippets}/crud-put-example/curl-request.adoc[] + + + + diff --git a/spring-rest-docs/src/main/java/com/example/CRUDController.java b/spring-rest-docs/src/main/java/com/example/CRUDController.java new file mode 100644 index 0000000000..818b29d3a6 --- /dev/null +++ b/spring-rest-docs/src/main/java/com/example/CRUDController.java @@ -0,0 +1,55 @@ +package com.example; + +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/crud") +public class CRUDController { + + @RequestMapping(method=RequestMethod.GET) + @ResponseStatus(HttpStatus.OK) + public List read(@RequestBody CrudInput crudInput) { + List returnList=new ArrayList(); + returnList.add(crudInput); + return returnList; + } + + @ResponseStatus(HttpStatus.CREATED) + @RequestMapping(method=RequestMethod.POST) + public HttpHeaders save(@RequestBody CrudInput crudInput) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setLocation(linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri()); + return httpHeaders; + } + + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + @ResponseStatus(HttpStatus.OK) + HttpHeaders delete(@RequestBody CrudInput crudInput) { + HttpHeaders httpHeaders = new HttpHeaders(); + return httpHeaders; + } + + @RequestMapping(value = "/{id}", method = RequestMethod.PUT) + @ResponseStatus(HttpStatus.ACCEPTED) + void put(@PathVariable("id") long id, @RequestBody CrudInput crudInput) { + + } + + @RequestMapping(value = "/{id}", method = RequestMethod.PATCH) + @ResponseStatus(HttpStatus.NO_CONTENT) + void patch(@PathVariable("id") long id, @RequestBody CrudInput crudInput) { + + } +} diff --git a/spring-rest-docs/src/main/java/com/example/CrudInput.java b/spring-rest-docs/src/main/java/com/example/CrudInput.java new file mode 100644 index 0000000000..3d91b7d45a --- /dev/null +++ b/spring-rest-docs/src/main/java/com/example/CrudInput.java @@ -0,0 +1,42 @@ +package com.example; + +import java.net.URI; +import java.util.Collections; +import java.util.List; + +import org.hibernate.validator.constraints.NotBlank; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class CrudInput { + + //@NotBlank + private final String title; + + private final String body; + + private final List tagUris; + + @JsonCreator + public CrudInput(@JsonProperty("title") String title, + @JsonProperty("body") String body, @JsonProperty("tags") List tagUris) { + this.title = title; + this.body = body; + this.tagUris = tagUris == null ? Collections.emptyList() : tagUris; + } + + public String getTitle() { + return title; + } + + public String getBody() { + return body; + } + + @JsonProperty("tags") + public List getTagUris() { + return this.tagUris; + } + +} \ No newline at end of file diff --git a/spring-rest-docs/src/main/java/com/example/IndexController.java b/spring-rest-docs/src/main/java/com/example/IndexController.java index 92d987f05d..a6b4537c43 100644 --- a/spring-rest-docs/src/main/java/com/example/IndexController.java +++ b/spring-rest-docs/src/main/java/com/example/IndexController.java @@ -1,23 +1,22 @@ package com.example; +import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; + import org.springframework.hateoas.ResourceSupport; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; - @RestController -@RequestMapping("/api") +@RequestMapping("/") public class IndexController { - @RequestMapping(method = RequestMethod.GET) - public ResourceSupport index() { - ResourceSupport index = new ResourceSupport(); - index.add(linkTo(MyRestController.class).withRel("notes")); - index.add(linkTo(MyRestController.class).withRel("tags")); - return index; - } + @RequestMapping(method=RequestMethod.GET) + public ResourceSupport index() { + ResourceSupport index = new ResourceSupport(); + index.add(linkTo(CRUDController.class).withRel("crud")); + return index; + } } \ No newline at end of file diff --git a/spring-rest-docs/src/main/java/com/example/MyRestController.java b/spring-rest-docs/src/main/java/com/example/MyRestController.java deleted file mode 100644 index 896b82abfb..0000000000 --- a/spring-rest-docs/src/main/java/com/example/MyRestController.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.example; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/rest/api") -public class MyRestController { - - @RequestMapping(method = RequestMethod.GET) - public String index() { - return "Hello"; - } - -} diff --git a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java index da09f9accc..dd20ef324e 100644 --- a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java +++ b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java @@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringRestDocsApplication { - public static void main(String[] args) { - SpringApplication.run(SpringRestDocsApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(SpringRestDocsApplication.class, args); + } } diff --git a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java index 5490e90ff5..195b9dc514 100644 --- a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java +++ b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java @@ -1,28 +1,39 @@ package com.example; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.hateoas.MediaTypes; import org.springframework.restdocs.RestDocumentation; +import org.springframework.restdocs.constraints.ConstraintDescriptions; import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Collections.singletonList; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.key; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.util.StringUtils.collectionToDelimitedString; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringRestDocsApplication.class) @@ -35,6 +46,9 @@ public class ApiDocumentation { @Autowired private WebApplicationContext context; + @Autowired + private ObjectMapper objectMapper; + private RestDocumentationResultHandler document; private MockMvc mockMvc; @@ -42,25 +56,179 @@ public class ApiDocumentation { @Before public void setUp() { this.document = document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())); - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) - .alwaysDo(this.document).build(); + .alwaysDo(this.document) + .build(); + } + + + @Test + public void headersExample() throws Exception { + this.document.snippets(responseHeaders(headerWithName("Content-Type") + .description("The Content-Type of the payload, e.g. `application/hal+json`"))); + this.mockMvc.perform(get("/")) + .andExpect(status().isOk()); } @Test public void indexExample() throws Exception { this.document.snippets( - links( - linkWithRel("notes").description("The <>"), - linkWithRel("tags").description("The <>") - ), - responseFields(fieldWithPath("_links").description("<> to other resources"))); - - this.mockMvc.perform(get("/api")).andExpect(status().isOk()); + links(linkWithRel("crud").description("The <>")), + responseFields(fieldWithPath("_links").description("<> to other resources")) + ); + this.mockMvc.perform(get("/")) + .andExpect(status().isOk()); } + + @Test + public void crudGetExample() throws Exception { + + Map tag = new HashMap<>(); + tag.put("name", "GET"); + + String tagLocation = this.mockMvc.perform(get("/crud") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + this.mockMvc.perform(get("/crud") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(crud))) + .andExpect(status().isOk()); + } + + @Test + public void crudCreateExample() throws Exception { + Map tag = new HashMap<>(); + tag.put("name", "CREATE"); + + String tagLocation = this.mockMvc.perform(post("/crud") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isCreated()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + ConstrainedFields fields = new ConstrainedFields(CrudInput.class); + this.document.snippets(requestFields( + fields.withPath("title").description("The title of the note"), + fields.withPath("body").description("The body of the note"), + fields.withPath("tags").description("An array of tag resource URIs"))); + this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isCreated()); + + + } + + @Test + public void crudDeleteExample() throws Exception { + + Map tag = new HashMap<>(); + tag.put("name", "DELETE"); + + String tagLocation = this.mockMvc.perform(delete("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + this.mockMvc.perform(delete("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(crud))) + .andExpect(status().isOk()); + } + + @Test + public void crudPatchExample() throws Exception { + + Map tag = new HashMap<>(); + tag.put("name", "PATCH"); + + String tagLocation = this.mockMvc.perform(patch("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isNoContent()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + this.mockMvc.perform(patch("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(crud))) + .andExpect(status().isNoContent()); + } + + + @Test + public void crudPutExample() throws Exception { + Map tag = new HashMap<>(); + tag.put("name", "PUT"); + + String tagLocation = this.mockMvc.perform(put("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isAccepted()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + this.mockMvc.perform(put("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(crud))) + .andExpect(status().isAccepted()); + } + + @Test public void contextLoads() { } -} \ No newline at end of file + + private static class ConstrainedFields { + + private final ConstraintDescriptions constraintDescriptions; + + ConstrainedFields(Class input) { + this.constraintDescriptions = new ConstraintDescriptions(input); + } + + private FieldDescriptor withPath(String path) { + return fieldWithPath(path) + .attributes(key("constraints") + .value(collectionToDelimitedString(this.constraintDescriptions.descriptionsForProperty(path), ". "))); + } + } + + +} diff --git a/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java b/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java new file mode 100644 index 0000000000..7f3c4db1f9 --- /dev/null +++ b/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java @@ -0,0 +1,158 @@ +package com.example; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.hateoas.MediaTypes; +import org.springframework.restdocs.RestDocumentation; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class) +@WebAppConfiguration +public class GettingStartedDocumentation { + + @Rule + public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets"); + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private WebApplicationContext context; + + private MockMvc mockMvc; + + + @Before + public void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(documentationConfiguration(this.restDocumentation)) + .alwaysDo(document("{method-name}/{step}/", + preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()))) + .build(); + } + + @Test + public void index() throws Exception { + this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("_links.crud", is(notNullValue()))) + .andExpect(jsonPath("_links.crud", is(notNullValue()))); + } + +// String createNote() throws Exception { +// Map note = new HashMap(); +// note.put("title", "Note creation with cURL"); +// note.put("body", "An example of how to create a note using curl"); +// String noteLocation = this.mockMvc.perform(post("/crud") +// .contentType(MediaTypes.HAL_JSON) +// .content(objectMapper.writeValueAsString(note))) +// .andExpect(status().isCreated()) +// .andExpect(header().string("Location", notNullValue())) +// .andReturn() +// .getResponse() +// .getHeader("Location"); +// return noteLocation; +// } +// +// MvcResult getNote(String noteLocation) throws Exception { +// return this.mockMvc.perform(get(noteLocation)) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("title", is(notNullValue()))) +// .andExpect(jsonPath("body", is(notNullValue()))) +// .andExpect(jsonPath("_links.crud", is(notNullValue()))) +// .andReturn(); +// } +// +// +// String createTag() throws Exception, JsonProcessingException { +// Map tag = new HashMap(); +// tag.put("name", "getting-started"); +// String tagLocation = this.mockMvc.perform(post("/crud") +// .contentType(MediaTypes.HAL_JSON) +// .content(objectMapper.writeValueAsString(tag))) +// .andExpect(status().isCreated()) +// .andExpect(header().string("Location", notNullValue())) +// .andReturn() +// .getResponse() +// .getHeader("Location"); +// return tagLocation; +// } +// +// void getTag(String tagLocation) throws Exception { +// this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) +// .andExpect(jsonPath("name", is(notNullValue()))) +// .andExpect(jsonPath("_links.tagged-notes", is(notNullValue()))); +// } +// +// String createTaggedNote(String tag) throws Exception { +// Map note = new HashMap(); +// note.put("title", "Tagged note creation with cURL"); +// note.put("body", "An example of how to create a tagged note using cURL"); +// note.put("tags", Arrays.asList(tag)); +// +// String noteLocation = this.mockMvc.perform(post("/notes") +// .contentType(MediaTypes.HAL_JSON) +// .content(objectMapper.writeValueAsString(note))) +// .andExpect(status().isCreated()) +// .andExpect(header().string("Location", notNullValue())) +// .andReturn() +// .getResponse() +// .getHeader("Location"); +// return noteLocation; +// } +// +// void getTags(String noteTagsLocation) throws Exception { +// this.mockMvc.perform(get(noteTagsLocation)) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("_embedded.tags", hasSize(1))); +// } +// +// void tagExistingNote(String noteLocation, String tagLocation) throws Exception { +// Map update = new HashMap(); +// update.put("tags", Arrays.asList(tagLocation)); +// this.mockMvc.perform(patch(noteLocation) +// .contentType(MediaTypes.HAL_JSON) +// .content(objectMapper.writeValueAsString(update))) +// .andExpect(status().isNoContent()); +// } +// +// MvcResult getTaggedExistingNote(String noteLocation) throws Exception { +// return this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()).andReturn(); +// } +// +// void getTagsForExistingNote(String noteTagsLocation) throws Exception { +// this.mockMvc.perform(get(noteTagsLocation)) +// .andExpect(status().isOk()).andExpect(jsonPath("_embedded.tags", hasSize(1))); +// } +// +// private String getLink(MvcResult result, String rel) +// throws UnsupportedEncodingException { +// return JsonPath.parse(result.getResponse().getContentAsString()).read("_links." + rel + ".href"); +// } + +}