feat: initial Claude Code configuration scaffold
Comprehensive Claude Code guidance system with: - 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit - 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#), infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD), testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability) - 3 hooks: secret detection, auto-formatting, TDD git pre-commit - Strict TDD enforcement with 80%+ coverage requirements - Multi-model strategy: Opus for planning, Sonnet for execution (opusplan) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
675
.claude/skills/languages/java/SKILL.md
Normal file
675
.claude/skills/languages/java/SKILL.md
Normal file
@@ -0,0 +1,675 @@
|
||||
---
|
||||
name: java-development
|
||||
description: Java development patterns with Spring Boot, JUnit 5, and modern Java practices. Use when writing Java code.
|
||||
---
|
||||
|
||||
# Java Development Skill
|
||||
|
||||
## Project Structure (Spring Boot)
|
||||
|
||||
```
|
||||
project/
|
||||
├── src/
|
||||
│ ├── main/
|
||||
│ │ ├── java/
|
||||
│ │ │ └── com/mycompany/myapp/
|
||||
│ │ │ ├── MyApplication.java
|
||||
│ │ │ ├── domain/
|
||||
│ │ │ │ ├── User.java
|
||||
│ │ │ │ └── Order.java
|
||||
│ │ │ ├── repository/
|
||||
│ │ │ │ └── UserRepository.java
|
||||
│ │ │ ├── service/
|
||||
│ │ │ │ └── UserService.java
|
||||
│ │ │ ├── controller/
|
||||
│ │ │ │ └── UserController.java
|
||||
│ │ │ └── config/
|
||||
│ │ │ └── SecurityConfig.java
|
||||
│ │ └── resources/
|
||||
│ │ ├── application.yml
|
||||
│ │ └── application-test.yml
|
||||
│ └── test/
|
||||
│ └── java/
|
||||
│ └── com/mycompany/myapp/
|
||||
│ ├── service/
|
||||
│ │ └── UserServiceTest.java
|
||||
│ └── controller/
|
||||
│ └── UserControllerTest.java
|
||||
├── pom.xml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Testing with JUnit 5
|
||||
|
||||
### Unit Tests with Mockito
|
||||
```java
|
||||
package com.mycompany.myapp.service;
|
||||
|
||||
import com.mycompany.myapp.domain.User;
|
||||
import com.mycompany.myapp.repository.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
@Nested
|
||||
@DisplayName("createUser")
|
||||
class CreateUser {
|
||||
|
||||
@Test
|
||||
@DisplayName("should create user with valid data")
|
||||
void shouldCreateUserWithValidData() {
|
||||
// Given
|
||||
var request = UserFixtures.createUserRequest();
|
||||
var savedUser = UserFixtures.user();
|
||||
when(userRepository.save(any(User.class))).thenReturn(savedUser);
|
||||
|
||||
// When
|
||||
var result = userService.createUser(request);
|
||||
|
||||
// Then
|
||||
assertThat(result.getId()).isNotNull();
|
||||
assertThat(result.getEmail()).isEqualTo(request.getEmail());
|
||||
verify(userRepository).save(any(User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should throw exception for duplicate email")
|
||||
void shouldThrowExceptionForDuplicateEmail() {
|
||||
// Given
|
||||
var request = UserFixtures.createUserRequest();
|
||||
when(userRepository.existsByEmail(request.getEmail())).thenReturn(true);
|
||||
|
||||
// When/Then
|
||||
assertThatThrownBy(() -> userService.createUser(request))
|
||||
.isInstanceOf(DuplicateEmailException.class)
|
||||
.hasMessageContaining(request.getEmail());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("getUserById")
|
||||
class GetUserById {
|
||||
|
||||
@Test
|
||||
@DisplayName("should return user when found")
|
||||
void shouldReturnUserWhenFound() {
|
||||
// Given
|
||||
var user = UserFixtures.user();
|
||||
when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));
|
||||
|
||||
// When
|
||||
var result = userService.getUserById(user.getId());
|
||||
|
||||
// Then
|
||||
assertThat(result).isPresent();
|
||||
assertThat(result.get().getId()).isEqualTo(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should return empty when user not found")
|
||||
void shouldReturnEmptyWhenNotFound() {
|
||||
// Given
|
||||
when(userRepository.findById(any())).thenReturn(Optional.empty());
|
||||
|
||||
// When
|
||||
var result = userService.getUserById("unknown-id");
|
||||
|
||||
// Then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Test Fixtures
|
||||
```java
|
||||
package com.mycompany.myapp.fixtures;
|
||||
|
||||
import com.mycompany.myapp.domain.User;
|
||||
import com.mycompany.myapp.dto.CreateUserRequest;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class UserFixtures {
|
||||
|
||||
private UserFixtures() {}
|
||||
|
||||
public static User user() {
|
||||
return user(builder -> {});
|
||||
}
|
||||
|
||||
public static User user(UserCustomizer customizer) {
|
||||
var builder = User.builder()
|
||||
.id(UUID.randomUUID().toString())
|
||||
.email("test@example.com")
|
||||
.name("Test User")
|
||||
.role(Role.USER)
|
||||
.createdAt(Instant.now())
|
||||
.updatedAt(Instant.now());
|
||||
|
||||
customizer.customize(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static CreateUserRequest createUserRequest() {
|
||||
return createUserRequest(builder -> {});
|
||||
}
|
||||
|
||||
public static CreateUserRequest createUserRequest(CreateUserRequestCustomizer customizer) {
|
||||
var builder = CreateUserRequest.builder()
|
||||
.email("newuser@example.com")
|
||||
.name("New User")
|
||||
.password("SecurePass123!");
|
||||
|
||||
customizer.customize(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UserCustomizer {
|
||||
void customize(User.UserBuilder builder);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CreateUserRequestCustomizer {
|
||||
void customize(CreateUserRequest.CreateUserRequestBuilder builder);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in tests
|
||||
var adminUser = UserFixtures.user(builder -> builder.role(Role.ADMIN));
|
||||
var customRequest = UserFixtures.createUserRequest(builder ->
|
||||
builder.email("custom@example.com"));
|
||||
```
|
||||
|
||||
### Parameterized Tests
|
||||
```java
|
||||
package com.mycompany.myapp.validation;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class EmailValidatorTest {
|
||||
|
||||
private final EmailValidator validator = new EmailValidator();
|
||||
|
||||
@ParameterizedTest
|
||||
@DisplayName("should accept valid emails")
|
||||
@ValueSource(strings = {
|
||||
"user@example.com",
|
||||
"user.name@example.com",
|
||||
"user+tag@example.co.uk"
|
||||
})
|
||||
void shouldAcceptValidEmails(String email) {
|
||||
assertThat(validator.isValid(email)).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@DisplayName("should reject invalid emails")
|
||||
@ValueSource(strings = {
|
||||
"invalid",
|
||||
"missing@domain",
|
||||
"@nodomain.com",
|
||||
"spaces in@email.com"
|
||||
})
|
||||
void shouldRejectInvalidEmails(String email) {
|
||||
assertThat(validator.isValid(email)).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@DisplayName("should reject null and empty emails")
|
||||
@NullAndEmptySource
|
||||
void shouldRejectNullAndEmpty(String email) {
|
||||
assertThat(validator.isValid(email)).isFalse();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@DisplayName("should validate password strength")
|
||||
@CsvSource({
|
||||
"password,false",
|
||||
"Password1,false",
|
||||
"Password1!,true",
|
||||
"P@ssw0rd,true"
|
||||
})
|
||||
void shouldValidatePasswordStrength(String password, boolean expected) {
|
||||
assertThat(validator.isStrongPassword(password)).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests with Spring
|
||||
```java
|
||||
package com.mycompany.myapp.controller;
|
||||
|
||||
import com.mycompany.myapp.fixtures.UserFixtures;
|
||||
import com.mycompany.myapp.repository.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
class UserControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("POST /api/users creates user and returns 201")
|
||||
void createUserReturnsCreated() throws Exception {
|
||||
var request = """
|
||||
{
|
||||
"email": "new@example.com",
|
||||
"name": "New User",
|
||||
"password": "SecurePass123!"
|
||||
}
|
||||
""";
|
||||
|
||||
mockMvc.perform(post("/api/users")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(request))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.id").isNotEmpty())
|
||||
.andExpect(jsonPath("$.email").value("new@example.com"))
|
||||
.andExpect(jsonPath("$.name").value("New User"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GET /api/users/{id} returns user when found")
|
||||
void getUserReturnsUserWhenFound() throws Exception {
|
||||
var user = userRepository.save(UserFixtures.user());
|
||||
|
||||
mockMvc.perform(get("/api/users/{id}", user.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").value(user.getId()))
|
||||
.andExpect(jsonPath("$.email").value(user.getEmail()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GET /api/users/{id} returns 404 when not found")
|
||||
void getUserReturns404WhenNotFound() throws Exception {
|
||||
mockMvc.perform(get("/api/users/{id}", "unknown-id"))
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Domain Models with Records
|
||||
|
||||
```java
|
||||
package com.mycompany.myapp.domain;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.time.Instant;
|
||||
|
||||
// Immutable domain model using records (Java 17+)
|
||||
public record User(
|
||||
String id,
|
||||
@NotBlank @Email String email,
|
||||
@NotBlank @Size(max = 100) String name,
|
||||
Role role,
|
||||
Instant createdAt,
|
||||
Instant updatedAt
|
||||
) {
|
||||
// Compact constructor for validation
|
||||
public User {
|
||||
if (email != null) {
|
||||
email = email.toLowerCase().trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Static factory methods
|
||||
public static User create(String email, String name) {
|
||||
var now = Instant.now();
|
||||
return new User(
|
||||
UUID.randomUUID().toString(),
|
||||
email,
|
||||
name,
|
||||
Role.USER,
|
||||
now,
|
||||
now
|
||||
);
|
||||
}
|
||||
|
||||
// Wither methods for immutable updates
|
||||
public User withName(String newName) {
|
||||
return new User(id, email, newName, role, createdAt, Instant.now());
|
||||
}
|
||||
|
||||
public User withRole(Role newRole) {
|
||||
return new User(id, email, name, newRole, createdAt, Instant.now());
|
||||
}
|
||||
}
|
||||
|
||||
// For mutable entities (JPA)
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String id;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Role role;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private Instant createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private Instant updatedAt;
|
||||
|
||||
@PrePersist
|
||||
void onCreate() {
|
||||
createdAt = Instant.now();
|
||||
updatedAt = createdAt;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
void onUpdate() {
|
||||
updatedAt = Instant.now();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Service Layer
|
||||
|
||||
```java
|
||||
package com.mycompany.myapp.service;
|
||||
|
||||
import com.mycompany.myapp.domain.User;
|
||||
import com.mycompany.myapp.dto.CreateUserRequest;
|
||||
import com.mycompany.myapp.exception.DuplicateEmailException;
|
||||
import com.mycompany.myapp.exception.UserNotFoundException;
|
||||
import com.mycompany.myapp.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class UserService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
@Transactional
|
||||
public User createUser(CreateUserRequest request) {
|
||||
if (userRepository.existsByEmail(request.getEmail())) {
|
||||
throw new DuplicateEmailException(request.getEmail());
|
||||
}
|
||||
|
||||
var user = User.create(
|
||||
request.getEmail(),
|
||||
request.getName()
|
||||
);
|
||||
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
public Optional<User> getUserById(String id) {
|
||||
return userRepository.findById(id);
|
||||
}
|
||||
|
||||
public User getUserByIdOrThrow(String id) {
|
||||
return userRepository.findById(id)
|
||||
.orElseThrow(() -> new UserNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User updateUser(String id, UpdateUserRequest request) {
|
||||
var user = getUserByIdOrThrow(id);
|
||||
|
||||
var updatedUser = user
|
||||
.withName(request.getName())
|
||||
.withRole(request.getRole());
|
||||
|
||||
return userRepository.save(updatedUser);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteUser(String id) {
|
||||
if (!userRepository.existsById(id)) {
|
||||
throw new UserNotFoundException(id);
|
||||
}
|
||||
userRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## REST Controllers
|
||||
|
||||
```java
|
||||
package com.mycompany.myapp.controller;
|
||||
|
||||
import com.mycompany.myapp.dto.*;
|
||||
import com.mycompany.myapp.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
|
||||
private final UserService userService;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@GetMapping
|
||||
public List<UserResponse> listUsers() {
|
||||
return userService.getAllUsers().stream()
|
||||
.map(userMapper::toResponse)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<UserResponse> getUser(@PathVariable String id) {
|
||||
return userService.getUserById(id)
|
||||
.map(userMapper::toResponse)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public UserResponse createUser(@Valid @RequestBody CreateUserRequest request) {
|
||||
var user = userService.createUser(request);
|
||||
return userMapper.toResponse(user);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public UserResponse updateUser(
|
||||
@PathVariable String id,
|
||||
@Valid @RequestBody UpdateUserRequest request) {
|
||||
var user = userService.updateUser(id, request);
|
||||
return userMapper.toResponse(user);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteUser(@PathVariable String id) {
|
||||
userService.deleteUser(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exception Handling
|
||||
|
||||
```java
|
||||
package com.mycompany.myapp.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ProblemDetail;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(UserNotFoundException.class)
|
||||
public ProblemDetail handleUserNotFound(UserNotFoundException ex) {
|
||||
var problem = ProblemDetail.forStatusAndDetail(
|
||||
HttpStatus.NOT_FOUND,
|
||||
ex.getMessage()
|
||||
);
|
||||
problem.setTitle("User Not Found");
|
||||
problem.setType(URI.create("https://api.myapp.com/errors/user-not-found"));
|
||||
return problem;
|
||||
}
|
||||
|
||||
@ExceptionHandler(DuplicateEmailException.class)
|
||||
public ProblemDetail handleDuplicateEmail(DuplicateEmailException ex) {
|
||||
var problem = ProblemDetail.forStatusAndDetail(
|
||||
HttpStatus.CONFLICT,
|
||||
ex.getMessage()
|
||||
);
|
||||
problem.setTitle("Duplicate Email");
|
||||
problem.setType(URI.create("https://api.myapp.com/errors/duplicate-email"));
|
||||
return problem;
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
|
||||
var problem = ProblemDetail.forStatusAndDetail(
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"Validation failed"
|
||||
);
|
||||
problem.setTitle("Validation Error");
|
||||
|
||||
var errors = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(e -> new ValidationError(e.getField(), e.getDefaultMessage()))
|
||||
.toList();
|
||||
|
||||
problem.setProperty("errors", errors);
|
||||
return problem;
|
||||
}
|
||||
|
||||
record ValidationError(String field, String message) {}
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Maven
|
||||
mvn clean install # Build and test
|
||||
mvn test # Run tests only
|
||||
mvn verify # Run integration tests
|
||||
mvn spring-boot:run # Run application
|
||||
mvn jacoco:report # Generate coverage report
|
||||
|
||||
# Gradle
|
||||
./gradlew build # Build and test
|
||||
./gradlew test # Run tests only
|
||||
./gradlew integrationTest # Run integration tests
|
||||
./gradlew bootRun # Run application
|
||||
./gradlew jacocoTestReport # Generate coverage report
|
||||
|
||||
# Common flags
|
||||
-DskipTests # Skip tests
|
||||
-Dspring.profiles.active=test # Set profile
|
||||
```
|
||||
|
||||
## Dependencies (pom.xml)
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
Reference in New Issue
Block a user