Welcome to the final project for the Java Spring Boot unit. In this project, you will be working with a Spring Boot application that includes a database for a web store and an already implemented authentication and authorization system. You will be responsible for implementing the REST endpoints that allow users to interact with the store, and securing those endpoints using Spring Boot's security annotations.
To get started, open the project file located in
java-spring-boot-final/pom.xml
in IntelliJ.
This project contains a Spring Boot application with the necessary dependencies to create a REST API backed by a mysql database with authentication and authorization. The application is already configured to use the database and the authentication and authorization system is already implemented. However, the REST endpoints that allow users to interact with the rest of the database are not yet implemented.
The application source contains the following directories:
src/main/java/org.example/models
- Contains the model classes that represent the data in the database. You will not need to create any new model classes.src/main/java/org.example/daos
- Contains the data access objects that interact with the database. Currently, there is only one DAO class,UserDao
, which interacts with theusers
table.src/main/java/org.example/services
- Contains the service classes that provide services that are not directly related to the database. The only service class currently isCustomUserDetailsService
, which provides the authentication system andauth/login
endpoint. You will not need to create any new service classes for this project.src/main/java/org.example/controllers
- Contains the controller classes that provide the REST endpoints for the application. Currently, there are two controller classes,UserController
andProfileController
. TheUserController
class provides methods for ADMIN users to create, read, update, and delete users. TheProfileController
class provides methods for users to read and update their own profiles.src/main/java/org.example/exceptions
- Contains the exception classes that are thrown by the application. Currently, there is only one exception class,DAOException
, which is thrown when an error occurs in the DAO classes. You will not need to create any new exception classes for this project.
To create the database, you will need to run the create-database.sql
script
located in the sql
directory. You can run this script by opening MySql
Workbench and opening the script file. Then, you can execute the script by
clicking on the lightning bolt icon in the toolbar.
To configure the application to connect to the database, you will need to
modify the application.properties
file located in the src/main/resources
directory. You will need to set the following properties:
spring.datasource.username
- The username for your mysql server.spring.datasource.password
- The password for your mysql server.
To run the application, you can right-click on the SpringBootApplication
class in IntelliJ and select "Run WebStoreApplication". This will start the
application and you should see output in the console indicating that the
application has started.
If you see an error message in the console, you may need to check the
application.properties
file to make sure that the database connection
properties are set correctly, and make sure that you have created the database
using the create-database.sql
script.
To help you test the REST endpoints, a Postman collection has been provided
that contains a set of requests that you can use to interact with the REST
endpoints. You can find the Postman collection in the postman
directory.
To load the Postman collection, open Postman and click on the "Import" button
in the top left corner. Then, click on the "Choose Files" button and select
the java-spring-boot-final/postman/web-store.postman_collection.json
Once you have imported the collection, you should see a new collection called "Web Store" in the left sidebar. You can expand the collection to see the requests that are available.
Make sure that the application is running before you try to execute the requests in Postman.
You should first execute the login
request to authenticate as an admin user.
Then, you can execute the other requests to interact with the REST endpoints.
The requests in the Profile
folder and the User
folder are already
implemented and should work correctly as long as you run the login
request
first to authenticate as an admin user. The requests in the Products
,
Orders
, and OrderItems
folders are not yet implemented and will not work
until you implement the REST endpoints.
Note
You should not need to modify the postman collection, but you may change the request bodies or parameters if you want to test different scenarios.
Your task is to implement the REST endpoints that allow users to interact with the store. You will need to create a DAO class and a controller class for each of the tables in the database.
Stop the application and follow the steps below to complete the exercise.
In the models
folder, notice that there is a class for each of the tables in
the database. You will need to use these classes to interact with the database
in the DAO classes and controller classes.
In the daos
folder, create a new DAO class for each of the tables in the
database (other than the users
and roles
tables). Each DAO class should
contain methods for creating, reading, updating, and deleting items in the
table:
getAll
- Retrieves all items from the table.getById
- Retrieves an item by its id.create
- Creates a new item in the table.update
- Updates an existing item in the table.delete
- Deletes an item from the table.
You can refer to the existing UserDao
class for an example of what these
classes should look like. However, note that the UserDao
class contains an
injected PasswordEncoder
bean that is used to encode passwords. You will not
need to use this bean in your DAO classes.
Note
Each DAO class should be annotated with @Component
annotation so
that it can be injected into the controller classes.
As in the UserDao
class, each DAO class should contain a JdbcTemplate
object that is created from a DataSource
object that is injected through the
constructor.
For example:
@Component
public class ProductDao {
private JdbcTemplate jdbcTemplate;
public ProductDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// ...
}
Each DAO class will also need a mapping method that maps a ResultSet
object
to a model object so that your SELECT
queries can map the results to the
appropriate model objects. You can refer to the UserDao
class for an example
of what this method should look like.
Create a new controller class for each of the tables in the database (other
than the users
and roles
tables). Each controller class should contain
methods providing REST endpoints for creating, reading, updating, and deleting
items in the table. These endpoints are detailed below.
You can refer to the existing UserController
and ProfileController
classes
for an example of what these classes should look like.
Note that each controller class injects its corresponding DAO class through
an @Autowired
class member. You will need to do the same for your controller
classes.
For example:
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductDao productDao;
// ...
}
Note
Your "Get by Id" methods should return a 404 status code if the item is not
found in the database. You can do this by throwing a ResponseStatusException
with a HttpStatus.NOT_FOUND
status code. (Refer to the UserController
class
for an example of how to do this.)
When you are finished, you should have implemented all of the REST endpoints in the Postman collection, and they should all function properly and return appropriate data.
GET /products
- Retrieves all products.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.POST /products
- Creates a new product from the request body and returns the created product with a 201 CREATED http status code.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.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.
GET /orders
- Retrieves all orders.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.POST /orders
- Creates a new order from the request body and returns the created order with a 201 CREATED http status code.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.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.
GET /order-items
- Retrieves all order items.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.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.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.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.
You can verify that the application is working correctly by running the application and then launching Postman to test the REST endpoints. You should be able to create, read, update, and delete items in the database using the REST endpoints that you created.
You can also verify that the application is working correctly by running the
included unit tests. The unit tests are located in the src/test/java
directory.
To run the unit tests, right-click on the src/test/java
directory and select
"Run All Tests".
Warning
DO NOT modify the unit tests. Your project will be evaluated based on the unit tests all passing. Modifying the unit tests will result in a failing grade.
Your project will be evaluated based on the unit tests all passing.
Add the @PreAuthorize
annotation to all of the REST endpoints in the
controller classes to require that users be authenticated in order to access
the endpoints. You can use the isAuthenticated()
expression to require that
users be authenticated.
Add a Principal
argument to the create and update endpoints for the
Order
controller that will allow you to get the username of the user and
overwrite the username
field in the passed Order
object. This will
guaranatee that the username
field is always set to the username of the
user that created or updated the order.
For example:
@PostMapping
public void createOrder(@RequestBody Order order, Principal principal) {
String username = principal.getName();
order.setUsername(username);
// ...
}
Add optional user
query parameters to the Orders
GET endpoint that will
allow users to only retrieve orders that belong to a specific user. If the
user
query parameter is not provided, the endpoint should return all orders.
@GetMapping
public List<Order> listOrders(@RequestParam(required = false) String username) {
if (username != null) {
return orderDao.getOrdersByUsername(username);
} else {
return orderDao.getOrders();
}
}
Note
You will need to create a new method in the OrderDao
class that retrieves
orders by username.
Add optional orderId
query parameters to the OrderItems
GET endpoint that
will allow users to only retrieve order items that belong to a specific order.
If the orderId
query parameter is not provided, the endpoint should return
all order items.
@GetMapping
public List<OrderItem> listOrderItems(@RequestParam(required = false) Long orderId) {
if (orderId != null) {
return orderItemDao.getOrderItemsByOrderId(orderId);
} else {
return orderItemDao.getOrderItems();
}
}
Note
You will need to create a new method in the OrderItemDao
class that retrieves
order items by order id.