diff --git a/PRA/PRA_guide.md b/PRA/PRA_guide.md deleted file mode 100644 index c532cbfe5..000000000 --- a/PRA/PRA_guide.md +++ /dev/null @@ -1,147 +0,0 @@ -# Laboratory Practice Guide: Software Development Project - -## Objective - -Design and implement a full-stack application using Spring Boot for the backend and React for the frontend. - -## Project Structure - -### Backend (Spring Boot) - -1. **Project Initialization** - - - Use Spring Initializr to create a new Spring Boot project - - Include dependencies: Web, JPA, and your chosen database, H2 is ok. - -2. **Folder Structure DDD** - - ``` - com.example.project - ├── controller - ├── model - ├── repository - ├── service - └── config - ``` - -3. **Entity Design** - - - Create domain model classes in the `model` package - - Use appropriate JPA annotations - -4. **Repository Layer** - - - Develop JPA repositories in the `repository` package - - Extend `JpaRepository` for basic CRUD operations - -5. **Service Layer** - - - Implement business logic in the `service` package - - Use `@Service` annotation for service classes - -6. **Controller Layer** - - - Create REST controllers in the `controller` package - - Use `@RestController` and appropriate HTTP method annotations - -7. **Configuration** - - - Set up database configuration in `application.properties` or `application.yml` - - Create additional configurations in the `config` package if needed - -### Frontend (React) - -1. **Project Setup** - - - Use Create React App: `npx create-react-app frontend` or vite - - [Vite guide]([Getting Started | Vite](https://vite.dev/guide/)) - - ```bash - npm create vite@latest - ``` - - - -2. **Folder Structure** - - ``` - src - ├── components - ├── pages - ├── services - └── utils - ``` - -3. **Component Development** - - - Create reusable components in the `components` folder - - Develop page components in the `pages` folder - -4. **API Integration** - - - Use Axios for API calls - - Create an API service in the `services` folder - -5. **State Management** - - - Utilize React hooks for local state management - - Consider Redux or Context API for global state if needed - -6. **Routing** - - - Implement routing using React Router - -## Development Process - -1. **Backend Development** - - Implement entities, repositories, services, and controllers - - Test API endpoints using Postman or Swagger -2. **Frontend Development** - - Create React components and implement UI - - Integrate with backend API using Axios -3. **Testing** - - Write unit tests for both backend and frontend - - Implement integration tests for API endpoints -4. **Documentation** - - Document API endpoints: Postman or Swagger. - - Create README files for both backend and frontend - -## Best Practices - -- Follow SOLID principles -- Use meaningful naming conventions -- Implement proper error handling -- Write clean, readable, and well-commented code -- Utilize dependency injection in Spring Boot -- Follow React best practices and hooks guidelines - -### Submission Guidelines - -- Fork the existing [pronunciationApp](https://github.com/AlbertProfe/pronunciationApp) repository and clone it to your local environment. -- Create a new branch named `PRA01-YourName` from the latest commit. -- Commit your changes with clear, descriptive messages. -- Push your branch to your forked repository. -- Create a pull request to the AlbertProfe repository with a summary of your changes, titled: - - `PRA01-YourName-NameLaboratory` -- Initial pull request must be submitted before the two-week deadline -- Students who does not complete the lab before the deadline must create a second pull request with additional enhancements or features - -### Evaluation Criteria - -**Backend (Spring Boot)** - -- Correct implementation of JPA entities and repositories. -- Proper use of Spring Boot annotations and best practices. -- Functionality of service layer and controllers. -- Quality and coverage of Swagger API tests. -- Code clarity and documentation. - -**Frontend (React)** - -- Proper component structure and reusability -- Effective use of React hooks (useState, useEffect, etc.) -- Correct implementation of state management -- Proper handling of API calls and asynchronous operations -- Responsive and user-friendly UI design - -> Remember to test your application thoroughly and ensure it meets all specified requirements. diff --git a/PRA02 b/PRA02 new file mode 100644 index 000000000..e69de29bb 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..781cb81fb --- /dev/null +++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/controller/UserController.java @@ -0,0 +1,7 @@ +package dev.pronunciationAppBack.controller; + +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class UserController { +} 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..ab400f562 --- /dev/null +++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/model/User.java @@ -0,0 +1,64 @@ +package dev.pronunciationAppBack.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class User { + + @Id + private String id; + private String userName; + private int userAge; + private String userEmail; + private String password; + + public User(String id, String userName, int userAge, String userEmail, String password){ + + this.id = id; + this.userName = userName; + this.userAge = userAge; + this.userEmail = userEmail; + this.password = password; + } + + public String getUserEmail() { + return userEmail; + } + + public int getUserAge() { + return userAge; + } + + public String getId() { + return id; + } + + public String getUserName() { + return userName; + } + + public String getPassword() { + return password; + } + + public void setId(String id) { + this.id = id; + } + + public void setUserAge(int userAge) { + this.userAge = userAge; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} 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..7cbae483c --- /dev/null +++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/repository/UserRepository.java @@ -0,0 +1,7 @@ +package dev.pronunciationAppBack.repository; + +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository { +} 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..f9a2e10bb --- /dev/null +++ b/backend/pronunciationAppBack/src/main/java/dev/pronunciationAppBack/service/UserService.java @@ -0,0 +1,7 @@ +package dev.pronunciationAppBack.service; + +import org.springframework.stereotype.Service; + +@Service +public class UserService { +} diff --git a/backend/resources/Test-JUnit DB/test-JUnit-Word.md b/backend/resources/Test-JUnit DB/test-JUnit-Word.md deleted file mode 100644 index e9312dc38..000000000 --- a/backend/resources/Test-JUnit DB/test-JUnit-Word.md +++ /dev/null @@ -1,171 +0,0 @@ -# Test JUnit Word H2 DB - -## Test - -Here are 4 simple test cases for CRUD operations using `JUnit` and `Spring Boot` for the Word `entity`: - -```java -package dev.pronunciationAppBack; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; - -import static org.assertj.core.api.Assertions.assertThat; - -@DataJpaTest -public class WordRepositoryTest { - - @Autowired - private TestEntityManager entityManager; - - @Autowired - private WordRepository wordRepository; - - @Test - public void testCreateWord() { - Word word = new Word("1", "Example", "A thing characteristic of its kind", "ɪɡˈzæmpəl", "This is an example sentence.", true, 1); - Word savedWord = wordRepository.save(word); - assertThat(savedWord).isNotNull(); - assertThat(savedWord.getId()).isEqualTo("1"); - } - - @Test - public void testReadWord() { - Word word = new Word("2", "Test", "A procedure to evaluate", "test", "This is a test sentence.", true, 2); - entityManager.persist(word); - - Word foundWord = wordRepository.findById("2").orElse(null); - assertThat(foundWord).isNotNull(); - assertThat(foundWord.getWordName()).isEqualTo("Test"); - } - - @Test - public void testUpdateWord() { - Word word = new Word("3", "Update", "To bring up to date", "ˈʌpdeɪt", "This word will be updated.", true, 3); - entityManager.persist(word); - - Word wordToUpdate = wordRepository.findById("3").orElse(null); - assertThat(wordToUpdate).isNotNull(); - wordToUpdate.setDefinition("To make something more modern or up to date"); - wordRepository.save(wordToUpdate); - - Word updatedWord = wordRepository.findById("3").orElse(null); - assertThat(updatedWord).isNotNull(); - assertThat(updatedWord.getDefinition()).isEqualTo("To make something more modern or up to date"); - } - - @Test - public void testDeleteWord() { - Word word = new Word("4", "Delete", "To remove or erase", "dɪˈliːt", "This word will be deleted.", true, 4); - entityManager.persist(word); - - wordRepository.deleteById("4"); - - Word deletedWord = wordRepository.findById("4").orElse(null); - assertThat(deletedWord).isNull(); - } -} -``` - -These test cases cover the basic CRUD (Create, Read, Update, Delete) operations for the Word entity. Here's a brief explanation of each test: - -1. `testCreateWord`: Tests the creation of a new Word entity. -2. `testReadWord`: Tests reading a Word entity from the database. -3. `testUpdateWord`: Tests updating an existing Word entity. -4. `testDeleteWord`: Tests deleting a Word entity from the database. - -## Repository - -Note that these tests assume you have a `WordRepository` interface that extends `JpaRepository`. You'll need to create this interface in your project: - -```java -package dev.pronunciationAppBack; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface WordRepository extends JpaRepository { -} -``` - -Also, make sure you have the necessary dependencies in your `pom.xml` or `build.gradle` file for Spring Boot Test and JPA Test. - -## H2 and application.properties - -> For these JUnit tests to run properly with an in-memory database, we need to add the `H2 database` dependency and configure the `application.properties` file. - -Here's what you need to add: - -1. H2 Database Dependency: - Add this to your `pom.xml` if you're using Maven: - -```xml - - com.h2database - h2 - test - -``` - -Or if you're using Gradle, add this to your `build.gradle`: - -```gradle -dependencies { - testImplementation 'com.h2database:h2' -} -``` - -2. Application Properties: - Create a file named `application.properties` in your `src/test/resources` directory with the following content: - -```properties -spring.datasource.url=jdbc:h2:mem:testdb -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=sa -spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect - -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.show-sql=true -``` - -These settings will: - -- Configure an **in-memory H2 database for testing** -- Set up the database to create tables based on your entities and drop them after the tests -- Show SQL statements in the console, which can be helpful for debugging - -## Local DB - -First you must create the DB: - -```properties -# DDL OPTIONS: create-drop, create, update, none, validate -spring.jpa.hibernate.ddl-auto=create -``` - -Once created, change DDL to none - -```properties -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= - - -# H2 LOCAL DB SERVER -spring.datasource.url=jdbc:h2:/home/albert/MyProjects/DataBase/pronunciationDB/pronunciationDB.db -spring.datasource.username=albert -spring.datasource.password=1234 - -# DDL OPTIONS: create-drop, create, update, none, validate -spring.jpa.hibernate.ddl-auto=none -``` diff --git a/backend/resources/annotations/Param-PathVariable.md b/backend/resources/annotations/Param-PathVariable.md deleted file mode 100644 index 386848a2f..000000000 --- a/backend/resources/annotations/Param-PathVariable.md +++ /dev/null @@ -1,66 +0,0 @@ -# Param vs Path Variable - -## Using @DeleteMapping in Spring Boot with Postman - -Spring Boot's `@DeleteMapping` annotation simplifies the process of handling HTTP DELETE requests. - -> The `@DeleteMapping` annotation in Spring Boot provides a clean and efficient way to handle DELETE requests. Whether you choose to use path variables or query parameters depends on your API design preferences and requirements. Postman is an excellent tool for testing these endpoints, allowing you to easily send DELETE requests and verify the results. - -## DeleteWord Example - -Here's a simple example of a delete operation in a Spring Boot controller: - -```java -@RestController -@RequestMapping("/words") -public class WordController { - - @Autowired - private WordRepository wordRepository; - - @DeleteMapping("/{id}") - public String deleteWord(@PathVariable("id") String idToDelete) { - wordRepository.deleteById(idToDelete); - return "Word deleted"; - } -} -``` - -In this example, the `deleteWord` method is mapped to handle DELETE requests to the `/words/{id}` endpoint[1][4]. The `@PathVariable` annotation binds the `id` from the URL to the `idToDelete` parameter[3]. - -## Testing with Postman - -To test this endpoint using Postman, follow these steps: - -1. Open Postman and create a new request. -2. Set the HTTP method to DELETE. -3. Enter the URL: `http://localhost:8080/words/{id}` (replace `{id}` with the actual ID you want to delete). -4. Click the "Send" button to execute the request. - -### Using Path Variable - -For our `deleteWord` example, we're using a path variable. The ID is part of the URL path: - -``` -DELETE http://localhost:8080/words/123 -``` - -Here, `123` is the ID of the word to be deleted[5]. - -### Using Query Parameter (Alternative Approach) - -While our example uses a path variable, you could also design your endpoint to use a query parameter: - -```java -@DeleteMapping -public String deleteWord(@RequestParam("id") String idToDelete) { - wordRepository.deleteById(idToDelete); - return "Word deleted"; -} -``` - -To test this with Postman: - -1. Set the URL to `http://localhost:8080/words` -2. Add a query parameter: Key: `id`, Value: `123` -3. The full URL will look like: `http://localhost:8080/words?id=123` diff --git a/backend/resources/containers/Containers-Spring.md b/backend/resources/containers/Containers-Spring.md deleted file mode 100644 index 4da920796..000000000 --- a/backend/resources/containers/Containers-Spring.md +++ /dev/null @@ -1,150 +0,0 @@ -# Popular Containers in Spring Boot - -> Containers in Java are typically defined by their ability to hold and manage collections of objects or references. - -However, for example, List`and`Optional` serve fundamentally different purposes: - -- `List` is a collection container designed to store multiple elements with order and allow dynamic manipulation. -- `Optional` is a wrapper to explicitly handle the presence or absence of a single value, preventing null reference issues. - -While both can "contain" elements, they solve distinct programming challenges: data storage versus null-safety management. - -#### Wrapper and Collection - -> A **wrapper** is a layer of code that "wraps around" something simpler, adding extra functionality or protection. Like a protective cover that makes something easier to use or more powerful. -> -> In Java, a wrapper takes a basic object or value and provides additional methods or behaviors to interact with it more conveniently. - - - -> A **Collection** is a more structured way of storing and managing those items, with methods to add, remove, and manipulate the group of elements. -> -> An **Iterable** is something you can loop through, like a collection of items. -> -> - -**Container Comparison: List vs Optional** - -| Characteristic | List | Optional | -| -------------------- | ------------------------------- | ------------------------------------------- | -| **Purpose** | Store multiple elements | Represent optional value | -| **Nullability** | Can contain null elements | Explicitly prevents null | -| **Size** | Dynamic, variable length | Always contains 0 or 1 element | -| **Mutability** | Mutable (add/remove elements) | Immutable | -| **Creation** | `new ArrayList<>()` | `Optional.of()`, `Optional.empty()` | -| **Common Methods** | `.add()`, `.remove()`, `.get()` | `.isPresent()`, `.orElse()`, `.ifPresent()` | -| **Java 8+ Feature** | Pre-Java 8 | Introduced in Java 8 | -| **Typical Use Case** | Collection storage | Avoiding null checks | -| **Stream Support** | `.stream()` directly | Treated as stream with `.stream()` | -| **Performance** | Higher memory overhead | Lightweight wrapper | - -### Response Containers - -1. **ResponseEntity** - - - Full control over HTTP response - - Set status codes, headers, body - - Example: - - ```java - return ResponseEntity.ok(word); - return ResponseEntity.notFound().build(); - return ResponseEntity.status(HttpStatus.CREATED).body(word); - ``` - -2. **Optional** - - - Prevent null pointer exceptions - - Avoid explicit null checks - - Example: - - ```java - Optional word = repository.findById(id); - return word.orElseThrow(() -> new ResourceNotFoundException()); - ``` - -3. **Page** - - - Pagination support - - Metadata about result set - - Example: - - ```java - Page words = repository.findAll(PageRequest.of(0, 10)); - ``` - -4. **Mono** and **Flux** (Reactive Programming) - - - Asynchronous data streams - - Non-blocking operations - - Example: - - ```java - Mono wordMono = wordRepository.findById(id); - Flux wordFlux = wordRepository.findAll(); - ``` - -5. **Resource** (HATEOAS) - - - Include hyperlinks in responses - - Support for hypermedia-driven APIs - - Example: - - ```java - Resource resource = new Resource<>(word); - resource.add(linkTo(methodOn(WordController.class).getWord(id)).withSelfRel()); - ``` - -### Key Benefits - -- Type safety -- Explicit error handling -- Flexible response management -- Support for modern architectural patterns - -## Non-Response - -### Data Containers - -1. **List** - - - Basic collection of elements - - Dynamic sizing - - Example: `List words = new ArrayList<>();` - -2. **Set** - - - Unique elements - - No duplicates - - Example: `Set uniqueWordNames = new HashSet<>();` - -3. **Map** - - - Key-value pairs - - Fast lookups - - Example: `Map wordMap = new HashMap<>();` - -4. **Stream** - - - Functional-style operations - - Lazy evaluation - - Example: `words.stream().filter(w -> w.getLevel() > 2)` - -5. **CompletableFuture** - - - Asynchronous computation - - Chaining operations - - Example: `CompletableFuture wordFuture = CompletableFuture.supplyAsync(() -> createWord());` - -6. **Queue** - - - First-In-First-Out (FIFO) - - Task scheduling - - Example: `Queue wordQueue = new LinkedList<>();` - -### Key Characteristics - -- Thread-safety -- Performance optimization -- Flexible data manipulation -- Support for functional programming diff --git a/backend/resources/create project/CreateSpringBootproject.md b/backend/resources/create project/CreateSpringBootproject.md deleted file mode 100644 index 0636af37d..000000000 --- a/backend/resources/create project/CreateSpringBootproject.md +++ /dev/null @@ -1,39 +0,0 @@ -## Create and Download Spring Boot Project - -### Steps to Download - -1. Open URL: [create project](https://start.spring.io/#!type=maven-project&language=java&platformVersion=3.3.7&packaging=jar&jvmVersion=17&groupId=dev&artifactId=pronunciationAppBack&name=pronunciationAppBack&description=Spring%20Boot%20for%20app%20pronunciatoin&packageName=dev.pronunciationAppBack&dependencies=web,devtools,lombok,h2,data-jpa,postgresql) -2. Click "GENERATE" button to download ZIP - -### Project Import in IntelliJ IDEA - -```bash -# Extract downloaded ZIP -unzip pronunciationAppBack.zip - -# Open IntelliJ IDEA -File > Open > Select extracted project folder -``` - -### Recommended Import Process - -1. Launch IntelliJ IDEA -2. Select "Open" from welcome screen -3. Browse to extracted project directory -4. Click "Open" -5. Wait for Maven/Gradle to sync dependencies - -### Potential Dependencies Included - -- Spring Web -- Spring DevTools -- Lombok -- H2 Database -- Spring Data JPA -- PostgreSQL Driver - -### Troubleshooting - -- Ensure Java 17 is installed -- Check Maven/Gradle configuration -- Verify internet connection for dependency download diff --git a/backend/resources/create project/create-spring-boot-b.png b/backend/resources/create project/create-spring-boot-b.png deleted file mode 100644 index 7230e9fdd..000000000 Binary files a/backend/resources/create project/create-spring-boot-b.png and /dev/null differ diff --git a/backend/resources/create project/create-spring-boot.png b/backend/resources/create project/create-spring-boot.png deleted file mode 100644 index 415b8d1dd..000000000 Binary files a/backend/resources/create project/create-spring-boot.png and /dev/null differ diff --git a/backend/resources/create project/pronunciationAppBack-v0.0-project-structure.png b/backend/resources/create project/pronunciationAppBack-v0.0-project-structure.png deleted file mode 100644 index be08fd7a4..000000000 Binary files a/backend/resources/create project/pronunciationAppBack-v0.0-project-structure.png and /dev/null differ diff --git a/backend/resources/create project/pronunciationAppBack-v0.0-spring-io-create-project.png b/backend/resources/create project/pronunciationAppBack-v0.0-spring-io-create-project.png deleted file mode 100644 index 26cd92942..000000000 Binary files a/backend/resources/create project/pronunciationAppBack-v0.0-spring-io-create-project.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-api-rest-words.png b/backend/resources/images/pronunciationAppBack-v0.0-api-rest-words.png deleted file mode 100644 index 43e002693..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-api-rest-words.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller-2.png b/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller-2.png deleted file mode 100644 index e97453151..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller-2.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller.png b/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller.png deleted file mode 100644 index 2d4acde25..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-basic-CRUD-controller.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-db-2.png b/backend/resources/images/pronunciationAppBack-v0.0-db-2.png deleted file mode 100644 index c6672f82a..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-db-2.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-db.png b/backend/resources/images/pronunciationAppBack-v0.0-db.png deleted file mode 100644 index 2a496ab0a..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-db.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-project-structure-2.png b/backend/resources/images/pronunciationAppBack-v0.0-project-structure-2.png deleted file mode 100644 index 22a050a1f..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-project-structure-2.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.0-project-structure.png b/backend/resources/images/pronunciationAppBack-v0.0-project-structure.png deleted file mode 100644 index 3dbc4ee10..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.0-project-structure.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.2-checkhealth.png b/backend/resources/images/pronunciationAppBack-v0.2-checkhealth.png deleted file mode 100644 index c62a94f9e..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.2-checkhealth.png and /dev/null differ diff --git a/backend/resources/images/pronunciationAppBack-v0.2-project-structure.png b/backend/resources/images/pronunciationAppBack-v0.2-project-structure.png deleted file mode 100644 index d376fc077..000000000 Binary files a/backend/resources/images/pronunciationAppBack-v0.2-project-structure.png and /dev/null differ diff --git a/backend/resources/jpa/jpa-hibernate-jdbc.png b/backend/resources/jpa/jpa-hibernate-jdbc.png deleted file mode 100644 index cf6d0e302..000000000 Binary files a/backend/resources/jpa/jpa-hibernate-jdbc.png and /dev/null differ diff --git a/backend/resources/jpa/jpa.md b/backend/resources/jpa/jpa.md deleted file mode 100644 index 1b828ad61..000000000 --- a/backend/resources/jpa/jpa.md +++ /dev/null @@ -1,119 +0,0 @@ -# JPA - -- [Spring Boot: Data & DB – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-data.html) - -- [Spring Boot: JPA & DI – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-jpa.html) - -- [Spring Boot: JPA Mappings – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-jpa-2.html) - -- [Spring Boot: JPA Relationships – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-jpa-3.html) - -- [Spring Boot: JPA Queries – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-jpa-4.html) - -- [Spring Boot: JPA Inherence – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-jpa-5.html) - -- [Spring Boot: Scaling – albertprofe wiki](https://albertprofe.dev/springboot/boot-concepts-scaling.html) - -## Summary - -JPA stands for `Java Persistence API`. - -It is a Java specification for **managing, persisting, and accessing relational data** in Java applications. - -JPA is a **standard API for ORM (Object-Relational Mapping)** and provides a way to map Java objects to relational databases - -## Spring Boot DAL/DAO - -**ORM (Object-Relational Mapping)** - -> ORM is the concept of mapping object-oriented domain models to relational database tables. JPA and Hibernate are examples of ORM frameworks. - -```bash -App -└── Spring Data JPA - └── JPA (Java Persistence API) - └── Hibernate - └── JDBC (Java Database Connectivity) - └── Relational Database -``` - -**Application Layer** - -At the top, we have the application code that needs to interact with data. - -**Repository (Spring Data JPA)** - -Spring Data JPA provides a high-level abstraction for data access. It simplifies database operations by allowing developers to define interfaces that extend JpaRepository. For example: - -```java -public interface UserRepository extends JpaRepository { - List findByLastName(String lastName); -} -``` - -This interface automatically generates methods for common database operations. - -**JPA (Java Persistence API)** - -JPA is a specification that defines how to persist data in Java applications. - -**It's not an implementation**, but a set of interfaces and annotations that describe how to map Java objects to database tables. For example: - -```java -@Entity -public class User { - @Id - private Long id; - private String firstName; - private String lastName; -} -``` - -**Hibernate** - -Hibernate is a popular implementation of JPA. - - It provides the actual code that performs the object-relational mapping (ORM) based on JPA specifications. Hibernate translates JPA annotations and method calls into SQL queries. - -**JDBC (Java Database Connectivity)** - -JDBC is a low-level API for connecting Java applications to databases. - -It provides a set of Java classes and interfaces that send SQL statements to the database and process the results. Hibernate uses JDBC under the hood to communicate with the database. - -**Database** - -At the bottom is the actual database system, such as MySQL, PostgreSQL, or Oracle. - -## DAL/DAO for a 5yo - -Imagine you're building a house: - -- The **Application** is like the entire house. -- The **Repository** is like a smart assistant that helps you organize and find things in the house. -- **JPA** is like a blueprint that describes how rooms should be organized. -- **Hibernate** is the construction team that builds the rooms according to the blueprint. -- **JDBC** is like the basic tools (hammers, nails) the construction team uses. -- The **Database** is the foundation and structure of the house. -- **ORM** is the process of arranging the rooms to match how you want to use them. - -## Vendors and Context - -These technologies work together to simplify database operations in Java applications, from low-level database connections (JDBC) to high-level abstractions (Spring Data JPA) - -- JPA: Specification by Oracle (formerly Sun Microsystems) -- Hibernate: Open-source project maintained by Red Hat -- JDBC: Part of the Java Standard Edition (SE) platform -- Spring Data JPA: Part of the Spring Framework ecosystem -- Database vendors: MySQL (Oracle), PostgreSQL (open-source), Oracle Database, Microsoft SQL Server - -## CrudRepository vs. JpaRepository - -[In Spring Boot what is the difference between CrudRepository and JpaRepository in extending a Java repository interface - Stack Overflow](https://stackoverflow.com/questions/72058502/in-spring-boot-what-is-the-difference-between-crudrepository-and-jparepository-i) - -| CrudRepository | JpaRepository | -| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| CrudRepository does not provide any method for pagination and sorting. | JpaRepository extends PagingAndSortingRepository. It provides all the methods for implementing the pagination. | -| It works as a **marker** interface. | JpaRepository extends both **CrudRepository** and **PagingAndSortingRepository**. | -| It provides CRUD function only. For example **findById(), findAll(),** etc. | It provides some extra methods along with the method of PagingAndSortingRepository and CrudRepository. For example, **flush(), deleteInBatch().** | -| It is used when we do not need the functions provided by JpaRepository and PagingAndSortingRepository. | It is used when we want to implement pagination and sorting functionality in an application. | diff --git a/backend/resources/jpa/jparepository.png b/backend/resources/jpa/jparepository.png deleted file mode 100644 index 82c5d07b6..000000000 Binary files a/backend/resources/jpa/jparepository.png and /dev/null differ diff --git a/backend/resources/jpa/model/pronunciationApp-v0.2-model-1.png b/backend/resources/jpa/model/pronunciationApp-v0.2-model-1.png deleted file mode 100644 index 73ea6f2e9..000000000 Binary files a/backend/resources/jpa/model/pronunciationApp-v0.2-model-1.png and /dev/null differ diff --git a/backend/resources/jpa/model/pronunciationApp-v0.2-model.md b/backend/resources/jpa/model/pronunciationApp-v0.2-model.md deleted file mode 100644 index dd79f2f18..000000000 --- a/backend/resources/jpa/model/pronunciationApp-v0.2-model.md +++ /dev/null @@ -1,74 +0,0 @@ -# pronunciationApp-v0.2-model - -```mermaid -classDiagram - class User { - +String id - +String usrname - +int age - +String email - +int totalScore - +boolean isActive - } - class Word { - +String id - +String text - +String description - +String sentence - +int difficulty - +boolean isCommon - } - class Pronunciation { - +String id - +String audioName - +int audioSize - +String audioUrl - +String phoneticSpelling - +String speakerGender - +enum type // canonical, recorded - } - class Level { - +String id - +int number - +String name - +int requiredScore - +boolean isBlocked - } - class Category { - +String id - +String categoryName - +String subCategoryName - +String description - +int wordCount - } - class GameProgress { - +String id - +int currentScore - +enum currentStage // stage_01, stage_02 - +Date lastPlayedDate - +int wordsLearned - } - class Stage { - +String id - +String name - +String avatarUrl - +String status - +int progress - +int currentScore - } - class StageWords { - +String id - +enum status // done, pending, fail - +Date lastUpdatedDateTime - } - - - User "1" -- "*" GameProgress : tracks progress - Word "1" -- "1" Pronunciation : has pronunciation - Word "*" -- "*" Category : belongs to group - Word "*" -- "1" Level : has level - GameProgress "1" -- "1" Stage : is at stage - Stage "*" -- "1" Level : has level - Stage "1" -- "*" StageWords : has tracked words - StageWords "1" -- "1" Word : has a word -``` diff --git a/backend/resources/mock-data/bash-script/cli-execute-bash.png b/backend/resources/mock-data/bash-script/cli-execute-bash.png deleted file mode 100644 index 3af293426..000000000 Binary files a/backend/resources/mock-data/bash-script/cli-execute-bash.png and /dev/null differ diff --git a/backend/resources/mock-data/bash-script/data.json b/backend/resources/mock-data/bash-script/data.json deleted file mode 100644 index edfdfa618..000000000 --- a/backend/resources/mock-data/bash-script/data.json +++ /dev/null @@ -1,318 +0,0 @@ -[ - { - "id": "8f7d1b9e3a2c5f6e", - "wordName": "aberration", - "definition": "a departure from what is normal, usual, or expected", - "phoneticSpelling": "ˌæbəˈreɪʃən", - "sentence": "The sudden drop in temperature was an aberration for this time of year.", - "level": 3, - "active": true - }, - { - "id": "2c4a6b8d0e1f3g5h", - "wordName": "benevolent", - "definition": "kind, generous, and caring about others", - "phoneticSpelling": "bəˈnevələnt", - "sentence": "The benevolent donor provided funds for the new hospital wing.", - "level": 2, - "active": true - }, - { - "id": "7j9k1l3m5n2o4p6q", - "wordName": "cacophony", - "definition": "a harsh, discordant mixture of sounds", - "phoneticSpelling": "kəˈkɒfəni", - "sentence": "The cacophony of car horns filled the busy street.", - "level": 4, - "active": true - }, - { - "id": "1r3s5t7u9v2w4x6y", - "wordName": "diligent", - "definition": "having or showing care and conscientiousness in one's work or duties", - "phoneticSpelling": "ˈdɪlɪdʒənt", - "sentence": "The diligent student always completed her homework on time.", - "level": 2, - "active": true - }, - { - "id": "8z0a2b4c6d1e3f5g", - "wordName": "ephemeral", - "definition": "lasting for a very short time", - "phoneticSpelling": "ɪˈfemərəl", - "sentence": "The beauty of cherry blossoms is ephemeral, lasting only a few days.", - "level": 3, - "active": true - }, - { - "id": "7h9i1j3k5l2m4n6o", - "wordName": "facetious", - "definition": "treating serious issues with deliberately inappropriate humor", - "phoneticSpelling": "fəˈsiːʃəs", - "sentence": "His facetious remarks about the company's financial troubles were not well-received.", - "level": 4, - "active": true - }, - { - "id": "5p7q9r1s3t5u2v4w", - "wordName": "gregarious", - "definition": "fond of company; sociable", - "phoneticSpelling": "ɡrɪˈɡeəriəs", - "sentence": "The gregarious host made sure all the guests felt welcome at the party.", - "level": 3, - "active": true - }, - { - "id": "6x8y0z2a4b1c3d5e", - "wordName": "harbinger", - "definition": "a person or thing that announces or signals the approach of another", - "phoneticSpelling": "ˈhɑːbɪndʒə", - "sentence": "The robin is often seen as a harbinger of spring.", - "level": 4, - "active": true - }, - { - "id": "9f1g3h5i7j2k4l6m", - "wordName": "incessant", - "definition": "continuing without pause or interruption", - "phoneticSpelling": "ɪnˈsesənt", - "sentence": "The incessant barking of the neighbor's dog kept us awake all night.", - "level": 3, - "active": true - }, - { - "id": "2n4o6p8q0r1s3t5u", - "wordName": "juxtapose", - "definition": "to place or deal with close together for contrasting effect", - "phoneticSpelling": "ˈdʒʌkstəpəʊz", - "sentence": "The artist decided to juxtapose images of war and peace in her latest work.", - "level": 4, - "active": true - }, - { - "id": "7v9w1x3y5z2a4b6c", - "wordName": "kinetic", - "definition": "relating to or resulting from motion", - "phoneticSpelling": "kɪˈnetɪk", - "sentence": "The sculpture's kinetic elements moved gently in the breeze.", - "level": 3, - "active": true - }, - { - "id": "8d0e2f4g6h1i3j5k", - "wordName": "lethargic", - "definition": "sluggish and apathetic", - "phoneticSpelling": "lɪˈθɑːdʒɪk", - "sentence": "The hot weather made everyone feel lethargic and unmotivated.", - "level": 2, - "active": true - }, - { - "id": "1l3m5n7o9p2q4r6s", - "wordName": "mellifluous", - "definition": "sweet or musical; pleasant to hear", - "phoneticSpelling": "məˈlɪfluəs", - "sentence": "The singer's mellifluous voice captivated the audience.", - "level": 4, - "active": true - }, - { - "id": "5t7u9v1w3x5y2z4a", - "wordName": "nefarious", - "definition": "wicked or criminal", - "phoneticSpelling": "nɪˈfeəriəs", - "sentence": "The detective uncovered the CEO's nefarious scheme to embezzle company funds.", - "level": 3, - "active": true - }, - { - "id": "6b8c0d2e4f1g3h5i", - "wordName": "obfuscate", - "definition": "to render obscure, unclear, or unintelligible", - "phoneticSpelling": "ˈɒbfʌskeɪt", - "sentence": "The politician tried to obfuscate the issue by using complex jargon.", - "level": 4, - "active": true - }, - { - "id": "9j1k3l5m7n2o4p6q", - "wordName": "panacea", - "definition": "a solution or remedy for all difficulties or diseases", - "phoneticSpelling": "ˌpænəˈsiːə", - "sentence": "Exercise is not a panacea for all health problems, but it certainly helps.", - "level": 3, - "active": true - }, - { - "id": "2r4s6t8u0v1w3x5y", - "wordName": "quintessential", - "definition": "representing the most perfect or typical example of a quality or class", - "phoneticSpelling": "ˌkwɪntɪˈsenʃəl", - "sentence": "The small town diner was the quintessential American eating establishment.", - "level": 4, - "active": true - }, - { - "id": "7z9a1b3c5d2e4f6g", - "wordName": "resilient", - "definition": "able to withstand or recover quickly from difficult conditions", - "phoneticSpelling": "rɪˈzɪliənt", - "sentence": "The resilient community quickly rebuilt after the natural disaster.", - "level": 3, - "active": true - }, - { - "id": "8h0i2j4k6l1m3n5o", - "wordName": "surreptitious", - "definition": "kept secret, especially because it would not be approved of", - "phoneticSpelling": "ˌsʌrəpˈtɪʃəs", - "sentence": "He made a surreptitious attempt to leave the party without saying goodbye.", - "level": 4, - "active": true - }, - { - "id": "1p3q5r7s9t2u4v6w", - "wordName": "tenacious", - "definition": "tending to keep a firm hold of something; clinging or adhering closely", - "phoneticSpelling": "təˈneɪʃəs", - "sentence": "The tenacious climber refused to give up, even when faced with steep cliffs.", - "level": 3, - "active": true - }, - { - "id": "5x7y9z1a3b5c2d4e", - "wordName": "ubiquitous", - "definition": "present, appearing, or found everywhere", - "phoneticSpelling": "juːˈbɪkwɪtəs", - "sentence": "Smartphones have become ubiquitous in modern society.", - "level": 4, - "active": true - }, - { - "id": "6f8g0h2i4j1k3l5m", - "wordName": "vociferous", - "definition": "expressing or characterized by vehement opinions; loud and forceful", - "phoneticSpelling": "və(ʊ)ˈsɪfərəs", - "sentence": "The vociferous crowd demanded justice for the wrongly accused man.", - "level": 4, - "active": true - }, - { - "id": "9n1o3p5q7r2s4t6u", - "wordName": "whimsical", - "definition": "playfully quaint or fanciful, especially in an appealing and amusing way", - "phoneticSpelling": "ˈwɪmzɪkəl", - "sentence": "The artist's whimsical sculptures brought a smile to everyone's face.", - "level": 3, - "active": true - }, - { - "id": "2v4w6x8y0z1a3b5c", - "wordName": "xenophobia", - "definition": "dislike of or prejudice against people from other countries", - "phoneticSpelling": "ˌzenəˈfəʊbiə", - "sentence": "The politician's xenophobia was evident in his discriminatory policies.", - "level": 4, - "active": true - }, - { - "id": "7d9e1f3g5h2i4j6k", - "wordName": "yearn", - "definition": "to have an intense feeling of longing for something", - "phoneticSpelling": "jɜːn", - "sentence": "After months away, she yearned to return to her hometown.", - "level": 2, - "active": true - }, - { - "id": "8l0m2n4o6p1q3r5s", - "wordName": "zealous", - "definition": "having or showing zeal; enthusiastic and diligent", - "phoneticSpelling": "ˈzeləs", - "sentence": "The zealous volunteer dedicated all her free time to the charity.", - "level": 3, - "active": true - }, - { - "id": "1t3u5v7w9x2y4z6a", - "wordName": "ambivalent", - "definition": "having mixed feelings or contradictory ideas about something or someone", - "phoneticSpelling": "æmˈbɪvələnt", - "sentence": "She felt ambivalent about moving to a new city for her job.", - "level": 3, - "active": true - }, - { - "id": "5b7c9d1e3f5g2h4i", - "wordName": "brevity", - "definition": "concise and exact use of words in writing or speech", - "phoneticSpelling": "ˈbrevɪti", - "sentence": "The speaker's brevity was appreciated by the audience.", - "level": 3, - "active": true - }, - { - "id": "6j8k0l2m4n1o3p5q", - "wordName": "cognizant", - "definition": "having knowledge or awareness", - "phoneticSpelling": "ˈkɒɡnɪzənt", - "sentence": "The manager was cognizant of the team's concerns about the new project.", - "level": 4, - "active": true - }, - { - "id": "9r1s3t5u7v2w4x6y", - "wordName": "dexterous", - "definition": "showing or having skill, especially with the hands", - "phoneticSpelling": "ˈdekstərəs", - "sentence": "The dexterous magician amazed the crowd with his sleight of hand.", - "level": 3, - "active": true - }, - { - "id": "2z4a6b8c0d1e3f5g", - "wordName": "eloquent", - "definition": "fluent or persuasive in speaking or writing", - "phoneticSpelling": "ˈeləkwənt", - "sentence": "Her eloquent speech moved the entire audience to tears.", - "level": 3, - "active": true - }, - { - "id": "7h9i1j3k5l2m4n6o", - "wordName": "fortuitous", - "definition": "happening by chance rather than intention", - "phoneticSpelling": "fɔːˈtjuːɪtəs", - "sentence": "Their meeting at the airport was entirely fortuitous.", - "level": 4, - "active": true - }, - { - "id": "8p0q2r4s6t1u3v5w", - "wordName": "garrulous", - "definition": "excessively talkative, especially on trivial matters", - "phoneticSpelling": "ˈɡærʊləs", - "sentence": "The garrulous passenger talked throughout the entire flight.", - "level": 4, - "active": true - }, - { - "id": "1x3y5z7a9b2c4d6e", - "wordName": "haphazard", - "definition": "lacking any obvious principle of organization", - "phoneticSpelling": "hæpˈhæzəd", - "sentence": "The room was in a haphazard state, with clothes and books strewn everywhere.", - "level": 3, - "active": true - }, - { - "id": "5f7g9h1i3j5k2l4m", - "wordName": "iconoclast", - "definition": "a person who attacks or criticizes cherished beliefs or institutions", - "phoneticSpelling": "aɪˈkɒnəklæst", - "sentence": "The young artist was seen as an iconoclast in the conservative art world.", - "level": 4, - "active": true - } -] - diff --git a/backend/resources/mock-data/bash-script/h2-db-fill-with-mock-data.png b/backend/resources/mock-data/bash-script/h2-db-fill-with-mock-data.png deleted file mode 100644 index 9587257d3..000000000 Binary files a/backend/resources/mock-data/bash-script/h2-db-fill-with-mock-data.png and /dev/null differ diff --git a/backend/resources/mock-data/bash-script/import_words.sh b/backend/resources/mock-data/bash-script/import_words.sh deleted file mode 100755 index 568b90fdd..000000000 --- a/backend/resources/mock-data/bash-script/import_words.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Check if jq is installed -if ! command -v jq &> /dev/null -then - echo "jq is required but not installed. Please install jq and try again." - exit 1 -fi - -# Read the JSON file and process each word object -jq -c '.[]' data.json | while read -r word; do - # Send POST request for each word - curl -X POST \ - -H "Content-Type: application/json" \ - -d "$word" \ - http://localhost:8080/api/words/createWord - - echo -e "\nWord processed" - echo "----------" - - # Optional: Add a small delay between requests to avoid overwhelming the server - sleep 0.5 -done - -echo -e "\nAll words have been posted" - - diff --git a/backend/resources/mock-data/faker-java/java-faker.md b/backend/resources/mock-data/faker-java/java-faker.md deleted file mode 100644 index 0f7562bee..000000000 --- a/backend/resources/mock-data/faker-java/java-faker.md +++ /dev/null @@ -1,98 +0,0 @@ -# Java Faker - -> **Java Faker is a tool that creates realistic-looking fake data**, including names, addresses, phone numbers, and much more. - -- [GitHub - DiUS/java-faker: Brings the popular ruby faker gem to Java](https://github.com/DiUS/java-faker) - -It’s useful for: - -1. **Populating** databases with test data -2. Creating **mock objects for unit testing** -3. Generating sample data for applications -4. Prototyping user interfaces - -The library provides a wide range of pre-defined categories (like name, address, phone number) and methods to generate fake data within those categories. It’s easy to use and can generate data in multiple languages and locales. - -For example, you can create a Faker instance and generate fake data like this: - -```java -Faker faker = new Faker(); - // Generates a random full name -String name = faker.name().fullName(); -// Generates a random email address -String email = faker.internet().emailAddress(); -``` - -Dependency for maven: - -```xml - - com.github.javafaker - javafaker - 1.0.2 - -``` - -## Example #1 - -- [CustomerDataLoader.java at master · AlbertProfe/restaurantManager · GitHub](https://github.com/AlbertProfe/restaurantManager/blob/master/src/main/java/dev/example/restaurantManager/utilities/CustomerDataLoader.java) - -- [DataLoader.java at master · AlbertProfe/restaurantManager · GitHub](https://github.com/AlbertProfe/restaurantManager/blob/master/src/main/java/dev/example/restaurantManager/utilities/DataLoader.java) - -In this example, it's used to create fake customer data for a restaurant management system. Here's how it works: - -1. The `CustomerDataLoader` class is annotated with `@Component`, making it a Spring-managed bean[3]. - -2. It uses `@Autowired` to inject the `CustomerRepository`, which will be used to save the generated data[3]. - -3. The `createFakeCustomers()` method first checks if the database is empty: - -```java -if (customerRepository.count() == 0) { - // Generate fake data -} -``` - -4. A new `Faker` instance is created with the US locale: - -```java -Faker faker = new Faker(new Locale("en-US")); -``` - -5. The method then generates 50 fake customers in a loop: - -```java -for (int i = 0; i < qty; i++) { - Customer customer = new Customer( - UUID.randomUUID().toString(), - faker.name().fullName(), - faker.internet().emailAddress(), - faker.phoneNumber().cellPhone(), - faker.random().nextInt(18, 130), - faker.random().nextBoolean(), - faker.random().nextBoolean() - ); - customerRepository.save(customer); -} -``` - -6. For each customer, Java Faker generates: - - - A random full name using `faker.name().fullName()` - - A random email address using `faker.internet().emailAddress()` - - A random cell phone number using `faker.phoneNumber().cellPhone()` - - A random age between 18 and 130 using `faker.random().nextInt(18, 130)` - - Two random boolean values using `faker.random().nextBoolean()` - -7. Each generated customer is saved to the database using `customerRepository.save(customer)`[3]. - -This approach allows the application to populate the database with realistic-looking customer data for testing or development purposes, without the need for manual data entry. - -Citations: -[1] https://github.com/AlbertProfe/restaurantManager/tree/master -[2] https://albertprofe.dev/springboot/sblab8-1.html -[3] https://albertprofe.dev/springboot/sblab8-3.html - ---- - -Answer from Perplexity: pplx.ai/share diff --git a/backend/resources/mock-data/import-csv/import-csv.md b/backend/resources/mock-data/import-csv/import-csv.md deleted file mode 100644 index ff13c8d9d..000000000 --- a/backend/resources/mock-data/import-csv/import-csv.md +++ /dev/null @@ -1,32 +0,0 @@ -# Import CSV to H2 - -> To test the H2 database CSV import functionality with a basic CSV file for a UserApp object, follow these steps: - -1. Create a CSV file named `users.csv` with the following content: - -``` -id,username,email,created_at -1,john_doe,john@example.com,2025-01-24 20:00:00 -2,jane_smith,jane@example.com,2025-01-24 20:15:00 -3,bob_johnson,bob@example.com,2025-01-24 20:30:00 -``` - -2. Save this file in a location accessible to your H2 database, for example, `/path/to/users.csv`. - -3. Connect to your H2 database and execute the following SQL command: - -```sql -CREATE TABLE UserApp AS SELECT * FROM CSVREAD('/path/to/users.csv'); -``` - -This command will create a new table named `UserApp` with columns matching the CSV file structure. - -4. To verify the import, you can run a SELECT query: - -```sql -SELECT * FROM UserApp; -``` - -This should display the imported data from the CSV file. - -> **Note** that the H2 database will automatically infer the column types based on the data in the CSV file[2]. In this case, `id` will likely be treated as an INTEGER, while `username` and `email` will be VARCHAR, and `created_at` will be TIMESTAMP. diff --git a/backend/resources/mock-data/import-csv/userApp-from-csv.png b/backend/resources/mock-data/import-csv/userApp-from-csv.png deleted file mode 100644 index 163c5bd77..000000000 Binary files a/backend/resources/mock-data/import-csv/userApp-from-csv.png and /dev/null differ diff --git a/backend/resources/mock-data/import-csv/userApp-sql-import-csv.png b/backend/resources/mock-data/import-csv/userApp-sql-import-csv.png deleted file mode 100644 index 7b92d9239..000000000 Binary files a/backend/resources/mock-data/import-csv/userApp-sql-import-csv.png and /dev/null differ diff --git a/backend/resources/mock-data/import-csv/users.csv b/backend/resources/mock-data/import-csv/users.csv deleted file mode 100644 index d4ca3dc38..000000000 --- a/backend/resources/mock-data/import-csv/users.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,username,email,created_at -1,john_doe,john@example.com,2025-01-24 20:00:00 -2,jane_smith,jane@example.com,2025-01-24 20:15:00 -3,bob_johnson,bob@example.com,2025-01-24 20:30:00 diff --git a/backend/resources/mock-data/mockServer-postman/mock-serverPostman.md b/backend/resources/mock-data/mockServer-postman/mock-serverPostman.md deleted file mode 100644 index 62829a7a2..000000000 --- a/backend/resources/mock-data/mockServer-postman/mock-serverPostman.md +++ /dev/null @@ -1,49 +0,0 @@ -# How to create a mock server in Postman from a collection? - -> Mock servers simulate real API behavior and are useful for testing and development without relying on live APIs. - -## Reference - -- https://86c6ea23-f1a9-4a53-85a9-9c9869d64809.mock.pstmn.io/api/words/ - -- [Configure and use a Postman mock server | Postman Docs](https://learning.postman.com/docs/designing-and-developing-your-api/mocking-data/setting-up-mock/) - -## Step-by-step - - - -To create a mock server in Postman from a collection, follow these steps: - -1. **Select the Collection**: - - - In the Postman sidebar, go to **Collections**. - - Find the collection you want to mock, click **View more actions** (three dots), and select **Mock Collection**. - -2. **Configure Mock Server Details**: - - - Provide a name for your mock server. - - (Optional) Choose an environment to use environment variables with your mock server. - - Decide whether to make the mock server private or public. Private servers require an API key in the request header. - - Optionally, simulate a fixed network delay by specifying a response delay. - -3. **Create the Mock Server**: - - - Click **Create Mock Server** to finalize the setup. - - Postman will display the mock server URL, which can be used in requests. - -4. **Add Examples to Requests**: - - - Ensure that each request in your collection has at least one saved example. Postman uses these examples to generate responses for mock requests. - -5. **Use the Mock Server**: - - - Copy the mock server URL and use it in your API requests. - - If the server is private, include your Postman API key as an `x-api-key` header. - -6. **Edit or Delete Mock Servers**: - - - To edit, go to **Mock Servers** in the sidebar, select the desired server, and click **Edit Configuration**. - - To delete, click **View more actions** next to the server name and select **Delete**. - -Citations: -[1] https://learning.postman.com/docs/designing-and-developing-your-api/mocking-data/setting-up-mock/ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-1.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-1.png deleted file mode 100644 index 23c7bb50c..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-1.png and /dev/null differ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-2.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-2.png deleted file mode 100644 index e925ef98e..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-2.png and /dev/null differ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-3.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-3.png deleted file mode 100644 index 80a3d7bca..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-3.png and /dev/null differ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-4.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-4.png deleted file mode 100644 index afa299449..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-4.png and /dev/null differ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-5.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-5.png deleted file mode 100644 index 09d0cbe06..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-5.png and /dev/null differ diff --git a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-6.png b/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-6.png deleted file mode 100644 index 00cef8081..000000000 Binary files a/backend/resources/mock-data/mockServer-postman/mockServer_Postman_from_collection-6.png and /dev/null differ diff --git a/backend/resources/pronunciationApp-v0.1.md b/backend/resources/pronunciationApp-v0.1.md deleted file mode 100644 index 62dbb8fcd..000000000 --- a/backend/resources/pronunciationApp-v0.1.md +++ /dev/null @@ -1,390 +0,0 @@ -# PronunciationApp Backend v0.1 - -## Project - -- pronunciationApp Backend **GitHub** [code](https://github.com/AlbertProfe/pronunciationApp/tree/backend-spring-boot/backend/pronunciationAppBack) - -- pronunciatoinApp Backend [resources](https://github.com/AlbertProfe/pronunciationApp/tree/backend-spring-boot/backend/resources) **documentation** - -### Project Structure - -A Spring Boot backend application for the `Pronunciation App`, using: - -- H2 Database (local file-based or memory for certain purposes) -- `Spring Data JPA` -- REST Controller -- Service Layer -- Entity Mapping: `@Entity` - -### Dependencies - -Add these to your `pom.xml`: - -```xml - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-web - - - com.h2database - h2 - runtime - - -``` - -### Entity - -`Word.java`: - -```java -package com.pronunciationapp.model; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -@Entity -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; - - // Constructors - public Word() {} - - public Word(String id, String wordName, String definition, - String phoneticSpelling, String sentence, - boolean isActive, int level) { - this.id = id; - this.wordName = wordName; - this.definition = definition; - this.phoneticSpelling = phoneticSpelling; - this.sentence = sentence; - this.isActive = isActive; - this.level = level; - } - - // Getters and Setters - // ... (generate these for all fields) -} -``` - -### Repository - -`WordRepository.java`: - -```java -package com.pronunciationapp.repository; - -import com.pronunciationapp.model.Word; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public interface WordRepository extends JpaRepository { - List findByIsActiveTrue(); - List findByLevel(int level); - Word findByWordName(String wordName); -} -``` - -### Service - -`WordService.java`: - -```java -package com.pronunciationapp.service; - -import com.pronunciationapp.model.Word; -import com.pronunciationapp.repository.WordRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Service -public class WordService { - - @Autowired - private WordRepository wordRepository; - - public List getAllWords() { - return wordRepository.findAll(); - } - - public Optional getWordById(String id) { - return wordRepository.findById(id); - } - - public Word createWord(Word word) { - if (word.getId() == null) { - word.setId(UUID.randomUUID().toString()); - } - return wordRepository.save(word); - } - - public Word updateWord(String id, Word wordDetails) { - Word word = wordRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Word not found")); - - word.setWordName(wordDetails.getWordName()); - word.setDefinition(wordDetails.getDefinition()); - word.setPhoneticSpelling(wordDetails.getPhoneticSpelling()); - word.setSentence(wordDetails.getSentence()); - word.setActive(wordDetails.isActive()); - word.setLevel(wordDetails.getLevel()); - - return wordRepository.save(word); - } - - public void deleteWord(String id) { - Word word = wordRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Word not found")); - wordRepository.delete(word); - } - - public List getActiveWords() { - return wordRepository.findByIsActiveTrue(); - } - - public List getWordsByLevel(int level) { - return wordRepository.findByLevel(level); - } -} -``` - -### REST Controller - -`WordController.java`: - -```java -package com.pronunciationapp.controller; - -import com.pronunciationapp.model.Word; -import com.pronunciationapp.service.WordService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping("/api/words") -public class WordController { - - @Autowired - private WordService wordService; - - @GetMapping - public List getAllWords() { - return wordService.getAllWords(); - } - - @GetMapping("/{id}") - public ResponseEntity getWordById(@PathVariable String id) { - return wordService.getWordById(id) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); - } - - @PostMapping - public Word createWord(@RequestBody Word word) { - return wordService.createWord(word); - } - - @PutMapping("/{id}") - public ResponseEntity updateWord( - @PathVariable String id, - @RequestBody Word wordDetails - ) { - Word updatedWord = wordService.updateWord(id, wordDetails); - return ResponseEntity.ok(updatedWord); - } - - @DeleteMapping("/{id}") - public ResponseEntity deleteWord(@PathVariable String id) { - wordService.deleteWord(id); - return ResponseEntity.ok().build(); - } - - @GetMapping("/active") - public List getActiveWords() { - return wordService.getActiveWords(); - } - - @GetMapping("/level/{level}") - public List getWordsByLevel(@PathVariable int level) { - return wordService.getWordsByLevel(level); - } -} -``` - -### Application Properties - -`application.properties`: - -```properties -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 LOCAL DB SERVER -spring.datasource.url=jdbc:h2:/home/albert/MyProjects/DataBase/pronunciationDB/pronunciationDB.db -spring.datasource.username=albert -spring.datasource.password=1234 - -# DDL OPTIONS: create-drop, create, update, none, validate -spring.jpa.hibernate.ddl-auto=update - -# Enable H2 web console -spring.h2.console.path=/h2-console -``` - -## Testing with Postman - -Sample JSON for testing: - -```json -{ - "words": [ - { - "id": "8b9248a4e0b64bbccf82e7723a3734279bf9bbc4", - "wordName": "benevolent", - "definition": "Kind and generous", - "phoneticSpelling": "/bɪˈnɛvələnt/", - "sentence": "Her benevolent actions helped many people in need.", - "isActive": true, - "level": 2 - }, - { - "id": "3a7bd3e2a07e8c7e9b6e0d2c1f4a5b8d9c0e3f2", - "wordName": "serendipity", - "definition": "A fortunate discovery", - "phoneticSpelling": "/ˌserənˈdɪpɪti/", - "sentence": "Meeting her was pure serendipity.", - "isActive": true, - "level": 3 - } - ] -} -``` - -### Postman Collection Setup - -Create a new collection for `PronunciationApp` with these CRUD endpoints: - -1. **GET All Words** - - - Method: GET - - URL: `http://localhost:8080/api/words` - - Expected: Returns full list of words - -2. **GET Word by ID** - - - Method: GET - - URL: `http://localhost:8080/api/words/{id}` - - Params: Replace `{id}` with a specific word ID - - Expected: Returns single word details - -3. **POST Create Word** - - - Method: POST - - - URL: `http://localhost:8080/api/words` - - - Body (raw JSON): - - ```json - { - "wordName": "ephemeral", - "definition": "Lasting for a very short time", - "phoneticSpelling": "/ɪˈfɛmərəl/", - "sentence": "The beauty of cherry blossoms is ephemeral.", - "isActive": true, - "level": 2 - } - ``` - - - Expected: Returns created word with auto-generated ID - -4. **PUT Update Word** - - - Method: PUT - - - URL: `http://localhost:8080/api/words/{id}` - - - Params: Replace `{id}` with existing word ID - - - Body (raw JSON): - - ```json - { - "wordName": "ephemeral", - "definition": "Lasting for a very short time", - "phoneticSpelling": "/ɪˈfɛmərəl/", - "sentence": "The beauty of cherry blossoms is ephemeral.", - "isActive": false, - "level": 3 - } - ``` - - - Expected: Returns updated word - -5. **DELETE Word** - - - Method: DELETE - - URL: `http://localhost:8080/api/words/{id}` - - Params: Replace `{id}` with word ID to delete - - Expected: 200 OK response - -### Additional Test Endpoints - -- **GET Active Words**: `http://localhost:8080/api/words/active` -- **GET Words by Level**: `http://localhost:8080/api/words/level/2` - -### Testing Workflow - -1. Import collection in Postman -2. Start Spring Boot application -3. Execute requests in sequence -4. Verify responses match expected results - -### Common Testing Scenarios - -- Test creating words with missing fields -- Validate ID generation -- Check level and active status constraints -- Test boundary conditions (max/min levels) - -## Running the Application - -1. Ensure you have Java 17+ and Maven installed -2. Configure the database path in `application.properties` -3. Run the Spring Boot application -4. Access H2 console at `http://localhost:8080/h2-console` -5. Access API endpoints at `http://localhost:8080/api/words` - -## Notes - -- The application uses a **local** H2 database file -- UUID is used for generating unique word IDs -- Basic CRUD operations are implemented -- Additional **query methods are available in the repository** and service layers diff --git a/backend/resources/pronunciationApp-v0.2.md b/backend/resources/pronunciationApp-v0.2.md deleted file mode 100644 index aaf6a676a..000000000 --- a/backend/resources/pronunciationApp-v0.2.md +++ /dev/null @@ -1,498 +0,0 @@ -# PronunciationApp Backend v0.2 - -## Refactoring @RestController and adding @Service - -> Implementing a `WordService` and refactoring the `WordController` is a good practice in Spring Boot applications for several reasons. - -Let's implement the `WordService`, refactor the `WordController`, and discuss why this approach is beneficial. - -### Implementing WordService - -```java -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.Optional; - -@Service -public class WordService { - - @Autowired - private WordRepository wordRepository; - - public List getAllWords() { - return wordRepository.findAll(); - } - - public Optional getWordById(String id) { - return Optional.ofNullable(wordRepository.getWordById(id)); - } - - public Word createWord(Word word) { - return wordRepository.save(word); - } - - public Word updateWord(Word word) { - return wordRepository.save(word); - } - - public void deleteWord(String id) { - wordRepository.deleteById(id); - } - - public void deleteAllWords() { - wordRepository.deleteAll(); - } - - public boolean existsById(String id) { - return wordRepository.existsById(id); - } - - public long getWordCount() { - return wordRepository.count(); - } - - // Additional business logic can be added here - public Word getWordByPhoneticSpelling(String pronunciation) { - return wordRepository.getWordByPhoneticSpelling(pronunciation); - } -} -``` - -### Why is this refactoring necessary? - -1. **Separation of Concerns**: The service layer separates business logic from the controller, making the code more modular and easier to maintain. - -2. **Reusability**: Business logic in the service can be reused across different controllers or other services. - -3. **Testability**: It's easier to unit test business logic in a service class than in a controller. - -4. **Scalability**: As the application grows, having a separate service layer makes it easier to manage and scale the codebase. - -5. **Abstraction**: The controller doesn't need to know about the repository implementation, providing better abstraction. - -#### Refactoring WordController - -```java -@RestController -@RequestMapping("/api/words") -public class WordController { - - @Autowired - private WordService wordService; - - /* - @GetMapping("/hello") - public ResponseEntity hello() { - HttpHeaders headers = getCommonHeaders("Hello endpoint"); - return new ResponseEntity<>("hello Emiliano, are you sleeping?", headers, HttpStatus.OK); - } - */ - - @GetMapping - public ResponseEntity> getAllWords() { - List words = wordService.getAllWords(); - HttpHeaders headers = getCommonHeaders("Get all words"); - - return !words.isEmpty() - ? new ResponseEntity<>(words, headers, HttpStatus.OK) - : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND); - } - - @GetMapping("/{id}") - public ResponseEntity getWordById(@PathVariable String id) { - Optional word = wordService.getWordById(id); - HttpHeaders headers = getCommonHeaders("Get word by ID"); - - return word.map(value -> new ResponseEntity<>(value, headers, HttpStatus.OK)) - .orElseGet(() -> new ResponseEntity<>(headers, HttpStatus.NOT_FOUND)); - } - - @PostMapping("/createWord") - public ResponseEntity createWord(@RequestBody Word word) { - Word createdWord = wordService.createWord(word); - HttpHeaders headers = getCommonHeaders("Create a new word"); - - return new ResponseEntity<>(createdWord, headers, HttpStatus.CREATED); - } - - @PutMapping("/{id}") - public ResponseEntity updateWord(@PathVariable String id, @RequestBody Word word) { - Word updatedWord = wordService.updateWord(word); - HttpHeaders headers = getCommonHeaders("Update a word"); - - return new ResponseEntity<>(updatedWord, headers, HttpStatus.OK); - } - - @DeleteMapping("/{id}") - public ResponseEntity deleteWord(@PathVariable("id") String idToDelete) { - HttpHeaders headers = getCommonHeaders("Delete a word"); - - if (wordService.existsById(idToDelete)) { - wordService.deleteWord(idToDelete); - return new ResponseEntity<>("Word deleted", headers, HttpStatus.OK); - } else { - return new ResponseEntity<>("Word not found", headers, HttpStatus.NOT_FOUND); - } - } - - @DeleteMapping - public ResponseEntity deleteAllWords() { - wordService.deleteAllWords(); - HttpHeaders headers = getCommonHeaders("Delete all words"); - return new ResponseEntity<>("All words deleted", headers, HttpStatus.OK); - } - - 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(wordService.getWordCount())); - headers.add("object", "words"); - return headers; - } -} -``` - -## Check health - -Well create a new controller class that incorporates the health check endpoint along with other best practices. - -Here's the new `HealthController` class: - -```java -package dev.pronunciationAppBack.controller; - -import dev.pronunciationAppBack.model.Word; -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; - - @GetMapping - public ResponseEntity> healthCheck() { - Map healthStatus = new HashMap<>(); - healthStatus.put("status", "UP"); - healthStatus.put("timestamp", new Date()); - healthStatus.put("wordCount", wordService.getWordCount()); - - 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 getMemoryUsage() { - Runtime runtime = Runtime.getRuntime(); - Map memoryInfo = new HashMap<>(); - memoryInfo.put("total", runtime.totalMemory()); - memoryInfo.put("free", runtime.freeMemory()); - memoryInfo.put("used", runtime.totalMemory() - runtime.freeMemory()); - return memoryInfo; - } - - private Map getDiskSpace() { - java.io.File root = new java.io.File("/"); - Map 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; - } -} -``` - -This new `HealthController` class offers several improvements: - -1. **Dedicated controller**: It's a separate controller focused on health and monitoring, following the Single Responsibility Principle. - -2. **Comprehensive health check**: The `healthCheck()` method provides a detailed health status, including database connectivity, word count, memory usage, and disk space. - -3. **Modular design**: Different aspects of the health check are separated into methods, making the code more maintainable and easier to extend. - -4. **Resource monitoring**: It includes basic system resource monitoring (memory and disk space), which can be crucial for identifying potential issues. - -5. **Consistent headers**: It uses a `getCommonHeaders()` method similar to the `WordController`, maintaining consistency across the API. - -6. **Appropriate mapping**: The controller uses the `/api/health` endpoint, which is a common convention for health check APIs. - -> This health check endpoint provides a comprehensive overview of your application's health, making it valuable for monitoring and troubleshooting in production environments. - -## @Service business logic examples - -#### Real Use Case #1 - -Let's consider a real use case where the service layer becomes key: - -Imagine you're expanding your pronunciation app to include a feature for generating word lists based on difficulty levels. This involves complex logic that shouldn't be in the controller. - -Add this method to `WordService`: - -```java -public List getWordsByDifficultyLevel(int level) { - List allWords = wordRepository.findAll(); - return allWords.stream() - .filter(word -> calculateDifficulty(word) == level) - .collect(Collectors.toList()); -} - -private int calculateDifficulty(Word word) { - // Complex logic to determine word difficulty - // Based on factors like length, rarity, phonetic complexity, etc. - // This is a simplified example - int difficulty = word.getWord().length(); - difficulty += word.getPhoneticSpelling().length(); - // More factors... - return difficulty / 5; // Normalize to a 1-10 scale -} -``` - -And in `WordController`: - -```java -@GetMapping("/difficulty/{level}") -public ResponseEntity> getWordsByDifficulty(@PathVariable int level) { - List words = wordService.getWordsByDifficultyLevel(level); - HttpHeaders headers = getCommonHeaders("Get words by difficulty"); - - return !words.isEmpty() - ? new ResponseEntity<>(words, headers, HttpStatus.OK) - : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND); -} -``` - -In this case, the service layer is key because: - -1. It encapsulates complex business logic (difficulty calculation) that doesn't belong in the controller. -2. This logic can be reused in other parts of the application. -3. It's easier to test and modify the difficulty calculation independently of the controller. -4. If you decide to change how difficulty is calculated or stored (e.g., moving to a database-computed value), you only need to change the service, not the controller. - -> This structure allows your application to grow and adapt to new requirements more easily, demonstrating the importance of a well-structured service layer in a Spring Boot application. - -#### Real Use Case #2 - -Let's consider another real-world use case where the WordService would be essential: implementing a personalized word recommendation system based on a user's learning history and performance. - -Here's how we could implement this feature: - -```java -@Service -public class WordService { - @Autowired - private WordRepository wordRepository; - - @Autowired - private UserRepository userRepository; - - @Autowired - private UserProgressRepository userProgressRepository; - - public List getPersonalizedWordRecommendations(String userId, int count) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new UserNotFoundException("User not found")); - - List userProgress = userProgressRepository.findByUserId(userId); - - Set masteredWords = userProgress.stream() - .filter(progress -> progress.getMasteryLevel() > 0.8) - .map(UserProgress::getWordId) - .collect(Collectors.toSet()); - - List allWords = wordRepository.findAll(); - - return allWords.stream() - .filter(word -> !masteredWords.contains(word.getId())) - .sorted(Comparator.comparingDouble(word -> calculateRelevance(word, user, userProgress))) - .limit(count) - .collect(Collectors.toList()); - } - - private double calculateRelevance(Word word, User user, List userProgress) { - double difficultyScore = calculateDifficulty(word); - double userLevelScore = user.getProficiencyLevel(); - double progressScore = userProgress.stream() - .filter(progress -> progress.getWordId().equals(word.getId())) - .mapToDouble(UserProgress::getMasteryLevel) - .findFirst() - .orElse(0.0); - - // Complex algorithm to determine word relevance based on user's level, - // word difficulty, and user's progress on this word - return (difficultyScore * 0.4) + (userLevelScore * 0.3) + ((1 - progressScore) * 0.3); - } - - private double calculateDifficulty(Word word) { - // Implementation of difficulty calculation - // ... - } -} -``` - -In the controller: - -```java -@RestController -@RequestMapping("/api/words") -public class WordController { - @Autowired - private WordService wordService; - - @GetMapping("/recommendations/{userId}") - public ResponseEntity> getPersonalizedRecommendations( - @PathVariable String userId, - @RequestParam(defaultValue = "10") int count) { - List recommendations = wordService.getPersonalizedWordRecommendations(userId, count); - HttpHeaders headers = getCommonHeaders("Get personalized word recommendations"); - - return !recommendations.isEmpty() - ? new ResponseEntity<>(recommendations, headers, HttpStatus.OK) - : new ResponseEntity<>(headers, HttpStatus.NOT_FOUND); - } -} -``` - -This use case demonstrates why the service layer is key: - -1. Complex Business Logic: The recommendation system involves complex calculations and data processing that shouldn't be in the controller. - -2. Data Integration: It requires integration of data from multiple repositories (words, users, and user progress), which is better handled in a service. - -3. Reusability: The recommendation logic can be reused in other parts of the application, such as generating daily practice sessions or progress reports. - -4. Scalability: As the recommendation algorithm becomes more sophisticated (e.g., incorporating machine learning), the service can be easily extended without affecting the controller. - -5. Testability: The complex recommendation logic can be unit tested independently of the web layer. - -6. Separation of Concerns: The controller remains focused on handling HTTP requests and responses, while the service manages the business logic. - -7. Flexibility: If you decide to change how recommendations are generated (e.g., using a third-party AI service), you only need to modify the service, not the controller. - -> This example showcases how a well-structured service layer can handle complex, data-intensive operations while keeping the controller lean and focused on its primary responsibility of managing HTTP interactions. - -## JpaRepository - -```java -public interface WordRepository extends JpaRepository { -``` - -> JpaRepository is an interface provided by Spring Data JPA that simplifies database operations. -> -> It extends other repository interfaces, offering a complete set of CRUD (Create, Read, Update, Delete) operations, as well as paging and sorting capabilities. - -An interface in Java is a contract that specifies a set of abstract methods that a class must implement. It defines what a class should do, without specifying how it should do it. - -In the case of `WordRepository`, extending` JpaRepository` is a good idea because: - -1. Automatic implementation: Spring Data JPA automatically generates the implementation of the repository interface at runtime, saving developers from writing boilerplate code. - -2. Built-in methods: It provides common database operations like save(), findAll(), and deleteById() out of the box. - -3. Custom queries: You can define custom query methods, as seen with getWordById() and getWordByPhoneticSpelling(), by simply declaring them in the interface. - -4. Type safety: JpaRepository uses generics (), ensuring type safety for the entity (Word) and its ID type (String). - -5. Extensibility: You can easily add more custom methods as your application's requirements grow. - -### Queries - -- [Lab#SB08-3: H2 and API Rest – albertprofe wiki](https://albertprofe.dev/springboot/sblab8-3.html#jpa-query-methods) - -**JPA Derived Query Methods** - -`Spring Data JPA` can automatically create queries based on method names in your repository interface. - -```java -public interface UserRepository - extends JpaRepository { - List findByLastNameAndAge(String lastName, int age); -} -``` - -**@Query Annotation** - -You can use the `@Query` annotation to define custom JPQL queries. - -```java -public interface UserRepository - extends JpaRepository { - @Query("SELECT u FROM User u WHERE u.emailAddress = ?1") - User findByEmailAddress(String emailAddress); -} -``` - -**EntityManager with JPQL** - -For more complex queries, you can use the `EntityManager` directly with JPQL. - -```java -@PersistenceContext -private EntityManager entityManager; - -public List findUsersByAgeRange(int minAge, int maxAge) { - String jpql = "SELECT u FROM User u WHERE u.age BETWEEN :minAge AND :maxAge"; - return entityManager.createQuery(jpql, User.class) - .setParameter("minAge", minAge) - .setParameter("maxAge", maxAge) - .getResultList(); -} -``` - -**Native SQL Queries** - -When you need to use database-specific features, you can write native SQL queries. - -```java -public interface UserRepository extends JpaRepository { - @Query(value = "SELECT * FROM users WHERE status = ?1", nativeQuery = true) - List findUsersByStatus(int status); -} -``` diff --git a/backend/resources/springboot-for-5yo.md b/backend/resources/springboot-for-5yo.md deleted file mode 100644 index 37463525d..000000000 --- a/backend/resources/springboot-for-5yo.md +++ /dev/null @@ -1,41 +0,0 @@ -# Spring Boot and MVC to a 5yo kid - -> Imagine a big restaurant called `Spring Boot`. - -Here's how it works: - -- The menu is like the `View`. It shows you all the yummy food you can order. - -- When you come in, you meet the maitre. They're like the `Front Controller`, welcoming you and guiding you to your table. - -- The waiter is like the `Controller`. They take your order and make sure you get what you wan. - -- Once the waiter has the order, our restaurant is so big that all the waiters are queuing to deliver the orders to the **kitchen**: it's where all the food is prepared. Our restaurant has a orders-board where the waiter boss pins the order, it is like the `Service`. - -- In the kitchen, there's a chef who's like `JPA`. They know all the recipes and how to make the dishes just right. - -- The cook is like `SQL queries.` They do the actual cooking, following the chef's instructions. - -- Each plate has a super-sercret code just for superheroes, this secret code is the `id` at `database`. - -- All the ingredients are kept in the pantry and fridge, which is like the `database`. - -- And finally, the plates and dishes are like the `Model`. They hold all the delicious food that comes to your table. - -- Imagine you want pizza at home. The `API Rest` is like a super-fast delivery superhero who brings your yummy food exactly where you want it! - -- What if you could watch chefs cooking? The `WebSocket` is like a magical window where you see everything happening in the kitchen, right when it's happening! - -- The `Security` is like a kind and strong firend who makes sure only nice people can enter and keeps all the secret recipes safe. - -- The `Thymeleaf Templates` and `Vaadin` create super cool, colorful menus that make you smile and want to try everything. - -- When you want the perfect meal, the `AI` is like a magic waiter who knows exactly what you like and suggests the most delicious food just for you! - -- Someone needs to make sure everything works perfectly. The `Actuator` is like the restaurant's superhero manager who watches over everything. - -- What if restaurants could help each other? The `Spring Cloud` is like a big friendly restaurant family that shares cool tricks and helps everyone. - -- Want to cook something amazing super fast? The `Spring Initializr` is like a magic kitchen that appears instantly, ready to make any delicious meal you want! - -All these parts work together to make sure you have a great meal at the Spring Boot restaurant! diff --git a/frontend/resources/CreateReactviteProject.md b/frontend/resources/CreateReactviteProject.md deleted file mode 100644 index 8a95d296d..000000000 --- a/frontend/resources/CreateReactviteProject.md +++ /dev/null @@ -1,62 +0,0 @@ -# Create a Vite React project - -To create a Vite React project, follow these steps: - -1. Open your terminal and run one of the following commands: - -```bash -npm create vite@latest my-react-app -- --template react -``` - -or - -```bash -yarn create vite my-react-app --template react -``` - -or - -```bash -pnpm create vite my-react-app --template react -``` - -or - -```bash -bun create vite my-react-app --template react -``` - -2. Navigate to the project directory: - -```bash -cd my-react-app -``` - -3. Install dependencies: - -```bash -npm install -``` - -or use your preferred package manager (yarn, pnpm, or bun) - -4. Start the development server: - -```bash -npm run dev -``` - -This will scaffold a new Vite React project and start the development server. You can now open your browser and visit `http://localhost:5173` to see your React application[1]. - -## Additional Information - -- Vite requires Node.js version 18+ or 20+[1]. -- The project structure will be set up with React and its necessary dependencies. -- You can also use TypeScript by choosing the `react-ts` template instead of `react`[1]. -- Vite provides fast Hot Module Replacement (HMR) for a smooth development experience[1]. -- For production builds, use the command `npm run build`[1]. - -Remember to check the Vite documentation for more advanced configurations and features specific to React development. - -Citations: -[1] https://vite.dev/guide/ diff --git a/frontend/resources/pronunciationAppFront-v0.0-project-structure.png b/frontend/resources/pronunciationAppFront-v0.0-project-structure.png deleted file mode 100644 index 5f92a15ad..000000000 Binary files a/frontend/resources/pronunciationAppFront-v0.0-project-structure.png and /dev/null differ diff --git a/frontend/resources/pronunciationAppFront-v0.0-run-dev.png b/frontend/resources/pronunciationAppFront-v0.0-run-dev.png deleted file mode 100644 index 9a3248a7e..000000000 Binary files a/frontend/resources/pronunciationAppFront-v0.0-run-dev.png and /dev/null differ diff --git a/frontend/resources/pronunciationAppFront-v0.0-web.png b/frontend/resources/pronunciationAppFront-v0.0-web.png deleted file mode 100644 index 753aed9bf..000000000 Binary files a/frontend/resources/pronunciationAppFront-v0.0-web.png and /dev/null differ diff --git a/management/Mono-Multi-repo.md b/management/Mono-Multi-repo.md deleted file mode 100644 index 690e0c636..000000000 --- a/management/Mono-Multi-repo.md +++ /dev/null @@ -1,46 +0,0 @@ -Monorepos and multi-repos are two different approaches to organizing code repositories on platforms like GitHub. Each has its own advantages and considerations for development teams[1][2]. - -## Monorepo - -A monorepo is a single repository containing multiple projects or components[3]. This approach offers several benefits: - -- **Visibility:** Enhances accountability and security by making projects visible to a wider audience within the organization[1]. -- **Collaboration:** Fosters better teamwork by allowing developers to share and reuse assets efficiently[1]. -- **Atomic changes:** Enables changes across multiple projects with a single action, potentially speeding up development[1]. -- **Refactoring:** Simplifies application-wide refactoring, as all changes can be submitted in a single pull request[2]. - -However, monorepos also have some drawbacks: - -- **Scalability challenges:** Can be more difficult to manage as they grow in size[1]. -- **Large downloads:** Contributors may need to download the entire codebase, which can be time-consuming for large repositories[2]. -- **Versioning complexity:** Unmodified libraries may be newly versioned even without changes[2]. - -## Multi-Repo - -A multi-repo approach uses separate repositories for individual projects or components[3]. This structure offers: - -- **Simplicity:** Easier management of individual project complexities[1]. -- **Flexibility:** Teams can choose specific tools and workflows suited for their project[1]. -- **Fine-grained access control:** Offers more precise control over who can access specific projects[1]. - -However, multi-repos also have limitations: - -- **Coordination challenges:** May require more effort to coordinate changes across multiple projects[1]. -- **Dependency management:** Sharing common code across repositories can be more complex[3]. - -## Collaboration Considerations - -When choosing between monorepo and multi-repo for GitHub collaboration: - -1. **Team structure:** Monorepos can encourage a shared development culture, while multi-repos allow for more team autonomy[2]. -2. **Project interdependence:** Highly interconnected projects may benefit from a monorepo, while independent projects might suit a multi-repo approach[3]. -3. **Codebase size:** Large codebases in monorepos can slow down common Git operations[2]. -4. **Open-source contributions:** Multi-repos can make it easier for external contributors to navigate and contribute to specific projects[2]. - -Ultimately, the choice between monorepo and multi-repo depends on your team's specific needs, project structure, and collaboration goals. - -Citations: -[1] https://graphite.dev/guides/monorepo-vs-multi-repo -[2] https://kinsta.com/blog/monorepo-vs-multi-repo/ -[3] https://github.com/joelparkerhenderson/monorepo-vs-polyrepo?tab=readme-ov-file -[4] https://www.gitkraken.com/blog/monorepo-vs-multi-repo-collaboration diff --git a/management/architecture.jpg b/management/architecture.jpg deleted file mode 100644 index ecfefdbf2..000000000 Binary files a/management/architecture.jpg and /dev/null differ diff --git a/management/lang_en_pron_22080_speaker_8_syllable_all_version_50.mp4 b/management/lang_en_pron_22080_speaker_8_syllable_all_version_50.mp4 deleted file mode 100644 index bb6a4fa7f..000000000 Binary files a/management/lang_en_pron_22080_speaker_8_syllable_all_version_50.mp4 and /dev/null differ diff --git a/management/pronunciationApp.md b/management/pronunciationApp.md deleted file mode 100644 index 5fb5026d7..000000000 --- a/management/pronunciationApp.md +++ /dev/null @@ -1,30 +0,0 @@ -# English Pronountation App - -## Project - -| Project | DDD | UML | Code | -| -------------------------------------------- | ----- | ------------- | ---- | -| user-story | words | class diagram | | -| mock-up: figma, MUI cards | user | | | -| tech stack | | | | -| death Note: meta-goals | | | | -| architecture: h2-boot-rest-react | | | | -| markdown docs | | | | -| agile: metrics, goals, prioritation, sprints | | | | -| architecture draft | | | | - -## Links - -https://frenchtogether.com/ - -[x.com](https://x.com/starter_story) - -https://www.starterstory.com/ - -https://www.calai.app/ - -[Card - Definition, Meaning & Synonyms | Vocabulary.com](https://www.vocabulary.com/dictionary/card) - -https://forvo.com/ - -[The 17-Year-Old Who Built a $1.12M/Month Ai App | Zach Yadegari - YouTube](https://www.youtube.com/watch?v=t0U3UREdjmo) diff --git a/management/repository-structure.md b/management/repository-structure.md deleted file mode 100644 index 94fa3af70..000000000 --- a/management/repository-structure.md +++ /dev/null @@ -1,72 +0,0 @@ -# Project structure - -## Why is so relevant the project structure? - -- To handle the project: track, debug, mantain, trace, test, etc.. - -- Scale: keep it simple! - -- DDD: decouple - -- Stick to native structure - -- Tech debt - -- reuse code - -- create nice markdown documentation: onboarding, collaboration, etc.. - -## Repository Structure `pronunciationApp` - -Your repository structure could look like this: - -```bash -root/ - ├── .git/ # Git repository data - ├── frontend/ - │ ├── (React project files) - │ ├── package.json - │ ├── .gitignore - │ └── README.md - ├── backend/ - │ ├── (Spring Boot project files) - │ ├── pom.xml - │ ├── .gitignore - │ └── README.md - ├── PRA/ - │ ├── resources/ # New resources folder under PRA - │ │ └── (Resource files for PRA) - │ └── (Other PRA-related files and folders) - ├── .gitignore - └── README.md -``` - -## Setup Steps - -1. Create a new GitHub repository. -2. Clone the repository to your local machine. -3. Create two directories: `frontend` and `backend`. -4. Initialize your React project in the `frontend` directory. -5. Initialize your Spring Boot project in the `backend` directory. -6. Create a `.gitignore` file in the root directory to ignore common files/folders. -7. Create separate `.gitignore` files in both `frontend` and `backend` directories for project-specific ignores. -8. Add a README.md file in the root directory explaining the repository structure and how to run both projects. - -## Best Practices - -1. Use separate branches for frontend and backend development. -2. Create pull requests for merging changes into the main branch. -3. Use meaningful commit messages for each project. -4. Regularly pull changes from the remote repository to stay updated. -5. Consider using GitHub Actions for CI/CD pipelines for both projects. - -## Managing Dependencies - -- For the React project, use `npm` or `yarn` to manage dependencies (recorded in `package.json`). -- For the Spring Boot project, use Maven or Gradle to manage dependencies (recorded in `pom.xml` or `build.gradle`). - -## Collaboration - -- Clearly document how to set up and run both projects in their respective README files. -- Use GitHub Issues to track tasks, bugs, and feature requests for both projects. -- Consider using project boards in GitHub to manage the development process.