ソースを参照

working stuff

Fabien Chéret 5 年 前
コミット
27626373f8

+ 17 - 0
pom.xml

@@ -14,6 +14,10 @@
 	<name>parking-toll</name>
 	<description>Demo project for Spring Boot</description>
 
+	<properties>
+		<lambok.version>1.18.12</lambok.version>
+	</properties>
+
 	<dependencies>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
@@ -23,6 +27,11 @@
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<version>${lambok.version}</version>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>
@@ -48,9 +57,17 @@
 				<configuration>
 					<source>11</source>
 					<target>11</target>
+					<annotationProcessorPaths>
+						<path>
+							<groupId>org.projectlombok</groupId>
+							<artifactId>lombok</artifactId>
+							<version>${lambok.version}</version>
+						</path>
+					</annotationProcessorPaths>
 				</configuration>
 			</plugin>
 		</plugins>
+
 	</build>
 
 </project>

+ 18 - 9
src/main/java/eu/fibane/parkingtoll/api/ParkingLotApi.java

@@ -9,34 +9,43 @@ import javax.validation.Valid;
 import java.util.Collection;
 import java.util.List;
 
-@RequestMapping(produces = { "application/json" },
-        consumes = { "application/json" })
 public interface ParkingLotApi {
 
-    @PostMapping(value = "/parking_lot")
+    @PostMapping(value = "/parking_lot",
+            produces = { "application/json" },
+            consumes = { "application/json" })
     ResponseEntity<ParkingLot> addParkingLot(@Valid @RequestBody ParkingLot parkingLotItem);
 
 
-    @DeleteMapping(value = "/parking_lot/{parkingLotId}")
+    @DeleteMapping(value = "/parking_lot/{parkingLotId}",
+            produces = { "application/json" },
+            consumes = { "application/json" })
     ResponseEntity<ParkingLot> parkingLotDeleteById(@PathVariable("parkingLotId") Long parkingLotId);
 
-    @GetMapping(value = "/parking_lot/{parkingLotId}")
+    @GetMapping(value = "/parking_lot/{parkingLotId}",
+            produces = { "application/json" })
     ResponseEntity<ParkingLot> parkingLotGetById(@PathVariable("parkingLotId") Long parkingLotId);
 
 
-    @DeleteMapping(value = "/parking_lot/{parkingLotId}/park")
+    @DeleteMapping(value = "/parking_lot/{parkingLotId}/park",
+            produces = { "application/json" })
     ResponseEntity<CarSlot> leaveParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem);
 
 
-    @PostMapping(value = "/parking_lot/{parkingLotId}/park")
+    @PostMapping(value = "/parking_lot/{parkingLotId}/park",
+            produces = { "application/json" },
+            consumes = { "application/json" })
     ResponseEntity<CarSlot> parkParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem);
 
 
-    @PutMapping(value = "/parking_lot")
+    @PutMapping(value = "/parking_lot",
+            produces = { "application/json" },
+            consumes = { "application/json" })
     ResponseEntity<ParkingLot> updateParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody ParkingLot parkingLotItem);
 
 
-    @GetMapping(value = "/parking_lot")
+    @GetMapping(value = "/parking_lot",
+            produces = { "application/json" })
     ResponseEntity<Collection<ParkingLot>> searchParkingLot(@Valid @RequestParam(value = "searchString", required = false) String searchString);
 
 }

+ 15 - 26
src/main/java/eu/fibane/parkingtoll/api/ParkingLotApiController.java

@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
-import java.io.IOException;
+import java.math.BigDecimal;
 import java.net.URI;
 import java.util.Collection;
 import java.util.stream.Collectors;
@@ -27,19 +27,18 @@ public class ParkingLotApiController implements ParkingLotApi {
     private static final Logger log = LoggerFactory.getLogger(ParkingLotApiController.class);
 
     private final ObjectMapper objectMapper;
-
     private final HttpServletRequest request;
-
-    @Autowired
     private PersistenceManager persistenceManager;
 
-    @org.springframework.beans.factory.annotation.Autowired
-    public ParkingLotApiController(ObjectMapper objectMapper, HttpServletRequest request) {
+    @Autowired
+    public ParkingLotApiController(ObjectMapper objectMapper, HttpServletRequest request, PersistenceManager persistenceManager) {
         this.objectMapper = objectMapper;
         this.request = request;
+        this.persistenceManager = persistenceManager;
     }
 
     public ResponseEntity<ParkingLot> addParkingLot(@Valid @RequestBody ParkingLot parkingLotItem) {
+
         persistenceManager.addParkingLot(parkingLotItem);
         return ResponseEntity.created(URI.create("/parking_lot/" + parkingLotItem.getId())).body(parkingLotItem);
     }
@@ -61,31 +60,21 @@ public class ParkingLotApiController implements ParkingLotApi {
     }
 
     public ResponseEntity<CarSlot> leaveParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
-        String accept = request.getHeader("Accept");
-        if (accept != null && accept.contains("application/json")) {
-            try {
-                return new ResponseEntity<CarSlot>(objectMapper.readValue("{  \"arrival_time\" : \"2000-01-23T04:56:07.000+00:00\",  \"slot\" : 15,  \"type\" : \"25kW charging point\",  \"parking_lot_id\" : 0}", CarSlot.class), HttpStatus.NOT_IMPLEMENTED);
-            } catch (IOException e) {
-                log.error("Couldn't serialize response for content type application/json", e);
-                return new ResponseEntity<CarSlot>(HttpStatus.INTERNAL_SERVER_ERROR);
-            }
-        }
 
-        return new ResponseEntity<CarSlot>(HttpStatus.NOT_IMPLEMENTED);
+        ParkingLot parkingLot = persistenceManager.getParkingLotById(parkingLotId);
+        CarSlot oldCarSlot = parkingLot.removeCar(carSlotItem);
+        BigDecimal fare = parkingLot.getPricingPolicy().computeFare(oldCarSlot.getArrivalTime(), oldCarSlot.getDepartureTime());
+        oldCarSlot.setPrice(fare);
+        return ResponseEntity.ok(oldCarSlot);
     }
 
     public ResponseEntity<CarSlot> parkParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
-        String accept = request.getHeader("Accept");
-        if (accept != null && accept.contains("application/json")) {
-            try {
-                return new ResponseEntity<CarSlot>(objectMapper.readValue("{  \"arrival_time\" : \"2000-01-23T04:56:07.000+00:00\",  \"slot\" : 15,  \"type\" : \"25kW charging point\",  \"parking_lot_id\" : 0}", CarSlot.class), HttpStatus.NOT_IMPLEMENTED);
-            } catch (IOException e) {
-                log.error("Couldn't serialize response for content type application/json", e);
-                return new ResponseEntity<CarSlot>(HttpStatus.INTERNAL_SERVER_ERROR);
-            }
-        }
+        //check if the asked parking exists
+        ParkingLot parkingLot = persistenceManager.getParkingLotById(parkingLotId);
+        carSlotItem.setParkingLotId(parkingLotId);
+        parkingLot.parkCar(carSlotItem);
 
-        return new ResponseEntity<CarSlot>(HttpStatus.NOT_IMPLEMENTED);
+        return ResponseEntity.ok(carSlotItem);
     }
 
     public ResponseEntity<ParkingLot> updateParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody ParkingLot parkingLotItem) {

+ 1 - 3
src/main/java/eu/fibane/parkingtoll/core/InMemoryPersistenceManager.java

@@ -20,10 +20,8 @@ public class InMemoryPersistenceManager implements PersistenceManager {
 
     @Override
     public ParkingLot addParkingLot(ParkingLot parkingLot) {
-        //check parking lot integrity
-        //we don't know what the user put
         parkingLot.setId(createID());
-        parkingLotMap.put(parkingLot.getId(),parkingLot);
+        parkingLotMap.put(parkingLot.getId(), parkingLot);
         return parkingLot;
     }
 

+ 6 - 6
src/main/java/eu/fibane/parkingtoll/core/PersistenceManager.java

@@ -11,28 +11,28 @@ public interface PersistenceManager {
      * Get all currently stored parking lots
      * @return
      */
-    public Collection<ParkingLot> getAllParkingLots();
+    Collection<ParkingLot> getAllParkingLots();
 
     /**
      * Adds a ParkingLot in database and assigns it a new ID
      * @param parkingLot the ParkingLot to add
      * @return
      */
-    public ParkingLot addParkingLot(ParkingLot parkingLot);
+    ParkingLot addParkingLot(ParkingLot parkingLot);
 
     /**
      * Search a parking lot by id
      * @param id the id to look for
      * @return the requested ParkingLot object
      */
-    public ParkingLot getParkingLotById(Long id);
+    ParkingLot getParkingLotById(Long id);
 
     /**
      * Update the specified ParkingLot in database. If it was not added previously, add it.
      * @param parkingLot the new value of parkingLot
      * @return the previous representation of parkingLot
      */
-    public ParkingLot updateParkingLot(Long id, ParkingLot parkingLot);
+    ParkingLot updateParkingLot(Long id, ParkingLot parkingLot);
 
     //public void parkCarAtParking(Long id);
 
@@ -41,11 +41,11 @@ public interface PersistenceManager {
      * @param id parking lot id to delete
      * @return the ParkingLot that was deleted, null if the parking lot was not found in the database
      */
-    public ParkingLot deleteParkingLotById(Long id);
+    ParkingLot deleteParkingLotById(Long id);
 
     /**
      * Clear all data stored in the database
      */
-    public void clearDatabase();
+    void clearDatabase();
 
 }

+ 13 - 0
src/main/java/eu/fibane/parkingtoll/exceptions/NoSuchCarInParkingException.java

@@ -0,0 +1,13 @@
+package eu.fibane.parkingtoll.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.NOT_FOUND)
+public class NoSuchCarInParkingException extends RuntimeException {
+
+    public NoSuchCarInParkingException(String message){
+        super(message);
+    }
+
+}

+ 13 - 0
src/main/java/eu/fibane/parkingtoll/exceptions/ParkingIsFullException.java

@@ -0,0 +1,13 @@
+package eu.fibane.parkingtoll.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
+public class ParkingIsFullException extends RuntimeException {
+
+    public ParkingIsFullException(String message){
+        super(message);
+    }
+
+}

+ 11 - 0
src/main/java/eu/fibane/parkingtoll/exceptions/ParkingTypeDoesNotExistException.java

@@ -0,0 +1,11 @@
+package eu.fibane.parkingtoll.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.NOT_FOUND)
+    public class ParkingTypeDoesNotExistException extends RuntimeException {
+    public ParkingTypeDoesNotExistException(String message){
+        super(message);
+    }
+}

+ 15 - 93
src/main/java/eu/fibane/parkingtoll/model/CarSlot.java

@@ -1,15 +1,19 @@
 package eu.fibane.parkingtoll.model;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
 import org.springframework.validation.annotation.Validated;
 
 import javax.validation.Valid;
-import java.time.OffsetDateTime;
+import java.math.BigDecimal;
+import java.time.Instant;
 import java.util.Objects;
 
 
-@Validated
+@Validated @Data
 public class CarSlot   {
+
   @JsonProperty("slot")
   private Long slot = null;
 
@@ -17,107 +21,25 @@ public class CarSlot   {
   private Long parkingLotId = null;
 
   @JsonProperty("arrival_time")
-  private OffsetDateTime arrivalTime = null;
+  private Instant arrivalTime = null;
+
+  @JsonProperty("departure_time")
+  private Instant departureTime = null;
 
   @JsonProperty("type")
   private String type = null;
 
-  public CarSlot slot(Long slot) {
-    this.slot = slot;
-    return this;
-  }
-
-  public Long getSlot() {
-    return slot;
-  }
+  @JsonProperty("price")
+  private BigDecimal price = null;
 
-  public void setSlot(Long slot) {
+  public CarSlot slot(Long slot) {
     this.slot = slot;
-  }
-
-  public CarSlot parkingLotId(Long parkingLotId) {
-    this.parkingLotId = parkingLotId;
-    return this;
-  }
-
-  public Long getParkingLotId() {
-    return parkingLotId;
-  }
-
-  public void setParkingLotId(Long parkingLotId) {
-    this.parkingLotId = parkingLotId;
-  }
-
-  public CarSlot arrivalTime(OffsetDateTime arrivalTime) {
-    this.arrivalTime = arrivalTime;
     return this;
   }
 
-  @Valid
-  public OffsetDateTime getArrivalTime() {
-    return arrivalTime;
+  public void updateDepartureTime(){
+    this.departureTime = Instant.now();
   }
 
-  public void setArrivalTime(OffsetDateTime arrivalTime) {
-    this.arrivalTime = arrivalTime;
-  }
-
-  public CarSlot type(String type) {
-    this.type = type;
-    return this;
-  }
-
-  public String getType() {
-    return type;
-  }
-
-  public void setType(String type) {
-    this.type = type;
-  }
-
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    CarSlot carSlot = (CarSlot) o;
-    return Objects.equals(this.slot, carSlot.slot) &&
-        Objects.equals(this.parkingLotId, carSlot.parkingLotId) &&
-        Objects.equals(this.arrivalTime, carSlot.arrivalTime) &&
-        Objects.equals(this.type, carSlot.type);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(slot, parkingLotId, arrivalTime, type);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append("class CarSlot {\n");
-
-    sb.append("    slot: ").append(toIndentedString(slot)).append("\n");
-    sb.append("    parkingLotId: ").append(toIndentedString(parkingLotId)).append("\n");
-    sb.append("    arrivalTime: ").append(toIndentedString(arrivalTime)).append("\n");
-    sb.append("    type: ").append(toIndentedString(type)).append("\n");
-    sb.append("}");
-    return sb.toString();
-  }
-
-  /**
-   * Convert the given object to string with each line indented by 4 spaces
-   * (except the first line).
-   */
-  private String toIndentedString(Object o) {
-    if (o == null) {
-      return "null";
-    }
-    return o.toString().replace("\n", "\n    ");
-  }
 }
 

+ 54 - 56
src/main/java/eu/fibane/parkingtoll/model/Layout.java

@@ -1,87 +1,85 @@
 package eu.fibane.parkingtoll.model;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import eu.fibane.parkingtoll.exceptions.NoSuchCarInParkingException;
+import eu.fibane.parkingtoll.exceptions.ParkingIsFullException;
+import lombok.Data;
 import org.springframework.validation.annotation.Validated;
 
-import java.util.Objects;
+import java.time.Instant;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
 
 /**
  * Layout
  */
 @Validated
-@javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2020-03-03T07:54:35.797Z")
-
-public class Layout   {
+@Data
+@JsonTypeName("layout")
+public class Layout {
   @JsonProperty("name")
   private String name = null;
 
   @JsonProperty("available")
-  private Long available = null;
-
-  public Layout name(String name) {
-    this.name = name;
-    return this;
+  private AtomicInteger available = new AtomicInteger();
+  @JsonIgnore
+  Deque<Long> ids;
+  @JsonIgnore
+  Map<Long, CarSlot> carSlots;
+
+  public Layout() {
+    this.carSlots = new HashMap<>();
   }
 
 
-  public String getName() {
-    return name;
-  }
-
-  public void setName(String name) {
-    this.name = name;
+  public void setAvailable(Integer available) {
+    this.available.set(available);
+    this.ids = LongStream.range(0, available).boxed().collect(Collectors.toCollection(ConcurrentLinkedDeque::new));
   }
 
-  public Layout available(Long available) {
-    this.available = available;
-    return this;
-  }
-
-
-  public Long getAvailable() {
-    return available;
+  public Long decrementAndGetID(){
+    Long id = null;
+    try {
+      id = ids.pop();
+    } catch (NoSuchElementException e){
+      return null; //parking is full
+    }
+    this.available.decrementAndGet();
+    return id;
   }
 
-  public void setAvailable(Long available) {
-    this.available = available;
+  public void incrementAndFree(Long id){
+    ids.add(id);
+    this.available.incrementAndGet();
   }
 
 
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
+  public void parkCar(CarSlot carSlot) {
+    Long id = decrementAndGetID();
+    if(id == null){
+      throw new ParkingIsFullException("Parking is full for this type of car");
     }
-    Layout layout = (Layout) o;
-    return Objects.equals(this.name, layout.name) &&
-        Objects.equals(this.available, layout.available);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(name, available);
-  }
-
-  @Override
-  public String toString() {
-    return "Layout{" +
-            "name='" + name + '\'' +
-            ", available=" + available +
-            '}';
+    carSlot.setSlot(id);
+    carSlot.setArrivalTime(Instant.now());
+    carSlots.put(id,carSlot);
   }
 
-  /**
-   * Convert the given object to string with each line indented by 4 spaces
-   * (except the first line).
-   */
-  private String toIndentedString(Object o) {
-    if (o == null) {
-      return "null";
+  public CarSlot removeCar(CarSlot carSlot) {
+    CarSlot slot = carSlots.get(carSlot.getSlot());
+    if(slot == null){
+      throw new NoSuchCarInParkingException("This car is not at the specified location");
     }
-    return o.toString().replace("\n", "\n    ");
+    //remove first, then make id available again
+    CarSlot storedCarSlot = carSlots.remove(carSlot.getSlot());
+    storedCarSlot.updateDepartureTime();
+    incrementAndFree(carSlot.getSlot());
+    return storedCarSlot;
   }
 }
 

+ 21 - 97
src/main/java/eu/fibane/parkingtoll/model/ParkingLot.java

@@ -1,6 +1,9 @@
 package eu.fibane.parkingtoll.model;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import eu.fibane.parkingtoll.exceptions.ParkingTypeDoesNotExistException;
+import lombok.Data;
 import org.springframework.validation.annotation.Validated;
 
 import javax.validation.Valid;
@@ -9,11 +12,14 @@ import javax.validation.constraints.Null;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * ParkingLot
  */
 @Validated
+@Data
 public class ParkingLot   {
   @JsonProperty("id")
   private Long id = null;
@@ -23,115 +29,33 @@ public class ParkingLot   {
 
   @JsonProperty("layout")
   @Valid
-  private List<Layout> layout = new ArrayList<Layout>();
+  private List<Layout> layoutList = new ArrayList<Layout>();
 
   @JsonProperty("pricing_policy")
   private PricingPolicy pricingPolicy = null;
 
-  public ParkingLot id(Long id) {
-    this.id = id;
-    return this;
+  public CarSlot parkCar(CarSlot carSlot){
+    Layout layout = getLayoutByName(carSlot.getType());
+    layout.parkCar(carSlot);
+    return carSlot;
   }
 
-  @Null
-  public Long getId() {
-    return id;
+  public CarSlot removeCar(CarSlot carSlot){
+    Layout layout = getLayoutByName(carSlot.getType());
+    CarSlot oldCarSlot = layout.removeCar(carSlot);
+    return oldCarSlot;
   }
 
-  public void setId(Long id) {
-    this.id = id;
-  }
-
-  public ParkingLot name(String name) {
-    this.name = name;
-    return this;
-  }
-
-  @NotNull
-  public String getName() {
-    return name;
-  }
-
-  public void setName(String name) {
-    this.name = name;
-  }
-
-  public ParkingLot layout(List<Layout> layout) {
-    this.layout = layout;
-    return this;
-  }
-
-  public ParkingLot addLayoutItem(Layout layoutItem) {
-    this.layout.add(layoutItem);
-    return this;
-  }
-
-  @NotNull  @Valid
-  public List<Layout> getLayout() {
-    return layout;
-  }
 
-  public void setLayout(List<Layout> layout) {
-    this.layout = layout;
+  private Layout getLayoutByName(String name){
+    Optional<Layout> layout = layoutList.stream().filter(a -> a.getName().equals(name)).findFirst();
+    return layout.orElseThrow(() -> new ParkingTypeDoesNotExistException("This type of slot does not exist in this parking lot"));
   }
 
-  public ParkingLot pricingPolicy(PricingPolicy pricingPolicy) {
-    this.pricingPolicy = pricingPolicy;
-    return this;
+  @JsonIgnore
+  public List<String> getSlotTypes(){
+    return layoutList.stream().map(Layout::getName).collect(Collectors.toList());
   }
 
-  @NotNull @Valid
-  public PricingPolicy getPricingPolicy() {
-    return pricingPolicy;
-  }
-
-  public void setPricingPolicy(PricingPolicy pricingPolicy) {
-    this.pricingPolicy = pricingPolicy;
-  }
-
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-    ParkingLot parkingLot = (ParkingLot) o;
-    return Objects.equals(this.id, parkingLot.id) &&
-        Objects.equals(this.name, parkingLot.name) &&
-        Objects.equals(this.layout, parkingLot.layout) &&
-        Objects.equals(this.pricingPolicy, parkingLot.pricingPolicy);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(id, name, layout, pricingPolicy);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append("class ParkingLot {\n");
-
-    sb.append("    id: ").append(toIndentedString(id)).append("\n");
-    sb.append("    name: ").append(toIndentedString(name)).append("\n");
-    sb.append("    layout: ").append(toIndentedString(layout)).append("\n");
-    sb.append("    pricingPolicy: ").append(toIndentedString(pricingPolicy)).append("\n");
-    sb.append("}");
-    return sb.toString();
-  }
-
-  /**
-   * Convert the given object to string with each line indented by 4 spaces
-   * (except the first line).
-   */
-  private String toIndentedString(Object o) {
-    if (o == null) {
-      return "null";
-    }
-    return o.toString().replace("\n", "\n    ");
-  }
 }
 

+ 18 - 60
src/main/java/eu/fibane/parkingtoll/model/PricingPolicy.java

@@ -1,17 +1,21 @@
 package eu.fibane.parkingtoll.model;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
 import org.springframework.validation.annotation.Validated;
 
 import javax.validation.Valid;
 import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.Objects;
 
 /**
  * PricingPolicy
  */
 @Validated
-
+@Data
 public class PricingPolicy   {
   @JsonProperty("flat_fee")
   private BigDecimal flatFee = null;
@@ -24,68 +28,22 @@ public class PricingPolicy   {
     return this;
   }
 
-  @Valid
-  public BigDecimal getFlatFee() {
-    return flatFee;
-  }
-
-  public void setFlatFee(BigDecimal flatFee) {
-    this.flatFee = flatFee;
-  }
-
-  public PricingPolicy perHourFare(BigDecimal perHourFare) {
-    this.perHourFare = perHourFare;
-    return this;
-  }
-
-  @Valid
-  public BigDecimal getPerHourFare() {
-    return perHourFare;
-  }
-
-  public void setPerHourFare(BigDecimal perHourFare) {
-    this.perHourFare = perHourFare;
-  }
-
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
+  public BigDecimal computeFare(Instant arrival, Instant departure){
+    BigDecimal result = BigDecimal.ZERO;
+    if(flatFee != null && flatFee.compareTo(BigDecimal.ZERO) != 0){
+      result = result.add(flatFee);
     }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
+    if(perHourFare != null && perHourFare.compareTo(BigDecimal.ZERO) != 0){
+      int multiplier = 0;
+      Duration duration = Duration.between(arrival,departure);
+      multiplier += duration.toHours();
+      if(duration.toMinutesPart() != 0){
+        multiplier++;
+      }
+      result = result.add(flatFee.multiply(BigDecimal.valueOf(multiplier)));
     }
-    PricingPolicy pricingPolicy = (PricingPolicy) o;
-    return Objects.equals(this.flatFee, pricingPolicy.flatFee) &&
-        Objects.equals(this.perHourFare, pricingPolicy.perHourFare);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(flatFee, perHourFare);
+    return result;
   }
 
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append("class PricingPolicy {\n");
-
-    sb.append("    flatFee: ").append(toIndentedString(flatFee)).append("\n");
-    sb.append("    perHourFare: ").append(toIndentedString(perHourFare)).append("\n");
-    sb.append("}");
-    return sb.toString();
-  }
-
-  /**
-   * Convert the given object to string with each line indented by 4 spaces
-   * (except the first line).
-   */
-  private String toIndentedString(Object o) {
-    if (o == null) {
-      return "null";
-    }
-    return o.toString().replace("\n", "\n    ");
-  }
 }
 

+ 8 - 0
src/test/java/eu/fibane/parkingtoll/ParkingTollApplicationTests.java

@@ -1,13 +1,21 @@
 package eu.fibane.parkingtoll;
 
+import eu.fibane.parkingtoll.api.ParkingLotApiController;
 import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 @SpringBootTest
 class ParkingTollApplicationTests {
 
+	@Autowired
+	private ParkingLotApiController parkingLotApiController;
+
 	@Test
 	void contextLoads() {
+		assertNotNull(parkingLotApiController);
 	}
 
 }

+ 26 - 0
src/test/java/eu/fibane/parkingtoll/controller/ParkingLotApiControllerTest.java

@@ -0,0 +1,26 @@
+package eu.fibane.parkingtoll.controller;
+
+import eu.fibane.parkingtoll.api.ParkingLotApiController;
+import org.apache.catalina.session.PersistentManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ParkingLotApiControllerTest {
+
+    //mocking our database
+    @Mock
+    private PersistentManager persistentManager;
+
+    @InjectMocks
+    private ParkingLotApiController controller;
+
+    @BeforeEach
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+
+
+}

+ 13 - 32
src/test/java/eu/fibane/parkingtoll/core/InMemoryPersistenceManagerTest.java

@@ -6,6 +6,10 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.security.SecureRandom;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -54,14 +58,14 @@ class InMemoryPersistenceManagerTest {
         ParkingLot parkingLot = new ParkingLot();
         List<Layout> layouts = new ArrayList<>();
         Layout layout = new Layout();
-        layout.setAvailable(random.nextLong());
+        layout.setAvailable(random.nextInt(1000));
         layout.setName("Test Parking Lot " + random.nextLong());
         layouts.add(layout);
         layout = new Layout();
-        layout.setAvailable(random.nextLong());
+        layout.setAvailable(random.nextInt(1000));
         layout.setName("Test Parking Lot " + random.nextLong());
         layouts.add(layout);
-        parkingLot.setLayout(layouts);
+        parkingLot.setLayoutList(layouts);
         return parkingLot;
     }
 
@@ -147,40 +151,17 @@ class InMemoryPersistenceManagerTest {
     }
 
     @Test
-    void testIDAllocation() throws ExecutionException, InterruptedException {
-        ExecutorService poolExecutor = Executors.newFixedThreadPool(10);
+    void testDurations(){
+        Instant arrival = Instant.now();
+        Instant departure = arrival.plus(20, ChronoUnit.MINUTES).plus(27,ChronoUnit.HOURS);
 
+        Duration duration = Duration.between(arrival,departure);
 
-        Callable<Void> myTask = () -> {
-            ParkingLot parkingLot;
-            for (int i = 0; i < 1000; i++) {
-                parkingLot = getNewParkingLot();
-                manager.addParkingLot(parkingLot);
-            }
-            return null;
-        };
+        assertEquals(20, duration.toMinutesPart());
+        assertEquals(27, duration.toHours());
 
-        List<Callable<Void>> callables = new ArrayList<>();
 
-        for (int i = 0; i < 10; i++) {
-            callables.add(myTask);
-        }
-
-
-        List<Future<Void>> futures = poolExecutor.invokeAll(callables);
-
-        //wait for finish
-        for (Future<Void> future : futures) {
-            future.get();
-        }
-
-        Collection<ParkingLot> parkingLots = manager.getAllParkingLots();
-        assertEquals(10_000, parkingLots.size());
-
-        Set<Long> ids = parkingLots.stream().map(ParkingLot::getId).collect(Collectors.toSet());
-        assertEquals(10_000,ids.size());
     }
 
 
-
 }