Skip to content

Commit 2de0ece

Browse files
committed
Added some Unit & Integration tests...
1 parent 2730651 commit 2de0ece

27 files changed

+872
-154
lines changed

README.md

+13-24
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
1-
# mytaxi backend applicant test
1+
# MyTaxiAPI
22

3-
## Task Description
4-
You should be able to start the example application by executing com.mytaxi.MytaxiServerApplicantTestApplication, which starts a webserver on port 8080 (http://localhost:8080) and serves SwaggerUI where can inspect and try existing endpoints.
3+
## Description
4+
MyTaxiApi, which starts a webserver on port 8080 (http://localhost:8080) and serves SwaggerUI where can inspect and try existing endpoints.
55

66
The project is based on a small web service which uses the following technologies:
77

88
* Java 1.8
99
* Spring MVC with Spring Boot
1010
* Database H2 (In-Memory)
1111
* Maven
12-
* Intellij as IDE is preferred but not mandatory. We do provide code formatter for intellij and eclipse in the etc folder.
12+
* Intellij as IDE is preferred but not mandatory.
1313

1414

15-
You should be aware of the following conventions while you are working on this exercise:
15+
NOTE:
1616

1717
* All new entities should have an ID with type of Long and a date_created with type of ZonedDateTime.
1818
* The architecture of the web service is built with the following components:
19-
* DataTransferObjects: Objects which are used for outside communication via the API
19+
* DataTransferObjects: Objects which are used for outside communication via the API
2020
* Controller: Implements the processing logic of the web service, parsing of parameters and validation of in- and outputs.
2121
* Service: Implements the business logic and handles the access to the DataAccessObjects.
2222
* DataAccessObjects: Interface for the database. Inserts, updates, deletes and reads objects from the database.
2323
* DomainObjects: Functional Objects which might be persisted in the database.
24-
* TestDrivenDevelopment is a good choice, but it's up to you how you are testing your code.
25-
26-
You should commit into your local git repository and include the commit history into the final result.
24+
* TDD/BDD are good choices.
2725

2826
---
2927

3028

31-
## Task 1
29+
## Objective 1
3230
* Write a new Controller for maintaining cars (CRUD).
3331
* Decide on your own how the methods should look like.
3432
* Entity Car: Should have at least the following characteristics: license_plate, seat_count, convertible, averageRating, engine_type (electric, gas, ...)
@@ -41,13 +39,13 @@ You should commit into your local git repository and include the commit history
4139
---
4240

4341

44-
## Task 2
42+
## Objective 2
4543
First come first serve: A car can be selected by exactly one ONLINE Driver. If a second driver tries to select a already used car you should throw a CarAlreadyInUseException.
4644

4745
---
4846

4947

50-
## Task 3
48+
## Objective 3
5149
Imagine a driver management frontend that is used internally by mytaxi employees to create and edit driver related data. For a new search functionality, we need an endpoint to search for drivers. It should be possible to search for drivers by their attributes (username, online_status) as well as car characteristics (license plate, averageRating, etc).
5250

5351
* implement a new endpoint for searching or extend an existing one
@@ -57,18 +55,9 @@ Imagine a driver management frontend that is used internally by mytaxi employees
5755
---
5856

5957

60-
## Task 4 (optional)
61-
This task is _voluntarily_, if you can't get enough of hacking tech challenges, implement security.
62-
Secure the API so that authentication is needed to access it. The details are up to you.
63-
64-
Please include instructions how to authenticate/login, so that we can test the endpoints you implemented!
58+
## Objective 4
59+
The API is secured (with Spring Security) such that authentication is needed to access it.
6560

6661
---
6762

68-
69-
Good luck!
70-
❤️ mytaxi
71-
72-
73-
74-
_NOTE: Please make sure to not submit any personal data with your tests result. Personal data is for example your name, your birth date, email address etc._
63+
❤️ MyTaxiAPI

pom.xml

+27-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<java.version>1.8</java.version>
3030
<io.springfox.springfox.version>2.8.0</io.springfox.springfox.version>
3131
<org.springframework.security.version>5.0.5.RELEASE</org.springframework.security.version>
32+
<junit.jupiter.version>5.3.1</junit.jupiter.version>
3233
</properties>
3334

3435
<dependencies>
@@ -81,6 +82,32 @@
8182
<artifactId>jaxb-api</artifactId>
8283
<version>2.2.11</version>
8384
</dependency>
85+
<dependency>
86+
<groupId>org.junit.jupiter</groupId>
87+
<artifactId>junit-jupiter-api</artifactId>
88+
<version>${junit.jupiter.version}</version>
89+
<scope>test</scope>
90+
</dependency>
91+
<dependency>
92+
<groupId>org.junit.jupiter</groupId>
93+
<artifactId>junit-jupiter-params</artifactId>
94+
<version>${junit.jupiter.version}</version>
95+
<scope>test</scope>
96+
</dependency>
97+
<dependency>
98+
<groupId>org.junit.jupiter</groupId>
99+
<artifactId>junit-jupiter-engine</artifactId>
100+
<version>${junit.jupiter.version}</version>
101+
<scope>test</scope>
102+
</dependency>
103+
104+
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
105+
<dependency>
106+
<groupId>com.google.code.gson</groupId>
107+
<artifactId>gson</artifactId>
108+
<version>2.8.5</version>
109+
</dependency>
110+
84111
</dependencies>
85112

86113
<build>
@@ -91,6 +118,4 @@
91118
</plugin>
92119
</plugins>
93120
</build>
94-
95-
96121
</project>

src/main/java/com/mytaxi/MytaxiServerApplicantTestApplication.java src/main/java/com/mytaxi/MyTaxiApiApp.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*/
2525
@EnableSwagger2
2626
@SpringBootApplication
27-
public class MytaxiServerApplicantTestApplication extends WebMvcConfigurerAdapter {
27+
public class MyTaxiApiApp extends WebMvcConfigurerAdapter {
2828

2929
public static void main(String[] args) {
30-
SpringApplication.run(MytaxiServerApplicantTestApplication.class, args);
30+
SpringApplication.run(MyTaxiApiApp.class, args);
3131
}
3232

3333

src/main/java/com/mytaxi/aop/security/LoginAttempts.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void logBefore(Authentication authentication) {
4343

4444
@AfterReturning(value = "com.mytaxi.aop.security.LoginAttempts.doAuthenticate()", returning = "authentication")
4545
public void logAfter(Authentication authentication) {
46-
String username = (String) authentication.getPrincipal();
46+
String username = String.valueOf(authentication.getPrincipal());
4747

4848
if (authentication.isAuthenticated()) {
4949
loginAttemptService.loginSucceeded(username);

src/main/java/com/mytaxi/config/WebSecurityConfig.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected void configure(HttpSecurity httpSecurity) throws Exception {
8181
.and()
8282
.logout()
8383
.logoutUrl(URL_LOGOUT)
84-
.logoutSuccessUrl("/login")
84+
.logoutSuccessUrl("/")
8585
.deleteCookies(COOKIES_SESSION).clearAuthentication(true)
8686
.and()
8787
.rememberMe().rememberMeCookieName(COOKIE_REMEMBER_ME).tokenValiditySeconds(1200)

src/main/java/com/mytaxi/controller/CarController.java

+17-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package com.mytaxi.controller;
22

33
import com.mytaxi.datatransferobject.CarDTO;
4+
import com.mytaxi.datatransferobject.SelectDTO;
45
import com.mytaxi.domainvalue.CarStatus;
5-
import com.mytaxi.domainvalue.Selection;
66
import com.mytaxi.exception.*;
7-
import com.mytaxi.service.persistence.car.CarService;
7+
import com.mytaxi.service.car.CarService;
88
import org.slf4j.Logger;
99
import org.slf4j.LoggerFactory;
1010
import org.springframework.beans.factory.annotation.Autowired;
1111
import org.springframework.http.HttpStatus;
1212
import org.springframework.security.access.prepost.PreAuthorize;
1313
import org.springframework.web.bind.annotation.*;
1414

15+
import javax.validation.Valid;
1516
import java.util.List;
1617

1718
/**
@@ -37,7 +38,7 @@ public CarController(final CarService carService) {
3738

3839
@GetMapping
3940
public List<CarDTO> findCarsByStatus(@RequestParam(required = false) CarStatus carStatus) throws EntityNotFoundException {
40-
LOGGER.info("findCarsByStatus: " + carStatus);
41+
LOGGER.debug("findCarsByStatus: {}", carStatus);
4142
if (null == carStatus) {
4243
return carService.findAllCars();
4344
}
@@ -46,24 +47,27 @@ public List<CarDTO> findCarsByStatus(@RequestParam(required = false) CarStatus c
4647

4748
@GetMapping("/{licenceNo}")
4849
public CarDTO findCarByLicenceNo(@PathVariable String licenceNo) throws EntityNotFoundException {
50+
LOGGER.debug("licenceNo: {}", licenceNo);
4951
return carService.findCarByLicenceNo(licenceNo);
5052
}
5153

52-
@PutMapping("/select/{licenceNo}/{driverId}")
54+
@PutMapping("/select")
5355
@ResponseStatus(HttpStatus.CREATED)
54-
public void toggleCarByLicenceNo(@PathVariable Long driverId,
55-
@RequestParam Selection selection,
56-
@PathVariable String licenceNo) throws EntityNotFoundException,
57-
CarAlreadyInUseException, CarAlreadySelectedException, CarAlreadyDeselectedException, DriverNotOnlineException, CarUnavailableException, DeselectionNotAllowedException, NoCarSelectionException {
58-
carService.toggleCar(driverId, licenceNo, selection);
56+
public void toggleCarByLicenceNo(@RequestBody @Valid SelectDTO selectDTO) throws EntityNotFoundException,
57+
CarAlreadyInUseException, CarAlreadySelectedException, CarAlreadyDeselectedException,
58+
DriverNotOnlineException, CarUnavailableException, DeselectionNotAllowedException, NoCarSelectionException {
59+
LOGGER.debug("Updating toggleCarByLicenceNo >>> \ndriverId: {} \nlicenceNo: {} \nselectionState: {}",
60+
selectDTO.getDriverId(), selectDTO.getLicenceNo(), selectDTO.getSelectionState());
61+
carService.toggleCar(selectDTO.getDriverId(), selectDTO.getLicenceNo(), selectDTO.getSelectionState());
5962
}
6063

6164

6265
@GetMapping("/find-cars")
63-
public List<CarDTO> carDTOList(@RequestParam String licenceNo,
64-
@RequestParam String make,
65-
@RequestParam String model,
66-
@RequestParam Integer seatCount) {
66+
public List<CarDTO> carDTOList(@RequestParam(required = false) String licenceNo,
67+
@RequestParam(required = false) String make,
68+
@RequestParam(required = false) String model,
69+
@RequestParam(required = false) Integer seatCount) {
70+
LOGGER.debug("toggleCarByLicenceNo >>> \nlicenceNo: {} \nmake: {} \nmodel: {} \nseatCount: {}", licenceNo, make, model, seatCount);
6771
return carService.findCarCharacteristics(licenceNo, make, model, seatCount);
6872
}
6973

src/main/java/com/mytaxi/controller/DriverController.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import com.mytaxi.domainvalue.OnlineStatus;
77
import com.mytaxi.exception.ConstraintsViolationException;
88
import com.mytaxi.exception.EntityNotFoundException;
9-
import com.mytaxi.service.persistence.driver.DriverService;
9+
import com.mytaxi.service.driver.DriverService;
1010
import org.springframework.beans.factory.annotation.Autowired;
1111
import org.springframework.http.HttpStatus;
1212
import org.springframework.security.access.prepost.PreAuthorize;

src/main/java/com/mytaxi/controller/ErrorPageCtrl.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package com.mytaxi.controller;
22

3+
import javassist.NotFoundException;
34
import org.springframework.boot.web.servlet.error.ErrorController;
5+
import org.springframework.http.HttpStatus;
46
import org.springframework.stereotype.Controller;
7+
import org.springframework.web.bind.annotation.ExceptionHandler;
58
import org.springframework.web.bind.annotation.GetMapping;
69
import org.springframework.web.bind.annotation.ResponseBody;
10+
import org.springframework.web.bind.annotation.ResponseStatus;
711
import org.springframework.web.servlet.ModelAndView;
12+
import springfox.documentation.annotations.ApiIgnore;
813

914
/**
1015
* PROJECT : server-applicant-test-18
@@ -15,11 +20,13 @@
1520
* E-MAIL : kudzai@bcs.org
1621
* CELL : +27-64-906-8809
1722
*/
18-
23+
@ApiIgnore
1924
@Controller
2025
public class ErrorPageCtrl implements ErrorController {
2126
private static final String ERROR_PATH = "/error";
2227

28+
@ExceptionHandler(NotFoundException.class)
29+
@ResponseStatus(HttpStatus.NOT_FOUND)
2330
@GetMapping(value = ERROR_PATH)
2431
public @ResponseBody
2532
ModelAndView handleError() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.mytaxi.datatransferobject;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.mytaxi.domainvalue.SelectionState;
5+
6+
import javax.validation.constraints.NotNull;
7+
8+
/**
9+
* PROJECT : server-applicant-test-18
10+
* PACKAGE : com.mytaxi.datatransferobject
11+
* USER : sean
12+
* DATE : 12-Fri-Oct-2018
13+
* TIME : 22:27
14+
* E-MAIL : kudzai@bcs.org
15+
* CELL : +27-64-906-8809
16+
*/
17+
@JsonInclude(JsonInclude.Include.NON_NULL)
18+
//@JsonDeserialize(as = SelectDTO.class)
19+
public class SelectDTO {
20+
21+
@NotNull(message = "Driver-ID can not be null!")
22+
private Long driverId;
23+
@NotNull(message = "Selection-State can not be null!")
24+
private SelectionState selectionState;
25+
@NotNull(message = "Licence-No can not be null!")
26+
private String licenceNo;
27+
28+
public SelectDTO() {
29+
super();
30+
}
31+
32+
public SelectDTO(@NotNull(message = "Driver-ID can not be null!") Long driverId, @NotNull(message = "Selection-State can not be null!") SelectionState selectionState, @NotNull(message = "Licence-No can not be null!") String licenceNo) {
33+
this.driverId = driverId;
34+
this.selectionState = selectionState;
35+
this.licenceNo = licenceNo;
36+
}
37+
38+
public Long getDriverId() {
39+
return driverId;
40+
}
41+
42+
public void setDriverId(Long driverId) {
43+
this.driverId = driverId;
44+
}
45+
46+
public SelectionState getSelectionState() {
47+
return selectionState;
48+
}
49+
50+
public void setSelectionState(SelectionState selectionState) {
51+
this.selectionState = selectionState;
52+
}
53+
54+
public String getLicenceNo() {
55+
return licenceNo;
56+
}
57+
58+
public void setLicenceNo(String licenceNo) {
59+
this.licenceNo = licenceNo;
60+
}
61+
}

src/main/java/com/mytaxi/domainvalue/Selection.java src/main/java/com/mytaxi/domainvalue/SelectionState.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
* E-MAIL : kudzai@bcs.org
1010
* CELL : +27-64-906-8809
1111
*/
12-
public enum Selection {
12+
public enum SelectionState {
1313
SELECT, DESELECT
1414
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.mytaxi.engine;
2+
3+
import com.mytaxi.domainobject.DriverDO;
4+
import com.mytaxi.domainobject.car.CarStateDO;
5+
import com.mytaxi.exception.*;
6+
7+
/**
8+
* PROJECT : server-applicant-test-18
9+
* PACKAGE : com.mytaxi.engine
10+
* USER : sean
11+
* DATE : 12-Fri-Oct-2018
12+
* TIME : 13:24
13+
* E-MAIL : kudzai@bcs.org
14+
* CELL : +27-64-906-8809
15+
*/
16+
public interface CarSelection {
17+
void selectCar(DriverDO driverDO, CarStateDO carStateDO) throws CarAlreadySelectedException, CarAlreadyInUseException,
18+
DriverNotOnlineException, CarUnavailableException, NoCarSelectionException;
19+
20+
void deselectCar(DriverDO driverDO, CarStateDO carStateDO) throws DeselectionNotAllowedException;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.mytaxi.engine;
2+
3+
import com.mytaxi.domainobject.DriverDO;
4+
import com.mytaxi.domainobject.car.CarStateDO;
5+
import com.mytaxi.exception.*;
6+
7+
/**
8+
* PROJECT : server-applicant-test-18
9+
* PACKAGE : com.mytaxi.engine
10+
* USER : sean
11+
* DATE : 12-Fri-Oct-2018
12+
* TIME : 13:23
13+
* E-MAIL : kudzai@bcs.org
14+
* CELL : +27-64-906-8809
15+
*/
16+
public interface CarSelectionRules {
17+
void validateCarSelectionRules(DriverDO driverDO, CarStateDO carStateDO) throws CarAlreadySelectedException, CarAlreadyInUseException,
18+
DriverNotOnlineException, CarUnavailableException, NoCarSelectionException;
19+
}

0 commit comments

Comments
 (0)