Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
1615f8e
create Word class and controller
AlbertProfe Jan 10, 2025
55f09b9
add repository and application.properties
AlbertProfe Jan 10, 2025
17056d0
create controller word method get all words
AlbertProfe Jan 10, 2025
4116bbd
spring boot rest controller docs
AlbertProfe Jan 10, 2025
8f2660e
renaming files
AlbertProfe Jan 10, 2025
5cbb18b
renaming files
AlbertProfe Jan 10, 2025
656ac27
WordController comments
AlbertProfe Jan 16, 2025
0b15c36
test with JUnit to word entity
AlbertProfe Jan 16, 2025
b5c117a
spring docs-resources images project structure
AlbertProfe Jan 16, 2025
aa68930
spring docs-resources test JUnit word
AlbertProfe Jan 16, 2025
0eb0b0f
db local
AlbertProfe Jan 17, 2025
557b0d6
new folder Test Junit
AlbertProfe Jan 17, 2025
f596df5
basic wordController CRUD
AlbertProfe Jan 17, 2025
8fd89c9
H2 local db and word controller
AlbertProfe Jan 17, 2025
8fdc1ad
move md
AlbertProfe Jan 17, 2025
6c6a27e
doc: containers and pronunciationApp-v0.1
AlbertProfe Jan 23, 2025
50b30db
feat: refactor controller and repository
AlbertProfe Jan 23, 2025
052eeef
doc: spring boot for 5yo
AlbertProfe Jan 23, 2025
bc9c5ee
fix: getWordByPhoneticSpelling at WordRepository pronunciation fields…
AlbertProfe Jan 23, 2025
c4bc3db
feat: createWords and deleteAllWords
AlbertProfe Jan 23, 2025
52be154
doc: styling docs
AlbertProfe Jan 23, 2025
04dde27
doc: annotations
AlbertProfe Jan 24, 2025
7685ec5
doc: add mock-data script
AlbertProfe Jan 24, 2025
eea3805
doc: image h2 with mock data
AlbertProfe Jan 24, 2025
90e659a
feat: refactor with ResponseEntity, headers and optional
AlbertProfe Jan 24, 2025
ec64ac1
doc: import .csv to H2 DB
AlbertProfe Jan 24, 2025
d6590ad
doc: mock Server postman
AlbertProfe Jan 29, 2025
0313b8c
doc: update mock Server Postman
AlbertProfe Jan 29, 2025
e9fc851
doc: java faker
AlbertProfe Jan 30, 2025
b297b2a
doc: jpa
AlbertProfe Jan 30, 2025
072c3e2
feat: refactor WordController, implement WordService and create Healt…
AlbertProfe Jan 30, 2025
39a3371
doc: pronunciationApp-v0.2.d and spring 5yo
AlbertProfe Jan 30, 2025
1561d0b
doc: task definition and estimated time for each task
emsmccoy Jan 30, 2025
1e814b1
feat: create User entity
emsmccoy Jan 30, 2025
3dee29a
feat: improve User entity with AI suggestions
emsmccoy Jan 30, 2025
fad4d9c
doc: first task completed
emsmccoy Jan 30, 2025
41e879a
feat: create files for UserAppController and UserAppService
emsmccoy Jan 30, 2025
bb9e867
feat: create files for UserController, UserRepository and UserService
emsmccoy Jan 30, 2025
b1233ef
fix: renamed UserApp to User and added annotation and schema for quer…
emsmccoy Jan 30, 2025
a2ce67b
Merge remote-tracking branch 'origin/PRA02-EmmaAlonsoMcCoy' into PRA0…
emsmccoy Jan 30, 2025
5e6c268
feat: implement UserController functions
emsmccoy Feb 1, 2025
cb9c12a
feat: finish implementing UserController functions
emsmccoy Feb 3, 2025
8c9c40a
feat: include user count in HealthController
emsmccoy Feb 3, 2025
197c1a9
doc: userController implementation
emsmccoy Feb 3, 2025
75c59f8
feat: set up connection to h2 and create data.sql for mock users
emsmccoy Feb 5, 2025
5c2defa
doc: finish common part and add Postman Runner image
emsmccoy Feb 5, 2025
a967de9
doc: Update README.md
emsmccoy Feb 5, 2025
3688f0c
feat: [optional] connect to Postgres DB. Change UUID in data.sql to e…
emsmccoy Feb 6, 2025
1ba23b3
doc: add images for connection to H2, pgAdmin 4, postgres terminal an…
emsmccoy Feb 6, 2025
a94b5bd
feat: implement DataInitializer with java faker
emsmccoy Feb 6, 2025
f8fcf79
doc: streams to create a list of objects from an int range
emsmccoy Feb 6, 2025
346fcc9
feat: create UserServiceUnitTest
emsmccoy Feb 7, 2025
ec5caa9
doc: document UserServiceUnitTest time
emsmccoy Feb 7, 2025
e596a10
fix: refactor deleteUserById by removing @RequestBody User user
emsmccoy Feb 7, 2025
e8142a2
feat: create UserControllerIntegrationTest
emsmccoy Feb 7, 2025
bc52fad
doc: document integration testing and final times
emsmccoy Feb 7, 2025
7f5dc90
doc: correct links for images
emsmccoy Feb 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added PRA/PRA02-H2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PRA/PRA02-PostgresTerminal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PRA/PRA02-PostmanRunner-Postgres.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PRA/PRA02-PostmanRunner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PRA/PRA02-pgAdmin4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# PRA#02-SpringBoot: Create User API Rest

## CIFO La Violeta - FullStack IFCD0210-25 MF01

This document serves as a guide and log for the backend development of the PRA#02 Spring Boot project.

---

## PR Submission Checklist

### **Common Tasks:**

- [x] Create User @Entity
- [x] Create UserController (Rest API controller)
- [x] Implement UserRepository
- [x] Configure application properties with local H2 database
- [x] Develop UserService
- [x] Test all endpoints with Postman

### Optional Tasks:

- [x] Configure Postgres database
- [x] Implement Faker for test data
- [x] Add unit tests for services
- [x] Integration tests for controllers

### **Testing**:

- [x] All endpoints tested in Postman.
- [x] Error handling implemented in controllers.
- [x] Data persistence verified in H2 database.

---

## Estimated Time for Tasks

### Common Part

| Task | Estimated Time | Actual Time | Impediments (if any) | New Concepts |
| --------------------------- | -------------- | -------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Create User @Entity | 1 hours | 45 min | | @PrePersist<br/>@GeneratedValue(strategy = GenerationType.UUID)<br/>@Column(unique = true, nullable = false) |
| Create UserController | 1 hours | 1.5 hour | | ResponseEntity utility methods <br/> Centralize headers handling using helper method |
| Implement UserRepository | 0.5 hours | 0.5 hours | | |
| Configure H2 database | 0.5 hours | 0.5 hours | | |
| Develop UserService | 2 hours | 1 hours | | |
| Test endpoints with Postman | 1.5 hours | 2 hours | data.sql only executes correctly if the table has been created previously | data.sql to introduce mock data <br/>postman data file to test and run collections |
| **Total** | **6.5 hours** | **6.25 hours** | | |

### Optional Part

| Task | Estimated Time | Actual Time | Impediments (if any) | New Concepts |
| --------------------------------- | -------------- | ------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Configure Postgres database | 2.5 hours | 2.5 hours | | |
| Implement Faker for test data | 1 hours | 1.5 hours | | Use Faker dinamically via API (not implemented) <br/> Streams: IntStream.range(x, y).mapToObj(i -> ...).collect(Collectors.toList() |
| Add unit tests for services | 2 hours | 1.5 hours | | Mockito JUnit integration |
| Integration tests for controllers | 2 hours | 1 hour so far | | MockMvc and Fluent API |
| **Total** | **7.5 hours** | **6.5 hours** | | |

---

## Images

### <u>H2</u>

#### Database Connection

![](./PRA/PRA02-H2.png)

#### Postman Runner Results

![](./PRA/PRA02-PostmanRunner.png)

### <u>Postgres</u>

#### Database Connection (pgAdmin 4 & terminal)

![](./PRA/PRA02-pgAdmin4.png)

![](/./PRA/PRA02-PostgresTerminal.png)

#### Postman Runner Results

![](./PRA/PRA02-PostmanRunner-Postgres.png)

---

## Future Improvements

- <mark>Implement Global Exception Handling.</mark>
- Add more unit and integration tests for key functionalities.
- Implement custom queries for enhanced user search.
- Optimize service layer for better performance and maintainability.
- Enhance validation for user input fields.

---
10 changes: 10 additions & 0 deletions backend/pronunciationAppBack/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand Down Expand Up @@ -65,6 +70,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package dev.pronunciationAppBack.controller;


import dev.pronunciationAppBack.service.UserService;
import dev.pronunciationAppBack.service.WordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/health")
public class HealthController {

@Autowired
private WordService wordService;

@Autowired
private UserService userService;

@GetMapping
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> healthStatus = new HashMap<>();
healthStatus.put("status", "UP");
healthStatus.put("timestamp", new Date());
healthStatus.put("wordCount", wordService.getWordCount());
healthStatus.put("userCount", userService.getUserCount());

// verify database connection
boolean databaseConnection = checkDatabaseConnection();
healthStatus.put("database", databaseConnection ? "Connected" : "Disconnected");

// Add more health checks as needed
healthStatus.put("memoryUsage", getMemoryUsage());
healthStatus.put("diskSpace", getDiskSpace());

HttpHeaders headers = getCommonHeaders("Health check endpoint");
HttpStatus status = databaseConnection ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;

return new ResponseEntity<>(healthStatus, headers, status);
}

private boolean checkDatabaseConnection() {
try {
wordService.getAllWords();
return true;
} catch (Exception e) {
return false;
}
}

private Map<String, Long> getMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
Map<String, Long> memoryInfo = new HashMap<>();
memoryInfo.put("total", runtime.totalMemory());
memoryInfo.put("free", runtime.freeMemory());
memoryInfo.put("used", runtime.totalMemory() - runtime.freeMemory());
return memoryInfo;
}

private Map<String, Long> getDiskSpace() {
java.io.File root = new java.io.File("/");
Map<String, Long> diskInfo = new HashMap<>();
diskInfo.put("total", root.getTotalSpace());
diskInfo.put("free", root.getFreeSpace());
diskInfo.put("usable", root.getUsableSpace());
return diskInfo;
}

private HttpHeaders getCommonHeaders(String description) {
HttpHeaders headers = new HttpHeaders();
headers.add("desc", description);
headers.add("content-type", "application/json");
headers.add("date", new Date().toString());
headers.add("server", "Spring Boot");
headers.add("version", "1.0.0");
return headers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package dev.pronunciationAppBack.controller;

import dev.pronunciationAppBack.service.UserService;
import dev.pronunciationAppBack.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
public UserService userService;

private HttpHeaders getHeaders(String message) {
HttpHeaders headers = new HttpHeaders();
headers.add("Description", message);
return headers;
}

@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();

return !users.isEmpty()
? ResponseEntity.ok().headers(getHeaders("Returning all users")).body(users)
: ResponseEntity.notFound().headers(getHeaders("No users found")).build();
}

@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable String id) {
Optional<User> user = userService.getUserById(id);

return user.map(u -> ResponseEntity.ok().headers(getHeaders("User found")).body(u))
.orElseGet(() -> ResponseEntity.notFound().headers(getHeaders("User not found")).build());
}

@PostMapping("/createUser")
public ResponseEntity<User> createUser(@RequestBody User user){
User createdUser = userService.createUser(user);

//return ResponseEntity.ok().body(createdUser);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);

//URI location = URI.create("/users/" + createdUser.getId());
//return ResponseEntity.created(location).headers(getHeaders("User created successfully")).body(user);
}

@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable String id, @RequestBody User user) {
if (user.getId() != null && !user.getId().equals(id)) {
return ResponseEntity.badRequest().headers(getHeaders("Mismatch between ID and user ID in body request")).build();
} else {
User updatedUser = userService.updateUser(user);
return ResponseEntity.ok().headers(getHeaders("User updated")).body(updatedUser);
}
}

@DeleteMapping("/{id}")
public ResponseEntity<String> deleteUserById(@PathVariable String id){
if (userService.existsById(id)) {
userService.deleteUser(id);
return ResponseEntity.ok().headers(getHeaders("User deleted successfully")).build();
} else {
return ResponseEntity.notFound().headers(getHeaders("User not found")).build();
}
}

@DeleteMapping
public ResponseEntity<String> deleteAllUsers(){
userService.deleteAllUsers();
if (userService.getAllUsers().isEmpty()){
return ResponseEntity.ok().headers(getHeaders("All users deleted succesfully")).build();
} else {
return ResponseEntity.notFound().headers(getHeaders("Could not delete all users")).build();
}
}

// private HttpHeaders getCommonHeaders(String description) {
// HttpHeaders headers = new HttpHeaders();
// headers.add("desc", description);
// headers.add("content-type", "application/json");
// headers.add("date", new Date().toString());
// headers.add("server", "Spring Boot");
// headers.add("version", "1.0.0");
// headers.add("word-count", String.valueOf(userService.getUserCount()));
// headers.add("object", "words");
// return headers;
// }
}
Loading