Skip to content

Commit

Permalink
Improvements to v2 (#2)
Browse files Browse the repository at this point in the history
* add dtos to crud operations

* exception handling to controllers

* refactor delete employee by id

* refactor controller endpoints urls

* add the postman collection

* update readme

* update postman collection
  • Loading branch information
andrecaiado authored Aug 29, 2024
1 parent dfcfad4 commit ec79c78
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 80 deletions.
70 changes: 59 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,86 @@
# Spring Boot Template project
This is a template project for Spring Boot applications.

## Features
# Contents

- [Features](#features)
- [Requirements](#requirements)
- [Getting Started](#getting-started)
- [Installation](#installation)
- [Try it out with the postman collection](#try-it-out-with-the-postman-collection)
- [PostgreSQL database](#postgresql-database)
- [Database migrations with Flyway](#database-migrations-with-flyway)
- [Spring Boot Docker Compose](#spring-boot-docker-compose)
- [In-memory database for testing](#in-memory-database-for-testing)
- [Running the application](#running-the-application)

# Features
- Exposes an API to perform CRUD operations on a single entity
- Persists the data in a PostgreSQL database
- Uses Spring Data JPA for database operations
- Uses Flyway for database migrations
- Uses Spring Boot Docker Compose to start and stop a Docker container running the PostgreSQL database
- Includes a datasource configuration for testing purposes that uses the H2 in-memory database

### PostgreSQL database
# Requirements

- Java 17
- Docker
- Docker Compose

# Getting Started

This section provides a step-by-step guide on how to run the project.

## Installation

1. Clone the repository by executing the following command:

```shell
git clone [email protected]:andrecaiado/spring-boot-template.git
```

2. Navigate into the project directory:

```
cd your-repository-name
```

3. Install the dependencies by executing the following command:

```shell
./mvnw clean install
```

4. Run the application by executing the following command:

```shell
./mvnw spring-boot:run
```

## Try it out with the postman collection

The Postman collection is available here: [spring-boot-template-rest-api.postman_collection.json](spring-boot-template-rest-api.postman_collection.json)


# PostgreSQL database
The PostrgreSQL configuration is located in `src/main/resources/application.yaml`.

The datasource configuration is located in the `docker-compose.yml` file so it can be picked up by Spring Boot Docker Compose.

### Database migrations with Flyway
## Database migrations with Flyway
Flyway configuration is located in `src/main/resources/application.yaml`.

The migration files are located at `src/main/resources/db/migration`.

### Spring Boot Docker Compose
# Spring Boot Docker Compose
With Spring Boot Docker Compose, the container running the PostgreSQL database will be automatically started when the application is started and stopped when the application is stopped.

Spring Boot Docker Compose will detect and use the `docker-compose.yml` file located in the root of the project.

The data source will be configured with the properties defined in the `docker-compose.yml` file.

### In-memory database for testing
# In-memory database for testing
The datasource configuration is located in `src/test/resources/application.yaml`.

When a test loads the application context or explicitly calls this configuration, the H2 in-memory database will be initialized.
Expand All @@ -42,9 +96,3 @@ spring:
baselineOnMigrate: true
enabled: true
```
### Running the application
To run the application, execute the following command:
```shell
mvn spring-boot:run
```
156 changes: 156 additions & 0 deletions spring-boot-template-rest-api.postman_collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
{
"info": {
"_postman_id": "b242b415-22c5-46ad-b547-58e9cb66f06c",
"name": "Spring Boot template REST API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "4423866"
},
"item": [
{
"name": "Get employee by id",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "localhost:8080/api/v1/employees/21",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"v1",
"employees",
"21"
]
}
},
"response": []
},
{
"name": "Get employees",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "localhost:8080/api/v1/employees?page=0&size=3",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"v1",
"employees"
],
"query": [
{
"key": "page",
"value": "0"
},
{
"key": "size",
"value": "3"
}
]
}
},
"response": []
},
{
"name": "Create employee",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"firstName\": \"David\",\n \"lastName\": \"Brent\",\n \"age\": 50,\n \"designation\": \"Inverse local framework\",\n \"phoneNumber\": \"501-193-9165\",\n \"joinedOn\": \"2023-06-12\",\n \"address\": \"11th Floor\",\n \"dateOfBirth\": \"2023-03-01\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:8080/api/v1/employees",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"v1",
"employees"
]
}
},
"response": []
},
{
"name": "Update employee",
"request": {
"method": "PUT",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"firstName\": \"Michael\",\n \"lastName\": \"Scott\",\n \"age\": 50,\n \"designation\": \"Inverse local framework\",\n \"phoneNumber\": \"501-193-9165\",\n \"joinedOn\": \"2023-06-12\",\n \"address\": \"11th Floor\",\n \"dateOfBirth\": \"2023-03-01\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:8080/api/v1/employees/21",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"v1",
"employees",
"21"
]
}
},
"response": []
},
{
"name": "Delete employee by id",
"request": {
"method": "DELETE",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"id\": 1,\n \"firstName\": \"Blayne\",\n \"lastName\": \"Calleja\",\n \"age\": 100,\n \"designation\": \"Inverse local framework\",\n \"phoneNumber\": \"501-193-9165\",\n \"joinedOn\": \"2023-06-12\",\n \"address\": \"11th Floor\",\n \"dateOfBirth\": \"2023-03-01\",\n \"createdAt\": \"2023-09-22T00:19:34\",\n \"updatedAt\": \"2023-07-01T13:26:53\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:8080/api/v1/employees/21",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"v1",
"employees",
"21"
]
}
},
"response": []
}
]
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package com.example.springboottemplate.controller;

import com.example.springboottemplate.entity.Employee;
import com.example.springboottemplate.dto.CreateEmployeeDto;
import com.example.springboottemplate.dto.EmployeeDto;
import com.example.springboottemplate.service.EmployeeService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/employee/v1")
@RequestMapping("/api/v1/employees")
public class EmployeeController {

private final EmployeeService employeeService;
Expand All @@ -17,68 +21,30 @@ public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}

/**
* This method is called when a GET request is made
* URL: localhost:8080/employee/v1/
* Purpose: Fetches all the employees in the employee table
* @return List of Employees
*/
@GetMapping("/")
public ResponseEntity<List<Employee>> getAllEmployees(){
return ResponseEntity.ok().body(employeeService.getAllEmployees());
@GetMapping()
public ResponseEntity<List<EmployeeDto>> getAllEmployees(@PageableDefault(page = 0, size = 10) Pageable pageable){
return ResponseEntity.ok().body(employeeService.getAllEmployees(pageable));
}

/**
* This method is called when a GET request is made
* URL: localhost:8080/employee/v1/1 (or any other id)
* Purpose: Fetches employee with the given id
* @param id - employee id
* @return Employee with the given id
*/
@GetMapping("/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable Integer id)
{
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable Integer id) {
return ResponseEntity.ok().body(employeeService.getEmployeeById(id));
}

/**
* This method is called when a POST request is made
* URL: localhost:8080/employee/v1/
* Purpose: Save an Employee entity
* @param employee - Request body is an Employee entity
* @return Saved Employee entity
*/
@PostMapping("/")
public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee)
{
return ResponseEntity.ok().body(employeeService.saveEmployee(employee));
@PostMapping()
public ResponseEntity<EmployeeDto> saveEmployee(@RequestBody CreateEmployeeDto employee) {
return new ResponseEntity<>(employeeService.saveEmployee(employee), HttpStatus.CREATED);
}

/**
* This method is called when a PUT request is made
* URL: localhost:8080/employee/v1/
* Purpose: Update an Employee entity
* @param employee - Employee entity to be updated
* @return Updated Employee
*/
@PutMapping("/")
public ResponseEntity<Employee> updateEmployee(@RequestBody Employee employee)
{
return ResponseEntity.ok().body(employeeService.updateEmployee(employee));
@PutMapping("/{id}")
public ResponseEntity<EmployeeDto> updateEmployee(@PathVariable Integer id, @RequestBody CreateEmployeeDto employee) {
return ResponseEntity.ok().body(employeeService.updateEmployee(id, employee));
}

/**
* This method is called when a PUT request is made
* URL: localhost:8080/employee/v1/1 (or any other id)
* Purpose: Delete an Employee entity
* @param id - employee's id to be deleted
* @return a String message indicating employee record has been deleted successfully
*/
@DeleteMapping("/{id}")
public ResponseEntity<String> deleteEmployeeById(@PathVariable Integer id)
{
public ResponseEntity<String> deleteEmployeeById(@PathVariable Integer id) {
employeeService.deleteEmployeeById(id);
return ResponseEntity.ok().body("Deleted employee successfully");
return ResponseEntity.ok().body("Employee deleted successfully");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.springboottemplate.dto;

import java.time.LocalDate;

public record CreateEmployeeDto(String firstName, String lastName, Integer age, String designation, String phoneNumber, LocalDate joinedOn, String address, LocalDate dateOfBirth) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.springboottemplate.dto;

import java.time.LocalDate;
import java.time.LocalDateTime;

public record EmployeeDto(Integer id, String firstName, String lastName, Integer age, String designation, String phoneNumber, LocalDate joinedOn, String address, LocalDate dateOfBirth, LocalDateTime createdAt, LocalDateTime updatedAt) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String firstName;
private String lastName;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.springboottemplate.exception;

public class EmployeeNotFoundException extends RuntimeException {
public EmployeeNotFoundException(String message) {
super(message);
}
}
Loading

0 comments on commit ec79c78

Please sign in to comment.