diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000000..ccf2ed7690e --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,119 @@ +language: ko-KR + +early_access: true +enable_free_tier: true +reviews: + profile: chill + request_changes_workflow: true + high_level_summary: true + high_level_summary_placeholder: '@coderabbitai μš”μ•½' + auto_title_placeholder: '@coderabbitai' + poem: true + review_status: true + collapse_walkthrough: false + path_filters: + - "!**/swagger.json" + - "!**/README.md" + path_instructions: + - path: "**/*.sql" + instructions: > + As a team convention, we do not specify FK constraints. Please avoid suggesting adding FK constraints in code reviews. + This file is a TypeScript file, but the SQL statements that modify the table structure are written as strings. For columns used as primary keys (PK) or foreign keys (FK), you must follow the convention of specifying the column type as "bigint". + If a table column name is "id" or ends with "_id", verify that the column type is "bigint". If the SQL is written with a type other than "bigint", issue a warning. This convention is mandatory, so please raise the severity level of the warning. Add the following characters to the beginning of the warning message so that users can easily notice it: 🚨🚨🚨 + + - path: "**/*Controller*(V[0-9]*)?.ts" + instructions: > + Review the API endpoints based on this guidelines. + + ### Richardson Maturity Model (RMM) + 1. Level 0: Single URI with POST requests for all actions. + 2. Level 1: Separate URIs for individual resources. + 3. Level 2: Use of HTTP methods (GET, POST, PUT, DELETE) to define operations on resources. + 4. Level 3: Hypermedia (HATEOAS) for advanced RESTful APIs. + + ### API Conventions + - URI Rules: + - Should be intuitive and self-explanatory. + - Should not map 1:1 to database tables. + - Must be stateless, with no session state between requests. + - Include "api" and version in the URI (/api/{version}/resource). + - Use kebab-case for URIs and camelCase for parameters and body contents. + - Resource identifiers should be unique and only one per URI path. + + - Design Principles: + - APIs should be designed around resources, which are abstractions rather than direct database tables. + - Stateless APIs facilitate scalability and flexibility. + - Clear separation of frontend and backend via URI structure. + - Versioning in URI paths is preferred for clarity and ease of caching. + - Maintain consistent naming conventions across the API. + - Use plural forms for resource names (/users instead of /user). + - Complex actions can include verbs in the URI (/orders/{orderId}/cancel). + + - Implementation Details: + - Avoid deeply nested resource paths to ensure maintainability. + - Ensure URIs reflect the data they provide, not the permissions or roles required to access them. + - Keep URIs simple and predictable, aiding both developers and automated systems. + abort_on_close: true + auto_review: + enabled: true + auto_incremental_review: true + ignore_title_keywords: [] + labels: [] + drafts: false + base_branches: [] + tools: + shellcheck: + enabled: true + ruff: + enabled: true + markdownlint: + enabled: true + github-checks: + enabled: true + timeout_ms: 90000 + languagetool: + enabled: true + disabled_rules: + - EN_UNPAIRED_BRACKETS + - EN_UNPAIRED_QUOTES + disabled_categories: + - TYPOS + - TYPOGRAPHY + - CASING + enabled_only: false + level: default + enabled_rules: [] + enabled_categories: [] + biome: + enabled: true + hadolint: + enabled: true + swiftlint: + enabled: true + phpstan: + enabled: true + level: default + golangci-lint: + enabled: true + yamllint: + enabled: true + gitleaks: + enabled: true + checkov: + enabled: true + ast-grep: + packages: [] + rule_dirs: [] + util_dirs: [] + essential_rules: true +chat: + auto_reply: true +knowledge_base: + learnings: + scope: auto + issues: + scope: auto + jira: + project_keys: [] + linear: + team_keys: [] diff --git a/build.gradle b/build.gradle index b52d8e1859d..c2b39005622 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { // Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) --> implementation 'io.projectreactor:reactor-core' + compileOnly 'org.projectlombok:lombok' implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' @@ -58,6 +59,8 @@ dependencies { testImplementation 'org.testcontainers:mysql' checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}" checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}" + + annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { diff --git a/src/main/java/org/springframework/samples/petclinic/course/CouresRepository.java b/src/main/java/org/springframework/samples/petclinic/course/CouresRepository.java new file mode 100644 index 00000000000..9246545191d --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/course/CouresRepository.java @@ -0,0 +1,6 @@ +package org.springframework.samples.petclinic.course; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CouresRepository extends JpaRepository { +} diff --git a/src/main/java/org/springframework/samples/petclinic/course/Course.java b/src/main/java/org/springframework/samples/petclinic/course/Course.java new file mode 100644 index 00000000000..df33a3c2055 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/course/Course.java @@ -0,0 +1,31 @@ +package org.springframework.samples.petclinic.course; + +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import org.springframework.samples.petclinic.model.BaseEntity; +import org.springframework.samples.petclinic.vet.Vet; + +@Getter +@Setter +@Entity +@Table(name = "courses") +public class Course extends BaseEntity { + private String name; + private String description; + // 예: μ΄ˆκΈ‰, 쀑급, κ³ κΈ‰ + private String difficulty; + + @ManyToOne + @JoinColumn(name = "vet_id") + private Vet instructor; + +} diff --git a/src/main/java/org/springframework/samples/petclinic/course/CourseController.java b/src/main/java/org/springframework/samples/petclinic/course/CourseController.java new file mode 100644 index 00000000000..cf1c83c018c --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/course/CourseController.java @@ -0,0 +1,30 @@ +package org.springframework.samples.petclinic.course; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.samples.petclinic.course.dto.CreateCourseRequest; +import org.springframework.samples.petclinic.vet.Vet; +import org.springframework.samples.petclinic.vet.VetRepository; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/courses") +public class CourseController { + private final CourseService courseService; + + + @PostMapping() + public ResponseEntity createCourse(@RequestBody CreateCourseRequest request) { + System.out.println(request); + + Course course = courseService.createCourse(request.getName(), request.getDescription(), request.getDescription(), request.getVetId()); + + return ResponseEntity.ok(course.getId()); + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/course/CourseService.java b/src/main/java/org/springframework/samples/petclinic/course/CourseService.java new file mode 100644 index 00000000000..10995b3cf07 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/course/CourseService.java @@ -0,0 +1,40 @@ +package org.springframework.samples.petclinic.course; + +import lombok.RequiredArgsConstructor; +import org.springframework.samples.petclinic.vet.Vet; +import org.springframework.samples.petclinic.vet.VetRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; + + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CourseService { + private final CouresRepository courseRepository; + private final VetRepository vetRepository; + + + @Transactional() + public Course createCourse(String name, String description, String difficulty, Integer vetId) { + Collection all = vetRepository.findAll(); + + all.forEach(course -> { + System.out.println(course.getId()); + }); + + Vet instructor = vetRepository.findById(vetId) + .orElseThrow(() -> new RuntimeException("Vet not found")); + + Course course = new Course(); + course.setName(name); + course.setDescription(description); + course.setDifficulty(difficulty); + course.setInstructor(instructor); + courseRepository.save(course); + + return course; + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/course/dto/CreateCourseRequest.java b/src/main/java/org/springframework/samples/petclinic/course/dto/CreateCourseRequest.java new file mode 100644 index 00000000000..8c165513aa5 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/course/dto/CreateCourseRequest.java @@ -0,0 +1,31 @@ +package org.springframework.samples.petclinic.course.dto; + +import lombok.Getter; + +@Getter() +public class CreateCourseRequest { + private String name; + private String description; + private String difficulty; + private Integer vetId; + + public CreateCourseRequest() { + } + + public CreateCourseRequest(String name, String description, String difficulty, Integer vetId) { + this.name = name; + this.description = description; + this.difficulty = difficulty; + this.vetId = vetId; + } + + @Override + public String toString() { + return "CreateCourseRequest{" + + "name='" + name + '\'' + + ", description='" + description + '\'' + + ", difficulty='" + difficulty + '\'' + + ", vetId=" + vetId + + '}'; + } +} diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java index 8b9e0823c86..84f3f832e63 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java @@ -23,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; +import java.util.Optional; /** * Repository class for Vet domain objects All method names are compliant @@ -37,6 +38,13 @@ */ public interface VetRepository extends Repository { + /** + * Retrieve a Vet from the data store. + * @return a Optional of Vet + */ + @Transactional(readOnly = true) + Optional findById(Integer id) throws DataAccessException; + /** * Retrieve all Vets from the data store. * @return a Collection of Vets diff --git a/src/main/resources/db/h2/schema.sql b/src/main/resources/db/h2/schema.sql index 4a6c322cbcc..4368a6e7e7d 100644 --- a/src/main/resources/db/h2/schema.sql +++ b/src/main/resources/db/h2/schema.sql @@ -62,3 +62,13 @@ CREATE TABLE visits ( ); ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id); CREATE INDEX visits_pet_id ON visits (pet_id); + +CREATE TABLE courses ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255), + description VARCHAR(1000), + difficulty VARCHAR(255), + vet_id INTEGER +); +CREATE INDEX courses_vet_id ON courses (vet_id); +