Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions web/src/main/java/com/rightmove/property/DisplayProperty.java
Original file line number Diff line number Diff line change
@@ -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);
}

}

}
5 changes: 5 additions & 0 deletions web/src/main/java/com/rightmove/property/PriceIndicator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.rightmove.property;

public enum PriceIndicator {
LOW,MEDIUM,HIGH
}
27 changes: 27 additions & 0 deletions web/src/main/java/com/rightmove/property/PropertyController.java
Original file line number Diff line number Diff line change
@@ -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<DisplayProperty> getPropertiesByPostcode(@RequestParam String postcode) {
return searchPropertyService.retrievePropertiesByPostcode(postcode);
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.rightmove.property;

import java.util.List;

public interface SearchPropertyService {
public List<DisplayProperty> retrievePropertiesByPostcode(String postcode);
}
Original file line number Diff line number Diff line change
@@ -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<DisplayProperty> 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;
}

}
68 changes: 68 additions & 0 deletions web/src/test/java/com/rightmove/EndToEndTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<DisplayProperty> 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);
}

}
Original file line number Diff line number Diff line change
@@ -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

}

}