Fabien Chéret 5 éve
szülő
commit
323e57c375

+ 25 - 1
README.md

@@ -4,7 +4,22 @@ Server created with SpringBoot, Lombok, and maven
 
 ## Build instructions  
 
-`mvn clean spring-boot:run`
+### Prerequisites
+
+- Maven3
+- JDK 11+
+
+**Building**
+
+`mvn clean install`
+
+**Running the server**
+
+`mvn spring-boot:run`
+
+or
+
+`java -jar target/parkingtoll-0.0.1-SNAPSHOT.jar`
 
 The server application will spawn on http://localhost:8080
 
@@ -21,4 +36,13 @@ Endpoints for viewing and manipulating the Parking Lots and park cars.
  - [Remove a car from a Parking Lot and returns the amount to pay](docs/delpark.md) : `DELETE /parking_lot​/{parkingLotId}​/park`
 
 
+### Future Improvements
+- relying on an actual persistent DAO
+
+### Unit tests and code coverage
+
+`mvn clean test`
+
+100% classes, 100% lines covered in package `eu.fibane.parkingtoll`
 
+JaCoCo report in `target/site/` folder

+ 1 - 0
lombok.config

@@ -0,0 +1 @@
+lombok.addLombokGeneratedAnnotation = true

+ 19 - 0
pom.xml

@@ -51,6 +51,25 @@
 				<groupId>org.springframework.boot</groupId>
 				<artifactId>spring-boot-maven-plugin</artifactId>
 			</plugin>
+			<plugin>
+				<artifactId>jacoco-maven-plugin</artifactId>
+				<groupId>org.jacoco</groupId>
+				<version>0.8.5</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>prepare-agent</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>report</id>
+						<phase>test</phase>
+						<goals>
+							<goal>report</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>

+ 0 - 50
src/main/java/eu/fibane/parkingtoll/api/ParkingLotApi.java

@@ -1,50 +0,0 @@
-package eu.fibane.parkingtoll.api;
-
-import eu.fibane.parkingtoll.model.CarSlot;
-import eu.fibane.parkingtoll.model.ParkingLot;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-import javax.validation.Valid;
-import java.util.Collection;
-
-public interface ParkingLotApi {
-
-    @PostMapping(value = "/parking_lot",
-            produces = { "application/json" },
-            consumes = { "application/json" })
-    ResponseEntity<ParkingLot> addParkingLot(@Valid @RequestBody ParkingLot parkingLotItem);
-
-
-    @DeleteMapping(value = "/parking_lot/{parkingLotId}",
-            produces = { "application/json" },
-            consumes = { "application/json" })
-    ResponseEntity<ParkingLot> parkingLotDeleteById(@PathVariable("parkingLotId") Long parkingLotId);
-
-    @GetMapping(value = "/parking_lot/{parkingLotId}",
-            produces = { "application/json" })
-    ResponseEntity<ParkingLot> parkingLotGetById(@PathVariable("parkingLotId") Long parkingLotId);
-
-
-    @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",
-            produces = { "application/json" },
-            consumes = { "application/json" })
-    ResponseEntity<CarSlot> parkParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem);
-
-
-    @PutMapping(value = "/parking_lot/{parkingLotId}",
-            produces = { "application/json" },
-            consumes = { "application/json" })
-    ResponseEntity<ParkingLot> updateParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody ParkingLot parkingLotItem);
-
-
-    @GetMapping(value = "/parking_lot",
-            produces = { "application/json" })
-    ResponseEntity<Collection<ParkingLot>> searchParkingLot(@Valid @RequestParam(value = "searchString", required = false) String searchString);
-
-}

+ 27 - 18
src/main/java/eu/fibane/parkingtoll/api/ParkingLotApiController.java

@@ -1,6 +1,5 @@
 package eu.fibane.parkingtoll.api;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import eu.fibane.parkingtoll.core.PersistenceManager;
 import eu.fibane.parkingtoll.model.CarSlot;
 import eu.fibane.parkingtoll.model.ParkingLot;
@@ -8,12 +7,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
 import java.math.BigDecimal;
 import java.net.URI;
@@ -21,37 +16,43 @@ import java.util.Collection;
 import java.util.stream.Collectors;
 
 @RestController
-public class ParkingLotApiController implements ParkingLotApi {
+public class ParkingLotApiController {
 
     private static final Logger log = LoggerFactory.getLogger(ParkingLotApiController.class);
 
-    private final ObjectMapper objectMapper;
-    private final HttpServletRequest request;
     private PersistenceManager persistenceManager;
 
     @Autowired
-    public ParkingLotApiController(ObjectMapper objectMapper, HttpServletRequest request, PersistenceManager persistenceManager) {
-        this.objectMapper = objectMapper;
-        this.request = request;
+    public ParkingLotApiController(PersistenceManager persistenceManager) {
         this.persistenceManager = persistenceManager;
     }
 
-    public ResponseEntity<ParkingLot> addParkingLot(@Valid @RequestBody ParkingLot parkingLotItem) {
+    @PostMapping(value = "/parking_lot",
+            produces = { "application/json" },
+            consumes = { "application/json" })
+    public ResponseEntity<ParkingLot> createParkingLot(@Valid @RequestBody ParkingLot parkingLotItem) {
 
         parkingLotItem = persistenceManager.addParkingLot(parkingLotItem);
         return ResponseEntity.created(URI.create("/parking_lot/" + parkingLotItem.getId())).body(parkingLotItem);
     }
 
-    public ResponseEntity<ParkingLot> parkingLotDeleteById(@PathVariable("parkingLotId") Long parkingLotId) {
+    @DeleteMapping(value = "/parking_lot/{parkingLotId}",
+            produces = { "application/json" },
+            consumes = { "application/json" })
+    public ResponseEntity<ParkingLot> deleteParkingLotById(@PathVariable("parkingLotId") Long parkingLotId) {
         ParkingLot parkingLot = persistenceManager.deleteParkingLotById(parkingLotId);
         return ResponseEntity.ok(parkingLot);
     }
 
-    public ResponseEntity<ParkingLot> parkingLotGetById(@PathVariable("parkingLotId") Long parkingLotId) {
+    @GetMapping(value = "/parking_lot/{parkingLotId}",
+            produces = { "application/json" })
+    public ResponseEntity<ParkingLot> getParkingById(@PathVariable("parkingLotId") Long parkingLotId) {
         ParkingLot parkingLot = persistenceManager.getParkingLotById(parkingLotId);
         return ResponseEntity.ok(parkingLot);
     }
 
+    @DeleteMapping(value = "/parking_lot/{parkingLotId}/park",
+            produces = { "application/json" })
     public ResponseEntity<CarSlot> leaveParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
 
         ParkingLot parkingLot = persistenceManager.getParkingLotById(parkingLotId);
@@ -61,7 +62,10 @@ public class ParkingLotApiController implements ParkingLotApi {
         return ResponseEntity.ok(oldCarSlot);
     }
 
-    public ResponseEntity<CarSlot> parkParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
+    @PostMapping(value = "/parking_lot/{parkingLotId}/park",
+            produces = { "application/json" },
+            consumes = { "application/json" })
+    public ResponseEntity<CarSlot> parkAtParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody CarSlot carSlotItem) {
         //check if the asked parking exists
         ParkingLot parkingLot = persistenceManager.getParkingLotById(parkingLotId);
         carSlotItem.setParkingLotId(parkingLotId);
@@ -70,11 +74,14 @@ public class ParkingLotApiController implements ParkingLotApi {
         return ResponseEntity.ok(carSlotItem);
     }
 
+    @PutMapping(value = "/parking_lot/{parkingLotId}",
+            produces = { "application/json" },
+            consumes = { "application/json" })
     public ResponseEntity<ParkingLot> updateParkingLot(@PathVariable("parkingLotId") Long parkingLotId, @Valid @RequestBody ParkingLot parkingLotItem) {
         Long oldID = parkingLotItem.getId();
         parkingLotItem.setId(parkingLotId);
-        persistenceManager.updateParkingLot(parkingLotId, parkingLotItem);
-        if(!parkingLotItem.getId().equals(oldID)){
+        boolean newItem = persistenceManager.updateParkingLot(parkingLotId, parkingLotItem);
+        if(newItem){
             //it's a creation - 201
             return ResponseEntity.created(URI.create("/parking_lot/" + parkingLotItem.getId())).build();
         }
@@ -82,6 +89,8 @@ public class ParkingLotApiController implements ParkingLotApi {
         return ResponseEntity.noContent().build();
     }
 
+    @GetMapping(value = "/parking_lot",
+            produces = { "application/json" })
     public ResponseEntity<Collection<ParkingLot>> searchParkingLot(@Valid @RequestParam(value = "searchString", required = false) String searchString) {
         Collection<ParkingLot> parkingLots = persistenceManager.getAllParkingLots();
         if(searchString != null && !searchString.isBlank()) {

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

@@ -4,6 +4,7 @@ import eu.fibane.parkingtoll.exceptions.ParkingNotFoundException;
 import eu.fibane.parkingtoll.model.ParkingLot;
 import org.springframework.stereotype.Repository;
 
+import java.io.StringReader;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -43,13 +44,15 @@ public class InMemoryPersistenceManager implements PersistenceManager {
     }
 
     @Override
-    public void updateParkingLot(Long id, ParkingLot parkingLot) {
+    public boolean updateParkingLot(Long id, ParkingLot parkingLot) {
         if(!parkingLotExists(id)){
             addParkingLot(parkingLot);
+            return true;
         } else {
             //secure case where user puts wrong ID
             parkingLot.setId(id);
             parkingLotMap.put(parkingLot.getId(), parkingLot);
+            return false;
         }
     }
 

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

@@ -8,14 +8,14 @@ public interface PersistenceManager {
 
     /**
      * Get all currently stored parking lots
-     * @return
+     * @return all currently stored parking lots
      */
     Collection<ParkingLot> getAllParkingLots();
 
     /**
      * Adds a ParkingLot in database and assigns it a new ID
      * @param parkingLot the ParkingLot to add
-     * @return
+     * @return the added ParkingLot
      */
     ParkingLot addParkingLot(ParkingLot parkingLot);
 
@@ -29,8 +29,9 @@ public interface PersistenceManager {
     /**
      * Update the specified ParkingLot in database. If it was not added previously, add it.
      * @param parkingLot the new value of parkingLot
+     * @return true if the parking was created, false if the parking was updated
      */
-    void updateParkingLot(Long id, ParkingLot parkingLot);
+    boolean updateParkingLot(Long id, ParkingLot parkingLot);
 
     //public void parkCarAtParking(Long id);
 

+ 0 - 1
src/main/resources/application.properties

@@ -1 +0,0 @@
-

+ 16 - 7
src/test/java/eu/fibane/parkingtoll/ParkingTollApplicationTests.java

@@ -99,9 +99,9 @@ class ParkingTollApplicationTests {
 		when(persistenceManager.deleteParkingLotById(eq(1L))).thenReturn(parkingLots.get(0));
 
 		//non existing id
-		assertThrows(ParkingNotFoundException.class, () -> parkingLotApiController.parkingLotDeleteById(-5L));
+		assertThrows(ParkingNotFoundException.class, () -> parkingLotApiController.deleteParkingLotById(-5L));
 
-		ResponseEntity<ParkingLot> result = parkingLotApiController.parkingLotDeleteById(1L);
+		ResponseEntity<ParkingLot> result = parkingLotApiController.deleteParkingLotById(1L);
 		assertEquals(parkingLots.get(0), result.getBody());
 		assertEquals(result.getStatusCode(), HttpStatus.OK);
 
@@ -114,7 +114,7 @@ class ParkingTollApplicationTests {
 
 		when(persistenceManager.addParkingLot(any())).thenReturn(storedParkingLot);
 
-		ResponseEntity<ParkingLot> response = parkingLotApiController.addParkingLot(parkingLots.get(0));
+		ResponseEntity<ParkingLot> response = parkingLotApiController.createParkingLot(parkingLots.get(0));
 
 		assertEquals(HttpStatus.CREATED, response.getStatusCode());
 		assertEquals(URI.create("/parking_lot/55") , response.getHeaders().getLocation());
@@ -126,7 +126,7 @@ class ParkingTollApplicationTests {
 	void parkingLotGetByIdTest(){
 		ParkingLot storedParkingLot = ParkingLotTest.generateParking("created parking", 5L);
 		when(persistenceManager.getParkingLotById(5L)).thenReturn(storedParkingLot);
-		ResponseEntity<ParkingLot> result = parkingLotApiController.parkingLotGetById(5L);
+		ResponseEntity<ParkingLot> result = parkingLotApiController.getParkingById(5L);
 
 		assertEquals(HttpStatus.OK, result.getStatusCode());
 
@@ -157,7 +157,7 @@ class ParkingTollApplicationTests {
 		carSlot.setType(storedParkingLot.getSlotTypes().get(0));
 		when(persistenceManager.getParkingLotById(storedParkingLot.getId())).thenReturn(storedParkingLot);
 
-		ResponseEntity<CarSlot> result = parkingLotApiController.parkParkingLot(storedParkingLot.getId(), carSlot);
+		ResponseEntity<CarSlot> result = parkingLotApiController.parkAtParkingLot(storedParkingLot.getId(), carSlot);
 		assertEquals(HttpStatus.OK, result.getStatusCode());
 	}
 
@@ -165,18 +165,27 @@ class ParkingTollApplicationTests {
 	void updateParkingLotTest(){
 		//no existing parking lots
 		when(persistenceManager.getParkingLotById(any())).thenReturn(null);
-		doNothing().when(persistenceManager).updateParkingLot(anyLong(), any());
+		when(persistenceManager.updateParkingLot(anyLong(), any())).thenReturn(true);
 
 		ParkingLot newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
 		ResponseEntity<ParkingLot> result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
-		assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
+		assertEquals(HttpStatus.CREATED, result.getStatusCode());
 
 
 		when(persistenceManager.parkingLotExists(5L)).thenReturn(true);
 		newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
 		result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
+		assertEquals(HttpStatus.CREATED, result.getStatusCode());
+
+		ParkingLot updated = ParkingLotTest.generateParking("created parking", 6L);
+
+		when(persistenceManager.updateParkingLot(anyLong(), any())).thenReturn(false);
+		newParkingLot = ParkingLotTest.generateParking("created parking", 5L);
+		result = parkingLotApiController.updateParkingLot(newParkingLot.getId(), newParkingLot);
 		assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
 
+
+
 	}
 
 }

+ 5 - 0
src/test/java/eu/fibane/parkingtoll/model/ParkingLotTest.java

@@ -1,6 +1,7 @@
 package eu.fibane.parkingtoll.model;
 
 import eu.fibane.parkingtoll.exceptions.ParkingIsFullException;
+import eu.fibane.parkingtoll.exceptions.ParkingTypeDoesNotExistException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -61,6 +62,10 @@ public class ParkingLotTest {
         assertNotNull(foundLayout2.getCarSlots());
         assertTrue(foundLayout2.getCarSlots().containsValue(parkedCarSlot2));
 
+        CarSlot carSlot3 = new CarSlot();
+        carSlot3.setType("TYPE THAT DOES NOT EXIST");
+        assertThrows(ParkingTypeDoesNotExistException.class, () -> parkingLot.parkCar(carSlot3));
+
     }
 
     @Test