diff --git a/web/src/main/java/com/rightmove/property/DisplayProperty.java b/web/src/main/java/com/rightmove/property/DisplayProperty.java new file mode 100644 index 0000000..e4149ee --- /dev/null +++ b/web/src/main/java/com/rightmove/property/DisplayProperty.java @@ -0,0 +1,83 @@ +package com.rightmove.property; + +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.rightmove.property.data.PropertyType; + +import java.util.Objects; + +public class DisplayProperty { + private final long id; + private final PriceIndicator priceIndicator; + private final String displayAddress; + private final PropertyType type; + + public DisplayProperty(Builder builder) { + this.id = builder.id; + this.priceIndicator = builder.priceIndicator; + this.displayAddress = builder.displayAddress; + this.type = builder.type; + } + + public long getId() { + return id; + } + + public PriceIndicator getPriceIndicator() { + return priceIndicator; + } + + public String getDisplayAddress() { + return displayAddress; + } + + public PropertyType getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DisplayProperty that = (DisplayProperty) o; + return id == that.id; + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder { + private long id; + private PriceIndicator priceIndicator; + private String displayAddress; + private PropertyType type; + + public Builder id(long id){ + this.id = id; + return this; + } + + public Builder priceIndicator(PriceIndicator priceIndicator){ + this.priceIndicator = priceIndicator; + return this; + } + + public Builder displayAddress(String address){ + this.displayAddress = address; + return this; + } + + public Builder propertyType(PropertyType type){ + this.type = type; + return this; + } + + public DisplayProperty build(){ + return new DisplayProperty(this); + } + + } + +} diff --git a/web/src/main/java/com/rightmove/property/PriceIndicator.java b/web/src/main/java/com/rightmove/property/PriceIndicator.java new file mode 100644 index 0000000..2bedb8a --- /dev/null +++ b/web/src/main/java/com/rightmove/property/PriceIndicator.java @@ -0,0 +1,5 @@ +package com.rightmove.property; + +public enum PriceIndicator { + LOW,MEDIUM,HIGH +} diff --git a/web/src/main/java/com/rightmove/property/PropertyController.java b/web/src/main/java/com/rightmove/property/PropertyController.java new file mode 100644 index 0000000..d8c6dad --- /dev/null +++ b/web/src/main/java/com/rightmove/property/PropertyController.java @@ -0,0 +1,27 @@ +package com.rightmove.property; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class PropertyController { + + private final SearchPropertyService searchPropertyService; + + @Autowired + public PropertyController(SearchPropertyService searchPropertyService) { + this.searchPropertyService = searchPropertyService; + } + + @GetMapping("/property") + public @ResponseBody + List getPropertiesByPostcode(@RequestParam String postcode) { + return searchPropertyService.retrievePropertiesByPostcode(postcode); + } + +} \ No newline at end of file diff --git a/web/src/main/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverter.java b/web/src/main/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverter.java new file mode 100644 index 0000000..b111c9a --- /dev/null +++ b/web/src/main/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverter.java @@ -0,0 +1,42 @@ +package com.rightmove.property; + +import com.rightmove.property.data.PropertyEntity; +import org.springframework.stereotype.Component; + +@Component +public class PropertyEntityToDisplayPropertyConverter { + + public PropertyEntityToDisplayPropertyConverter(){ } + + public DisplayProperty convert(PropertyEntity propertyEntity){ + + return new DisplayProperty.Builder() + .id(propertyEntity.getId()) + .priceIndicator( + calculatePriceIndicator(propertyEntity.getPrice())) + .displayAddress( + createDisplayAddress( + propertyEntity.getNumber().length(), + propertyEntity.getAddress(), + propertyEntity.getRegion(), + propertyEntity.getPostcode())) + .propertyType(propertyEntity.getType()) + .build(); + + } + + private PriceIndicator calculatePriceIndicator(Long price){ + if (price > 1000000){ + return PriceIndicator.HIGH; + } + if (price < 1000000 && price > 500000){ + return PriceIndicator.MEDIUM; + } + else return PriceIndicator.LOW; + } + + private String createDisplayAddress(Integer number, String address, String region, String postcode){ + return number + ", " + address + ", " + region + ", " + postcode; + } + +} diff --git a/web/src/main/java/com/rightmove/property/SearchPropertyService.java b/web/src/main/java/com/rightmove/property/SearchPropertyService.java new file mode 100644 index 0000000..6fb28ff --- /dev/null +++ b/web/src/main/java/com/rightmove/property/SearchPropertyService.java @@ -0,0 +1,7 @@ +package com.rightmove.property; + +import java.util.List; + +public interface SearchPropertyService { + public List retrievePropertiesByPostcode(String postcode); +} diff --git a/web/src/main/java/com/rightmove/property/SearchPropertyServiceImpl.java b/web/src/main/java/com/rightmove/property/SearchPropertyServiceImpl.java new file mode 100644 index 0000000..69fe96a --- /dev/null +++ b/web/src/main/java/com/rightmove/property/SearchPropertyServiceImpl.java @@ -0,0 +1,39 @@ +package com.rightmove.property; + +import com.rightmove.property.data.PropertyDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class SearchPropertyServiceImpl implements SearchPropertyService { + + private final PropertyDao propertyDao; + private final PropertyEntityToDisplayPropertyConverter converter; + + @Autowired + public SearchPropertyServiceImpl(PropertyDao propertyDao, PropertyEntityToDisplayPropertyConverter converter){ + this.propertyDao = propertyDao; + this.converter = converter; + } + + @Override + public List retrievePropertiesByPostcode(String postcode) { + if (isValidPostcode(postcode)){ + return propertyDao.getByPostcode(postcode) + .stream() + .map(converter::convert) + .collect(Collectors.toList()); + } else { + throw new RuntimeException("not valid postcode"); + } + } + + private boolean isValidPostcode(String postcode){ + if (postcode.length() > 100) return false; + return true; + } + +} diff --git a/web/src/test/java/com/rightmove/EndToEndTest.java b/web/src/test/java/com/rightmove/EndToEndTest.java new file mode 100644 index 0000000..c8e1a1c --- /dev/null +++ b/web/src/test/java/com/rightmove/EndToEndTest.java @@ -0,0 +1,68 @@ +package com.rightmove; + +import com.rightmove.property.data.PropertyDao; +import io.restassured.RestAssured; +import io.restassured.config.LogConfig; +import io.restassured.http.ContentType; +import io.restassured.path.json.JsonPath; +import org.apache.http.HttpStatus; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.junit4.SpringRunner; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndToEndTest { + + @Autowired + private PropertyDao propertyDao; + + @LocalServerPort + private int serverPort; + + @Before + public void setup() throws Exception { + RestAssured.port = serverPort; + RestAssured.config = RestAssured.config().logConfig(LogConfig.logConfig()); + } + + @Test + public void shouldFindPropertyByPostcode() { + + JsonPath jsonPath = given() + .when() + .accept(ContentType.JSON) + .queryParam("postcode", "MA12 3ZY") + .get("/property") + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .jsonPath(); + + assertThat(jsonPath.getList("properties")).hasSize(1); + assertThat(jsonPath.getLong("properties[0].id")).isEqualTo(3L); + } + + @Test + public void shouldNotFindPropertyWherePostcodeNotFound() throws Exception { + + JsonPath jsonPath = given() + .when() + .accept(ContentType.JSON) + .queryParam("postcode", "WA11 9RW") + .get("/property") + .then() + .statusCode(HttpStatus.SC_OK) + .extract() + .jsonPath(); + + assertThat(jsonPath.getList("properties")).hasSize(0); + } +} diff --git a/web/src/test/java/com/rightmove/property/PropertyControllerTest.java b/web/src/test/java/com/rightmove/property/PropertyControllerTest.java new file mode 100644 index 0000000..3763348 --- /dev/null +++ b/web/src/test/java/com/rightmove/property/PropertyControllerTest.java @@ -0,0 +1,59 @@ +package com.rightmove.property; + +import com.rightmove.property.data.PropertyType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.web.servlet.MockMvc; +import javax.servlet.http.HttpServletResponse; + +import java.util.ArrayList; +import java.util.List; + +import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +public class PropertyControllerTest { + + private MockMvc mockMvc; + private PropertyController propertyController; + private SearchPropertyService searchPropertyService; + + @BeforeEach + public void setUp() { + searchPropertyService = mock(SearchPropertyService.class); + propertyController = new PropertyController(searchPropertyService); + mockMvc = standaloneSetup(propertyController).build(); + } + + @Test + public void test1(){ + String postcode = "W1D 3QU"; + List properties = new ArrayList<>(); + properties.add(new DisplayProperty.Builder().id(1).displayAddress("1 Dragon Street " + postcode).priceIndicator(PriceIndicator.HIGH).propertyType(PropertyType.FLAT).build()); + when(searchPropertyService.retrievePropertiesByPostcode(any())).thenReturn(properties); + given(). + mockMvc(mockMvc) + .param("postcode", postcode) + .when() + .get("/property") + .then() + .statusCode(HttpServletResponse.SC_OK); + + } + + @Test + public void test2(){ + String postcode = "W1D 3QU"; + propertyController.getPropertiesByPostcode(postcode); + verify(searchPropertyService, times(1)).retrievePropertiesByPostcode(postcode); + } + + @Test + public void test3(){ + String postcode = ""; + propertyController.getPropertiesByPostcode(postcode); + verify(searchPropertyService, times(0)).retrievePropertiesByPostcode(postcode); + } + +} diff --git a/web/src/test/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverterTest.groovy b/web/src/test/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverterTest.groovy new file mode 100644 index 0000000..9807f75 --- /dev/null +++ b/web/src/test/java/com/rightmove/property/PropertyEntityToDisplayPropertyConverterTest.groovy @@ -0,0 +1,23 @@ +package com.rightmove.property + +import spock.lang.Specification +import spock.lang.Unroll + +class PropertyEntityToDisplayPropertyConverterTest extends Specification { + + @Unroll + def "when price is #price price indicator should be #priceIndicator"(){ + def propertyEntityToDisplayPropertyConverter = new PropertyEntityToDisplayPropertyConverter(); + + expect: + propertyEntityToDisplayPropertyConverter.calculatePriceIndicator(price) == priceIndicator + + where: + price || priceIndicator + 1200000 || PriceIndicator.HIGH + 800000 || PriceIndicator.MEDIUM + 100000 || PriceIndicator.LOW + + } + +}