Skip to content

Commit 2fc7168

Browse files
committed
Finished unit tests
1 parent c337721 commit 2fc7168

14 files changed

+958
-76
lines changed

README.md

+62-29
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ database are not yet implemented.
2424
The application source contains the following directories:
2525

2626
- `src/main/java/org.example/models` - Contains the model classes that represent
27-
the data in the database. Currently, there is only one model class, `User`,
28-
which represents a user from the database `users` table.
27+
the data in the database. *You will not need to create any new model classes.*
2928
- `src/main/java/org.example/daos` - Contains the data access objects that
3029
interact with the database. Currently, there is only one DAO class, `UserDao`,
3130
which interacts with the `users` table.
@@ -63,7 +62,6 @@ directory. You will need to set the following properties:
6362
- `spring.datasource.username` - The username for your mysql server.
6463
- `spring.datasource.password` - The password for your mysql server.
6564

66-
6765
### Running the Application
6866

6967
To run the application, you can right-click on the `SpringBootApplication`
@@ -109,21 +107,16 @@ until you implement the REST endpoints.
109107
## Exercise
110108

111109
Your task is to implement the REST endpoints that allow users to interact with
112-
the store. You will need to create a new model class, DAO class, and controller
110+
the store. You will need to create a DAO class and a controller
113111
class for each of the tables in the database.
114112

115113
Stop the application and follow the steps below to complete the exercise.
116114

117-
### Step 1: Create the Model Classes
118-
119-
In the `models` folder, create a new model class for each of the tables in the
120-
database (other than the `users` and `roles` tables). Each model class should
121-
contain a field for each column in the table, and should contain getters and
122-
setters for each field. You should also create a constructor that takes all of
123-
the fields as arguments.
115+
### Step 1: Review the Model Classes
124116

125-
You can refer to the existing `User` model class for an example of what these
126-
classes should look like.
117+
In the `models` folder, notice that there is a class for each of the tables in
118+
the database. You will need to use these classes to interact with the database
119+
in the DAO classes and controller classes.
127120

128121
### Step 2: Create the DAO Classes
129122

@@ -166,52 +159,93 @@ public class ProductDao {
166159
}
167160
```
168161

162+
Each DAO class will also need a mapping method that maps a `ResultSet` object
163+
to a model object so that your `SELECT` queries can map the results to the
164+
appropriate model objects. You can refer to the `UserDao` class for an example
165+
of what this method should look like.
166+
169167
### Step 3: Create the Controller Classes
170168

171169
Create a new controller class for each of the tables in the database (other
172170
than the `users` and `roles` tables). Each controller class should contain
173171
methods providing REST endpoints for creating, reading, updating, and deleting
174-
items in the table.
172+
items in the table. These endpoints are detailed below.
175173

176174
You can refer to the existing `UserController` and `ProfileController` classes
177175
for an example of what these classes should look like.
178176

179177
Note that each controller class injects its corresponding DAO class through
180-
the constructor. You should do the same in your controller classes.
178+
an `@Autowired` class member. You will need to do the same for your controller
179+
classes.
181180

182181
For example:
183182

184183
```java
185184
@RestController
186185
@RequestMapping("/products")
187186
public class ProductController {
187+
@Autowired
188188
private ProductDao productDao;
189189

190-
public ProductController(ProductDao productDao) {
191-
this.productDao = productDao;
192-
}
193-
194190
// ...
195191
}
196192
```
193+
> [!Note]
194+
> Your "Get by Id" methods should return a 404 status code if the item is not
195+
> found in the database. You can do this by throwing a `ResponseStatusException`
196+
> with a `HttpStatus.NOT_FOUND` status code. (Refer to the `UserController` class
197+
> for an example of how to do this.)
197198
198199
When you are finished, you should have implemented all of the REST endpoints
199200
in the Postman collection, and they should all function properly and return
200201
appropriate data.
201202

202-
## Testing / Verification
203+
### Product Endpoints
204+
205+
- `GET /products` - Retrieves all products.
206+
- `GET /products/{id}` - Retrieves a product by the id in the path, return a 404 NOT FOUND status code if the product is not found.
207+
- `POST /products` - Creates a new product from the request body and returns the created product with a 201 CREATED http status code.
208+
- `PUT /products/{id}` - Updates an existing product from the request body and returns the updated product, return a 404 NOT FOUND status code if the product is not found.
209+
- `DELETE /products/{id}` - Deletes a product by the id in the path and returns the number of rows affected, return a 404 NOT FOUND status code if the product is not found.
210+
211+
### Order Endpoints
212+
213+
- `GET /orders` - Retrieves all orders.
214+
- `GET /orders/{id}` - Retrieves an order by the id in the path, return a 404 NOT FOUND status code if the order is not found.
215+
- `POST /orders` - Creates a new order from the request body and returns the created order with a 201 CREATED http status code.
216+
- `PUT /orders/{id}` - Updates an existing order from the request body and returns the updated order, return a 404 NOT FOUND status code if the order is not found.
217+
- `DELETE /orders/{id}` - Deletes an order by the id in the path and returns the number of rows affected, return a 404 NOT FOUND status code if the order is not found.
218+
219+
### Order Item Endpoints
220+
221+
- `GET /order-items` - Retrieves all order items.
222+
- `GET /order-items/{id}` - Retrieves an order item by the id in the path, return a 404 NOT FOUND status code if the order item is not found.
223+
- `POST /order-items` - Creates a new order item from the request body and returns the created order item with a CREATED http 201 status code.
224+
- `PUT /order-items/{id}` - Updates an existing order item from the request body and returns the updated order item, return a 404 NOT FOUND status code if the order item is not found.
225+
- `DELETE /order-items/{id}` - Deletes an order item by the id in the path and returns the number of rows affected, return a 404 NOT FOUND status code if the order item is not found.
226+
227+
## Testing with Postman
203228

204229
You can verify that the application is working correctly by running the
205230
application and then launching Postman to test the REST endpoints. You should
206231
be able to create, read, update, and delete items in the database using the
207232
REST endpoints that you created.
208233

234+
## Testing with Included Unit Tests
235+
236+
You can also verify that the application is working correctly by running the
237+
included unit tests. The unit tests are located in the `src/test/java` directory.
238+
To run the unit tests, right-click on the `src/test/java` directory and select
239+
"Run All Tests".
240+
241+
> [!warning]
242+
> DO NOT modify the unit tests. Your project will be evaluated based on the
243+
> unit tests all passing. Modifying the unit tests will result in a failing
244+
> grade.
245+
209246
## Evaluation
210247

211-
Your project will be evaluated by the test proctor cloning your repository,
212-
running the database script, running the application, and executing the
213-
Postman requests. You will receive full credit if all of the requests execute
214-
successfully and return the expected data.
248+
Your project will be evaluated based on the unit tests all passing.
215249

216250
## Bonus Steps
217251

@@ -222,13 +256,13 @@ controller classes to require that users be authenticated in order to access
222256
the endpoints. You can use the `isAuthenticated()` expression to require that
223257
users be authenticated.
224258

225-
### Bonus Step 2: Constrain Product Creation and Update using `Principal`
259+
### Bonus Step 2: Constrain Order Creation and Update using `Principal`
226260

227261
Add a `Principal` argument to the create and update endpoints for the
228-
`Product` controller that will allow you to get the username of the user and
229-
overwrite the `username` field in the passed `Product` object. This will
262+
`Order` controller that will allow you to get the username of the user and
263+
overwrite the `username` field in the passed `Order` object. This will
230264
guaranatee that the `username` field is always set to the username of the
231-
user that created or updated the product.
265+
user that created or updated the order.
232266

233267
For example:
234268

@@ -263,7 +297,6 @@ public List<Order> listOrders(@RequestParam(required = false) String username) {
263297
> You will need to create a new method in the `OrderDao` class that retrieves
264298
> orders by username.
265299
266-
267300
### Bonus Step 4: Add optional query parameters to the `OrderItems` GET endpoint
268301

269302
Add optional `orderId` query parameters to the `OrderItems` GET endpoint that

java-springboot-final/src/main/java/org/example/controllers/ProfileController.java

+4-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.example.daos.UserDao;
44
import org.example.models.User;
5+
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.security.access.prepost.PreAuthorize;
67
import org.springframework.web.bind.annotation.*;
78

@@ -18,16 +19,8 @@ public class ProfileController {
1819
/**
1920
* The user data access object.
2021
*/
21-
private final UserDao userDao;
22-
23-
/**
24-
* Creates a new user controller.
25-
*
26-
* @param userDao The user data access object.
27-
*/
28-
public ProfileController(UserDao userDao) {
29-
this.userDao = userDao;
30-
}
22+
@Autowired
23+
private UserDao userDao;
3124

3225
/**
3326
* Gets the profile of the currently logged in user.
@@ -60,7 +53,7 @@ public List<String> getRoles(Principal principal) {
6053
* @param newPassword The new password.
6154
* @return The updated user.
6255
*/
63-
@PostMapping("/change-password")
56+
@PutMapping("/change-password")
6457
public User changePassword(Principal principal, @RequestBody String newPassword) {
6558
String username = principal.getName();
6659
User user = userDao.getUserByUsername(username);

java-springboot-final/src/main/java/org/example/controllers/UserController.java

+16-16
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import org.example.models.User;
44
import org.example.daos.UserDao;
5+
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.http.HttpStatus;
67
import org.springframework.security.access.prepost.PreAuthorize;
78
import org.springframework.web.bind.annotation.*;
9+
import org.springframework.web.server.ResponseStatusException;
810

911
import java.util.List;
1012

@@ -20,16 +22,8 @@ public class UserController {
2022
/**
2123
* The user data access object.
2224
*/
23-
private final UserDao userDao;
24-
25-
/**
26-
* Creates a new user controller.
27-
*
28-
* @param userDao The user data access object.
29-
*/
30-
public UserController(UserDao userDao) {
31-
this.userDao = userDao;
32-
}
25+
@Autowired
26+
private UserDao userDao;
3327

3428
/**
3529
* Gets all users.
@@ -75,6 +69,9 @@ public User create(@RequestBody User user) {
7569
@PutMapping(path = "/{username}/password")
7670
public User updatePassword(@RequestBody String password, @PathVariable String username) {
7771
User user = userDao.getUserByUsername(username);
72+
if (user == null) {
73+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found");
74+
}
7875
user.setPassword(password);
7976
return userDao.updatePassword(user);
8077
}
@@ -84,10 +81,9 @@ public User updatePassword(@RequestBody String password, @PathVariable String us
8481
*
8582
* @param username The username of the user to delete.
8683
*/
87-
@ResponseStatus(HttpStatus.NO_CONTENT)
8884
@DeleteMapping(path = "/{username}")
89-
public void delete(@PathVariable String username) {
90-
userDao.deleteUser(username);
85+
public int delete(@PathVariable String username) {
86+
return userDao.deleteUser(username);
9187
}
9288

9389
/**
@@ -118,9 +114,13 @@ public List<String> addRole(@PathVariable String username, @RequestBody String r
118114
* @param username The username of the user.
119115
* @param role The role to delete.
120116
*/
121-
@ResponseStatus(HttpStatus.NO_CONTENT)
122117
@DeleteMapping(path = "/{username}/roles/{role}")
123-
public void deleteRole(@PathVariable String username, @PathVariable String role) {
124-
userDao.deleteRole(username, role.toUpperCase());
118+
public int deleteRole(@PathVariable String username, @PathVariable String role) {
119+
var affectedRows = userDao.deleteRole(username, role.toUpperCase());
120+
if (affectedRows == 0) {
121+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Role not found");
122+
} else {
123+
return affectedRows;
124+
}
125125
}
126126
}

java-springboot-final/src/main/java/org/example/daos/UserDao.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ public User updatePassword(User user) {
100100
*
101101
* @param username The username of the user.
102102
*/
103-
public void deleteUser(String username) {
103+
public int deleteUser(String username) {
104104
String sql = "DELETE FROM users WHERE username = ? ";
105-
jdbcTemplate.update(sql, username);
105+
return jdbcTemplate.update(sql, username);
106106
}
107107

108108
/**
@@ -137,9 +137,9 @@ public List<String> addRole(String username, String role) {
137137
* @param username The username of the user.
138138
* @param role The role to delete.
139139
*/
140-
public void deleteRole(String username, String role) {
140+
public int deleteRole(String username, String role) {
141141
String sql = "DELETE FROM roles WHERE username = ? AND role = ?";
142-
jdbcTemplate.update(sql, username, role);
142+
return jdbcTemplate.update(sql, username, role);
143143
}
144144

145145
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.example.models;
2+
3+
/**
4+
* Model for an order.
5+
*/
6+
public class Order {
7+
/**
8+
* The ID of the order.
9+
*/
10+
private int id;
11+
12+
/**
13+
* The username of the order.
14+
*/
15+
private String username;
16+
17+
/**
18+
* Creates a new order.
19+
*/
20+
public Order() {
21+
}
22+
23+
/**
24+
* Creates a new order.
25+
*
26+
* @param id The ID of the order.
27+
* @param username The username of the order.
28+
*/
29+
public Order(int id, String username) {
30+
this.id = id;
31+
this.username = username;
32+
}
33+
34+
/**
35+
* Gets the ID of the order.
36+
*
37+
* @return int
38+
*/
39+
public int getId() {
40+
return id;
41+
}
42+
43+
/**
44+
* Sets the ID of the order.
45+
*
46+
* @param id The ID of the order.
47+
*/
48+
public void setId(int id) {
49+
this.id = id;
50+
}
51+
52+
/**
53+
* Gets the username of the order.
54+
*
55+
* @return String
56+
*/
57+
public String getUsername() {
58+
return username;
59+
}
60+
61+
/**
62+
* Sets the username of the order.
63+
*
64+
* @param username The username of the order.
65+
*/
66+
public void setUsername(String username) {
67+
this.username = username;
68+
}
69+
}

0 commit comments

Comments
 (0)