diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..aad394d07
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+.idea/
+
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..26d33521a
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..87ad010fe
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..63287a14d
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/pronunciationApp.iml b/.idea/pronunciationApp.iml
new file mode 100644
index 000000000..d6ebd4805
--- /dev/null
+++ b/.idea/pronunciationApp.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/.gitignore b/backend/.idea/.gitignore
new file mode 100644
index 000000000..26d33521a
--- /dev/null
+++ b/backend/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/backend/.idea/backend.iml b/backend/.idea/backend.iml
new file mode 100644
index 000000000..d6ebd4805
--- /dev/null
+++ b/backend/.idea/backend.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/compiler.xml b/backend/.idea/compiler.xml
new file mode 100644
index 000000000..564a55468
--- /dev/null
+++ b/backend/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/encodings.xml b/backend/.idea/encodings.xml
new file mode 100644
index 000000000..2c53f5a37
--- /dev/null
+++ b/backend/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/jarRepositories.xml b/backend/.idea/jarRepositories.xml
new file mode 100644
index 000000000..712ab9d98
--- /dev/null
+++ b/backend/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/misc.xml b/backend/.idea/misc.xml
new file mode 100644
index 000000000..3534a7794
--- /dev/null
+++ b/backend/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/modules.xml b/backend/.idea/modules.xml
new file mode 100644
index 000000000..e066844ef
--- /dev/null
+++ b/backend/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/.idea/vcs.xml b/backend/.idea/vcs.xml
new file mode 100644
index 000000000..6c0b86358
--- /dev/null
+++ b/backend/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/pronunciationAppBack/.gitignore b/backend/pronunciationAppBack/.gitignore
index 549e00a2a..aad394d07 100644
--- a/backend/pronunciationAppBack/.gitignore
+++ b/backend/pronunciationAppBack/.gitignore
@@ -18,6 +18,8 @@ target/
*.iws
*.iml
*.ipr
+.idea/
+
### NetBeans ###
/nbproject/private/
@@ -31,3 +33,4 @@ build/
### VS Code ###
.vscode/
+
diff --git a/backend/pronunciationAppBack/pom.xml b/backend/pronunciationAppBack/pom.xml
index 5963cf3b7..86715b863 100644
--- a/backend/pronunciationAppBack/pom.xml
+++ b/backend/pronunciationAppBack/pom.xml
@@ -12,7 +12,7 @@
pronunciationAppBack
0.0.1-SNAPSHOT
pronunciationAppBack
- Spring Boot for app pronunciatoin
+ Spring Boot for app pronunciation
@@ -27,7 +27,7 @@
- 17
+ 21
@@ -38,7 +38,11 @@
org.springframework.boot
spring-boot-starter-web
-
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ 3.4.2
+
org.springframework.boot
spring-boot-devtools
@@ -58,13 +62,19 @@
org.projectlombok
lombok
- true
+ 1.18.30
+ provided
org.springframework.boot
spring-boot-starter-test
test
+
+ com.github.javafaker
+ javafaker
+ 1.0.2
+
@@ -79,7 +89,10 @@
lombok
-
+ 21
+ 21
+ --enable-preview
+
org.springframework.boot
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/UserController.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/UserController.java
new file mode 100644
index 000000000..9ad8844b0
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/UserController.java
@@ -0,0 +1,163 @@
+package dev.pronunciationAppBack.controller;
+
+import dev.pronunciationAppBack.model.User;
+import dev.pronunciationAppBack.repository.UserRepository;
+import dev.pronunciationAppBack.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.http.HttpHeaders;
+
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+
+
+@RequestMapping("/api/v1/users")
+@RestController
+public class UserController {
+
+ @Value("${endpoint.url.users}")
+ private String endpointUrlUsers;
+
+ @Autowired
+ private UserService userService;
+
+ @GetMapping("/show-endpoint")
+ public String showEndPointUsers() {
+ return "The Users endpoint URL is: " + endpointUrlUsers;
+ }
+
+ @GetMapping("/allUsers")
+ public ResponseEntity> getAllUsers() {
+ List users = userService.getAllUsers();
+ HttpHeaders headers = getCommonHeaders("Get all users");
+ System.out.println("Number of users: " + users.size());
+ if (users.isEmpty()) {
+ return new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ } else {
+ return new ResponseEntity<>(users, headers, HttpStatus.OK);
+ }
+ }
+
+ @PostMapping
+ public ResponseEntity createUser(@RequestBody User user) {
+ User createdUser = userService.createUser(user);
+ HttpHeaders headers = getCommonHeaders("Create a new user");
+ return createdUser != null
+ ? new ResponseEntity<>(createdUser, headers, HttpStatus.CREATED)
+ : new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity updateUser(@PathVariable("id") String id, @RequestBody User user) {
+ User updatedUser = userService.updateUser(id, user);
+ HttpHeaders headers = getCommonHeaders("Update a user");
+ return updatedUser != null
+ ? new ResponseEntity<>(updatedUser, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deleteUser(@PathVariable("id") String id) {
+ boolean deleted = userService.deleteUser(id);
+ HttpHeaders headers = getCommonHeaders("Delete a user");
+ return deleted
+ ? new ResponseEntity<>(headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getUserById(@PathVariable("id") String id) {
+ User user = userService.getUserById(id);
+ HttpHeaders headers = getCommonHeaders("Get a user");
+ return user != null
+ ? new ResponseEntity<>(user, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ // New endpoints for PRA02
+
+ @GetMapping("/active-users")
+ public ResponseEntity> getActiveUsers() {
+ List activeUsers = userService.getActiveUsers();
+ HttpHeaders headers = getCommonHeaders("Get all active users");
+ return activeUsers.isEmpty() ? ResponseEntity.noContent().headers(headers).build()
+ : ResponseEntity.ok().headers(headers).body(activeUsers);
+ }
+
+ @GetMapping("/created-after")
+ public ResponseEntity> getUsersCreatedAtAfter(@RequestParam LocalDate date) {
+
+ List users = userService.getUsersCreatedAtAfter(date);
+ HttpHeaders headers = getCommonHeaders("Get users created after a specific date");
+ return users.isEmpty() ? ResponseEntity.noContent().headers(headers).build()
+ : ResponseEntity.ok().headers(headers).body(users);
+ }
+
+ @GetMapping("/username/contains")
+ public ResponseEntity> getUserNameContains(@RequestParam String username) {
+ List users = userService.getUserNameContains(username);
+ HttpHeaders headers = getCommonHeaders("Get users by username containing");
+ return users.isEmpty() ? ResponseEntity.noContent().headers(headers).build()
+ : ResponseEntity.ok().headers(headers).body(users);
+
+ }
+
+ @GetMapping("/email/contains")
+ public ResponseEntity> getUserEmailContains(@RequestParam String email) {
+ List users = userService.getUserEmailContains(email);
+ HttpHeaders headers = getCommonHeaders("Get users with email domain");
+ return users.isEmpty() ? ResponseEntity.noContent().headers(headers).build()
+ : ResponseEntity.ok().headers(headers).body(users);
+
+ }
+
+ //
+ @GetMapping("/active/created-after")
+ public ResponseEntity> getActiveUsersCreatedAfter(@RequestParam LocalDate date) {
+ List users = userService.getActiveUsersCreatedAfter(date);
+ HttpHeaders headers = getCommonHeaders("Get active users created after a specific date");
+ return users.isEmpty() ? ResponseEntity.noContent().headers(headers).build()
+ : ResponseEntity.ok().headers(headers).body(users);
+
+ }
+
+ @GetMapping("/email/{email}")
+ public ResponseEntity getUserByEmail(@PathVariable String email) {
+ User user = userService.getUserByEmail(email).orElse(null);
+ HttpHeaders headers = getCommonHeaders("Get a user by email");
+ return user != null
+ ? new ResponseEntity<>(user, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+
+ @GetMapping("/username/{userName}")
+ public ResponseEntity getUserByUserName(@PathVariable String userName) {
+ User user = userService.getUserByUserName(userName).orElse(null);
+ HttpHeaders headers = getCommonHeaders("Get a user by username");
+ return user != null
+ ? new ResponseEntity<>(user, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ @GetMapping("/count")
+ public ResponseEntity countUsers() {
+ return ResponseEntity.ok(userService.countUsers());
+ }
+
+ private HttpHeaders getCommonHeaders(String description) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("description", description);
+ headers.add("content-type", "application/json");
+ headers.add("date", new Date().toString());
+ headers.add("server", "H2 Database");
+ headers.add("version", "1.0.0");
+ headers.add("user-count", String.valueOf(userService.countUsers()));
+ headers.add("object", "users");
+ return headers;
+ }
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/WordController.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/WordController.java
new file mode 100644
index 000000000..742ad0fb3
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/WordController.java
@@ -0,0 +1,96 @@
+package dev.pronunciationAppBack.controller;
+
+
+import dev.pronunciationAppBack.model.Word;
+import dev.pronunciationAppBack.service.WordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.util.Date;
+import java.util.List;
+
+
+@RequestMapping("/api/v1/words")
+@RestController
+public class WordController {
+
+ @Value("${endpoint.url.words}")
+ private String endpointUrlWords;
+
+ @Autowired
+ public WordService wordService;
+
+ @GetMapping("/show-endpoint")
+ public String showEndPointWords() {
+ return "The Words endpoint URL is: " + endpointUrlWords;
+ }
+
+ @GetMapping("/allWords")
+ public ResponseEntity> getAllWords() {
+
+ List words = wordService.getAllWords();
+ HttpHeaders headers = getCommonHeaders("Get all customers");
+ System.out.println("Number of words: " + words.size());
+ for (Word word : words) {
+ System.out.println("Word: " + word);
+ }
+ return !words.isEmpty()
+ ? new ResponseEntity<>(words, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+
+ }
+
+ @PostMapping
+ public ResponseEntity createWord(@RequestBody Word word) {
+ Word createdWord = wordService.createWord(word);
+ HttpHeaders headers = getCommonHeaders("Create a new word");
+ return createdWord != null
+ ? new ResponseEntity<>(createdWord, headers, HttpStatus.CREATED)
+ : new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity updateWord(@PathVariable("id") String id, @RequestBody Word word) {
+ Word updatedWord = wordService.updateWord(id, word);
+ HttpHeaders headers = getCommonHeaders("Update a word");
+ return updatedWord != null
+ ? new ResponseEntity<>(updatedWord, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deleteWord(@PathVariable("id") String id) {
+ boolean deleted = wordService.deleteWord(id);
+ HttpHeaders headers = getCommonHeaders("Delete a word");
+ return deleted
+ ? new ResponseEntity<>(headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getWordById(@PathVariable("id") String id) {
+ Word word = wordService.getWordById(id);
+ HttpHeaders headers = getCommonHeaders("Get a word");
+ return word != null
+ ? new ResponseEntity<>(word, headers, HttpStatus.OK)
+ : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
+ }
+
+ private HttpHeaders getCommonHeaders(String description) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("description", description);
+ headers.add("content-type", "application/json");
+ headers.add("date", new Date().toString());
+ headers.add("server", "H2 Database");
+ headers.add("version", "1.0.0");
+ headers.add("word-count", String.valueOf(wordService.countWords()));
+ headers.add("object", "words");
+
+ return headers;
+ }
+
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/User.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/User.java
new file mode 100644
index 000000000..abbb762b0
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/User.java
@@ -0,0 +1,100 @@
+package dev.pronunciationAppBack.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.Pattern;
+import lombok.*;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.Email;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.UUID;
+
+@AllArgsConstructor
+@NoArgsConstructor
+//@Data
+@Entity
+@Table(name = "\"Users\"")
+public class User {
+ @Id
+ private String id;
+ @NotBlank(message = "Username is required")
+ @Size(min = 3, max = 15)
+ private String userName;
+ @Email(message = "Invalid email format")
+ @NotBlank(message = "Email is required")
+ private String email;
+ @Size(min = 8, max = 20, message = "Password must be at least 8 characters long")
+ @Pattern(
+ regexp = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$",
+ message = "Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character")
+ private String password;
+ private boolean active;
+ private LocalDate createdAt;
+
+ @JsonIgnore
+ @OneToMany(mappedBy = "userMapped", cascade = CascadeType.ALL,
+ fetch = FetchType.LAZY)
+ private List wordsMapped;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public LocalDate getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(LocalDate createdAt) {
+ this.createdAt = createdAt;
+ }
+ @Override
+ public String toString() {
+ return "Customer{" +
+ "id='" + id + '\'' +
+ "name: " + userName + "\n" +
+ "email: " + email + "\n" +
+ "active: " + active + "\n" +
+ "created at: " + createdAt + "\n" +
+ '}';
+ }
+
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/Word.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/Word.java
new file mode 100644
index 000000000..856f84831
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/Word.java
@@ -0,0 +1,100 @@
+package dev.pronunciationAppBack.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.util.UUID;
+
+//@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "Words")
+public class Word {
+
+ @Id
+ private String id;
+ private String wordName;
+ private String definition;
+ private String phoneticSpelling;
+ private String sentence;
+ private boolean isActive;
+ private int level;
+
+ @JsonIgnore
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "WORD_USER_FK_ID")
+ @JsonIgnoreProperties("wordsMapped")
+ private User userMapped;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getWordName() {
+ return wordName;
+ }
+
+ public void setWordName(String wordName) {
+ this.wordName = wordName;
+ }
+
+ public String getDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(String definition) {
+ this.definition = definition;
+ }
+
+ public String getPhoneticSpelling() {
+ return phoneticSpelling;
+ }
+
+ public void setPhoneticSpelling(String phoneticSpelling) {
+ this.phoneticSpelling = phoneticSpelling;
+ }
+
+ public String getSentence() {
+ return sentence;
+ }
+
+ public void setSentence(String sentence) {
+ this.sentence = sentence;
+ }
+
+ public boolean isActive() {
+ return isActive;
+ }
+
+ public void setActive(boolean active) {
+ isActive = active;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "Word{" +
+ "id='" + id + '\'' +
+ ", wordName='" + wordName + '\'' +
+ ", definition='" + definition + '\'' +
+ ", phoneticSpelling='" + phoneticSpelling + '\'' +
+ ", sentence='" + sentence + '\'' +
+ ", isActive=" + isActive +
+ ", level=" + level +
+ '}';
+ }
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/UserRepository.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/UserRepository.java
new file mode 100644
index 000000000..1e82eccc2
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/UserRepository.java
@@ -0,0 +1,38 @@
+package dev.pronunciationAppBack.repository;
+
+import dev.pronunciationAppBack.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+
+
+public interface UserRepository extends JpaRepository {
+ // Find active users
+ List findByActiveTrue();
+
+ // Find users created after a specific date
+ List findByCreatedAtAfter(LocalDate date);
+
+ // Find users by username containing
+ List findByUserNameContaining(String username);
+
+ // Count active users
+ long countByActiveTrue();
+
+ // Find users with email domain
+ List findByEmailContaining(String domain);
+
+ // Complex query with multiple conditions
+ @Query("SELECT u FROM User u WHERE u.active = true AND u.createdAt > :date")
+ List findActiveUsersCreatedAfter(
+ @Param("date") LocalDate date
+ );
+
+ Optional findByEmail(String email);
+
+ Optional findByUserName(String userName);
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/WordRepository.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/WordRepository.java
new file mode 100644
index 000000000..fa84d8e8c
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/WordRepository.java
@@ -0,0 +1,10 @@
+package dev.pronunciationAppBack.repository;
+
+import dev.pronunciationAppBack.model.Word;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.UUID;
+
+
+public interface WordRepository extends JpaRepository {
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserService.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserService.java
new file mode 100644
index 000000000..5e35edf31
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserService.java
@@ -0,0 +1,27 @@
+package dev.pronunciationAppBack.service;
+
+import dev.pronunciationAppBack.model.User;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+
+public interface UserService {
+
+ List getAllUsers();
+ User createUser(User user);
+ User getUserById(String id);
+ User updateUser(String id, User userDetails);
+ boolean deleteUser(String id);
+ long countUsers();
+ // Adding new methods for complex queries
+ List getActiveUsers();
+ List getUsersCreatedAtAfter(LocalDate date);
+ List getUserNameContains(String username);
+ List getUserEmailContains(String email);
+ List getActiveUsersCreatedAfter(LocalDate date);
+ Optional getUserByEmail(String email);
+ Optional getUserByUserName(String userName);
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserServiceImpl.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserServiceImpl.java
new file mode 100644
index 000000000..bfccd67dd
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserServiceImpl.java
@@ -0,0 +1,119 @@
+package dev.pronunciationAppBack.service;
+
+import dev.pronunciationAppBack.model.User;
+import dev.pronunciationAppBack.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+public class UserServiceImpl implements UserService {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ public List getActiveUsers() {
+ return userRepository.findByActiveTrue();
+ }
+
+ @Override
+ public List getUsersCreatedAtAfter(LocalDate date) {
+ return userRepository.findByCreatedAtAfter(date);
+ }
+
+ @Override
+ public List getUserNameContains(String username) {
+ return userRepository.findByUserNameContaining(username);
+ }
+
+ @Override
+ public List getUserEmailContains(String email) {
+ return userRepository.findByEmailContaining(email);
+ }
+
+ @Override
+ public List getActiveUsersCreatedAfter(LocalDate date) {
+ return userRepository.findActiveUsersCreatedAfter(date);
+ }
+
+ @Override
+ public Optional getUserByEmail(String email) {
+ return userRepository.findByEmail(email);
+ }
+
+ @Override
+ public Optional getUserByUserName(String userName) {
+ return userRepository.findByUserName(userName);
+ }
+
+ public long countActiveUsers() {
+ return userRepository.countByActiveTrue();
+ }
+
+ @Override
+ public List getAllUsers() {
+ return userRepository.findAll();
+ }
+
+ @Override
+ public User createUser(User user) {
+ if (userRepository.findByEmail(user.getEmail()).isPresent()) {
+ throw new IllegalArgumentException("A user with this email already exists.");
+ }
+ if (userRepository.findByUserName(user.getUserName()).isPresent()) {
+ throw new IllegalArgumentException("A user with this username already exists.");
+ }
+
+ user.setId(UUID.randomUUID().toString());
+ return userRepository.save(user);
+ }
+
+ @Override
+ public User getUserById(String id) {
+ return userRepository.findById(id).orElse(null);
+ }
+
+ @Override
+ public User updateUser(String id, User userDetails) {
+ User user = userRepository.findById(id).orElse(null);
+ if (user != null) {
+ if (userDetails.getUserName() != null) {
+ user.setUserName(userDetails.getUserName());
+ }
+ if (userDetails.getEmail() != null) {
+ user.setEmail(userDetails.getEmail());
+ }
+ if (userDetails.getPassword() != null) {
+ user.setPassword(userDetails.getPassword());
+ }
+ if (userDetails.isActive()) {
+ user.setActive(userDetails.isActive());
+ }
+ if (userDetails.getCreatedAt() != null){
+ user.setCreatedAt(userDetails.getCreatedAt());
+ }
+ return userRepository.save(user);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean deleteUser(String id) {
+ if (userRepository.existsById(id)){
+ userRepository.deleteById(id);
+ System.out.println("Deleted successfully!");
+ return true;
+ }
+ System.out.println("ID doesn't exist!");
+ return false;
+ }
+
+ @Override
+ public long countUsers() {
+ return userRepository.count();
+ }
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordService.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordService.java
new file mode 100644
index 000000000..fb0f44cc6
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordService.java
@@ -0,0 +1,15 @@
+package dev.pronunciationAppBack.service;
+import java.util.List;
+import java.util.UUID;
+
+import dev.pronunciationAppBack.model.Word;
+
+public interface WordService {
+ List getAllWords();
+ Word createWord(Word word);
+ Word getWordById(String id);
+ Word updateWord(String id, Word wordDetails);
+ boolean deleteWord(String id);
+ long countWords();
+
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordServiceImpl.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordServiceImpl.java
new file mode 100644
index 000000000..a56e5abb9
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/WordServiceImpl.java
@@ -0,0 +1,73 @@
+package dev.pronunciationAppBack.service;
+
+import dev.pronunciationAppBack.model.Word;
+import dev.pronunciationAppBack.repository.WordRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.UUID;
+@Service
+public class WordServiceImpl implements WordService {
+ @Autowired
+ private WordRepository wordRepository;
+
+ @Override
+ public List getAllWords() {
+
+ return wordRepository.findAll();
+ }
+
+ @Override
+ public Word createWord(Word word) {
+ word.setId(UUID.randomUUID().toString());
+ return wordRepository.save(word);
+ }
+
+ @Override
+ public Word getWordById(String id) {
+ return wordRepository.findById(id).orElse(null);
+ }
+
+ @Override
+ public Word updateWord(String id, Word wordDetails) {
+ Word word = wordRepository.findById(id).orElse(null);
+ if (word != null) {
+ if (wordDetails.getWordName() != null) {
+ word.setWordName(wordDetails.getWordName());
+ }
+ if (wordDetails.getDefinition() != null){
+ word.setDefinition(wordDetails.getDefinition());
+ }
+ if (wordDetails.getPhoneticSpelling() != null){
+ word.setPhoneticSpelling(wordDetails.getPhoneticSpelling());
+ }
+ if (wordDetails.getSentence() != null){
+ word.setSentence(wordDetails.getSentence());
+ }
+ if (wordDetails.isActive()){
+ word.setActive(wordDetails.isActive());
+ }
+ if (wordDetails.getLevel() != 0){
+ word.setLevel(wordDetails.getLevel());
+ }
+ return wordRepository.save(word);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean deleteWord(String id) {
+ if (wordRepository.existsById(id)){
+ wordRepository.deleteById(id);
+ System.out.println("Deleted successfully!");
+ return true;
+ }
+ System.out.println("ID doesn't exist!");
+ return false;
+ }
+
+ public long countWords() {
+ return wordRepository.count();
+ }
+}
diff --git a/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/utilities/UserFakerGenerator.java b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/utilities/UserFakerGenerator.java
new file mode 100644
index 000000000..5bae480ab
--- /dev/null
+++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/utilities/UserFakerGenerator.java
@@ -0,0 +1,35 @@
+package dev.pronunciationAppBack.utilities;
+
+import com.github.javafaker.Faker;
+import dev.pronunciationAppBack.model.User;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@Component
+public class UserFakerGenerator {
+
+ private final Faker faker = new Faker();
+
+ public User generateRandomUser() {
+
+ User user = new User();
+ user.setUserName(faker.name().username());
+ user.setEmail(faker.internet().emailAddress());
+ user.setPassword(faker.internet().password());
+ user.setActive(faker.bool().bool());
+ user.setCreatedAt(LocalDate.now());
+
+ // Additional fields if needed
+ return user;
+ }
+
+ public List generateUsers(int count) {
+ return IntStream.range(0, count)
+ .mapToObj(i -> generateRandomUser())
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/backend/pronunciationAppBack/src/main/resources/application.properties b/backend/pronunciationAppBack/src/main/resources/application.properties
index edb9ea0d2..8642bf434 100644
--- a/backend/pronunciationAppBack/src/main/resources/application.properties
+++ b/backend/pronunciationAppBack/src/main/resources/application.properties
@@ -1 +1,31 @@
spring.application.name=pronunciationAppBack
+
+# H2 DATABASE SERVER
+spring.datasource.driverClassName=org.h2.Driver
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.h2.console.enabled=true
+
+# H2 IN MEMORY
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.username=sa
+spring.datasource.password=
+
+# DDL OPTIONS: create-drop, create, update, none, validate
+spring.jpa.hibernate.ddl-auto=create-drop
+
+#spring.datasource.url=jdbc:postgresql://localhost:5432/pronunciation_db
+#spring.datasource.username=albertemb
+#spring.datasource.password=1234
+#spring.datasource.driver-class-name=org.postgresql.Driver
+
+# Config of JPA/Hibernate
+#spring.jpa.hibernate.ddl-auto=update
+#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+
+# ENDPOINTS
+# Import .env file
+spring.config.import=optional:file:.env
+# import from environment variables
+#endpoint.url.users=${ENDPOINT_URL_USERS}
+endpoint.url.users=/api/v1/users/
+endpoint.url.words=/api/v1/words/
\ No newline at end of file
diff --git a/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserControllerIntegrationTest.java b/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserControllerIntegrationTest.java
new file mode 100644
index 000000000..4e5378a68
--- /dev/null
+++ b/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserControllerIntegrationTest.java
@@ -0,0 +1,67 @@
+package dev.pronunciationAppBack;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import dev.pronunciationAppBack.model.User;
+import dev.pronunciationAppBack.repository.UserRepository;
+import dev.pronunciationAppBack.utilities.UserFakerGenerator;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.RequestBuilder;
+import org.springframework.test.web.servlet.ResultMatcher;
+
+import java.util.List;
+
+import static java.nio.file.Paths.get;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.springframework.mock.http.server.reactive.MockServerHttpRequest.post;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+class UserControllerIntegrationTest {
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Autowired
+ UserFakerGenerator userFakerGenerator;
+
+ @Test
+ public void testGenerateUsers() {
+ List users = userFakerGenerator.generateUsers(5);
+
+ assertNotNull(users);
+ assertEquals(5, users.size());
+ users.forEach(System.out::println);
+ }
+
+ @Test
+ void testGetAllUsers() throws Exception {
+
+ mockMvc.perform((RequestBuilder) get("/api/users"))
+ .andExpect(status().isOk())
+ .andExpect((ResultMatcher) content().contentType(MediaType.APPLICATION_JSON));
+ }
+
+ @Test
+ void testCreateUser() throws Exception {
+ User user = new User();
+ user.setUserName("newUser");
+ user.setEmail("newuser@example.com");
+
+ mockMvc.perform((RequestBuilder) post("/api/users")
+ .contentType(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.valueOf(new ObjectMapper().writeValueAsString(user))))
+ .andExpect(status().isOk())
+ .andExpect((ResultMatcher) jsonPath("$.username").value("newuser"));
+ }
+}
diff --git a/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserServiceUnitTest.java b/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserServiceUnitTest.java
new file mode 100644
index 000000000..c2bf8cad2
--- /dev/null
+++ b/backend/pronunciationAppBack/src/test/java/dev/pronunciationAppBack/UserServiceUnitTest.java
@@ -0,0 +1,37 @@
+package dev.pronunciationAppBack;
+
+import dev.pronunciationAppBack.model.User;
+import dev.pronunciationAppBack.repository.UserRepository;
+import dev.pronunciationAppBack.service.UserService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class UserServiceUnitTest {
+ @Mock
+ private UserRepository userRepository;
+
+ @InjectMocks
+ private UserService userService;
+
+ @Test
+ void testCreateUser() {
+ User user = new User();
+ user.setUserName("testuser");
+
+ when(userRepository.save(any(User.class))).thenReturn(user);
+
+ User createdUser = userService.createUser(user);
+
+ assertNotNull(createdUser);
+ assertEquals("testuser", createdUser.getUserName());
+ }
+}
\ No newline at end of file
diff --git a/backend/resources/CreateSpringBootproject.md b/backend/resources/create project/CreateSpringBootproject.md
similarity index 100%
rename from backend/resources/CreateSpringBootproject.md
rename to backend/resources/create project/CreateSpringBootproject.md
diff --git a/backend/resources/create project/create-spring-boot-b.png b/backend/resources/create project/create-spring-boot-b.png
new file mode 100644
index 000000000..7230e9fdd
Binary files /dev/null and b/backend/resources/create project/create-spring-boot-b.png differ
diff --git a/backend/resources/create project/create-spring-boot.png b/backend/resources/create project/create-spring-boot.png
new file mode 100644
index 000000000..415b8d1dd
Binary files /dev/null and b/backend/resources/create project/create-spring-boot.png differ
diff --git a/backend/resources/pronunciationAppBack-v0.0-project-structure.png b/backend/resources/create project/pronunciationAppBack-v0.0-project-structure.png
similarity index 100%
rename from backend/resources/pronunciationAppBack-v0.0-project-structure.png
rename to backend/resources/create project/pronunciationAppBack-v0.0-project-structure.png
diff --git a/backend/resources/start-spring-io-create-project.png b/backend/resources/create project/pronunciationAppBack-v0.0-spring-io-create-project.png
similarity index 100%
rename from backend/resources/start-spring-io-create-project.png
rename to backend/resources/create project/pronunciationAppBack-v0.0-spring-io-create-project.png
diff --git a/backend/resources/pronunciationAppBack-v0.0-api-rest-words.png b/backend/resources/pronunciationAppBack-v0.0-api-rest-words.png
new file mode 100644
index 000000000..43e002693
Binary files /dev/null and b/backend/resources/pronunciationAppBack-v0.0-api-rest-words.png differ
diff --git a/backend/resources/pronunciationAppBack-v0.0-db-2.png b/backend/resources/pronunciationAppBack-v0.0-db-2.png
new file mode 100644
index 000000000..c6672f82a
Binary files /dev/null and b/backend/resources/pronunciationAppBack-v0.0-db-2.png differ
diff --git a/backend/resources/pronunciationAppBack-v0.0-db.png b/backend/resources/pronunciationAppBack-v0.0-db.png
new file mode 100644
index 000000000..2a496ab0a
Binary files /dev/null and b/backend/resources/pronunciationAppBack-v0.0-db.png differ