Skip to content

Commit

Permalink
Add personage management functionality
Browse files Browse the repository at this point in the history
Introduced core classes, services, and configurations necessary for handling personages in the application. Includes creating, updating, retrieving, and deleting personages, as well as exception handling and data loading integrations.
  • Loading branch information
chertiav committed Nov 15, 2024
1 parent c3bbe60 commit fbcb8da
Show file tree
Hide file tree
Showing 19 changed files with 477 additions and 19 deletions.
96 changes: 84 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.4</version>
<version>3.3.5</version>
<relativePath/>
</parent>
<groupId>mate.academy</groupId>
Expand All @@ -15,7 +15,9 @@
<description>jv-rick-and-morty</description>
<properties>
<java.version>17</java.version>
<maven.checkstyle.plugin.version>3.1.1</maven.checkstyle.plugin.version>
<maven.checkstyle.plugin.version>3.6.0</maven.checkstyle.plugin.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<lombok.mapstruct.binding.version>0.2.0</lombok.mapstruct.binding.version>
<maven.checkstyle.plugin.configLocation>
https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml
</maven.checkstyle.plugin.configLocation>
Expand All @@ -25,34 +27,62 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<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>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--mapstruct-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
<executions>
<execution>
<phase>compile</phase>
Expand All @@ -66,6 +96,48 @@
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
<sourceDirectories>src</sourceDirectories>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<!--lombok-->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!--mapstruct-->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok.mapstruct.binding.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!--lombok-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
Expand Down
1 change: 0 additions & 1 deletion src/main/java/mate/academy/rickandmorty/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package mate.academy.rickandmorty.config;

import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.external.PersonageDataDto;
import mate.academy.rickandmorty.mapper.PersonageMapper;
import mate.academy.rickandmorty.model.Personage;
import mate.academy.rickandmorty.service.PersonageClient;
import mate.academy.rickandmorty.service.PersonageService;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class DataLoadConfiguration {
private final PersonageClient personageClient;
private final PersonageService personageService;
private final PersonageMapper personageMapper;

@Bean
public ApplicationRunner loadDataAtStartup() {
return args -> {
List<PersonageDataDto> charactersData = personageClient.fetchPersonages();
List<Personage> personages = charactersData.stream()
.map(personageMapper::toModelFromMeteData)
.toList();
personageService.savePersonages(personages);
};
}
}
13 changes: 13 additions & 0 deletions src/main/java/mate/academy/rickandmorty/config/MapperConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mate.academy.rickandmorty.config;

import org.mapstruct.InjectionStrategy;
import org.mapstruct.NullValueCheckStrategy;

@org.mapstruct.MapperConfig(
componentModel = "spring",
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
implementationPackage = "<PACKAGE_NAME>.impl"
)
public class MapperConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mate.academy.rickandmorty.controller;

import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.internal.CreatePersonageRequestDto;
import mate.academy.rickandmorty.dto.internal.PersonageDto;
import mate.academy.rickandmorty.service.PersonageService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/personages")
@RequiredArgsConstructor
public class PersonageController {
private final PersonageService personageService;

@GetMapping
public List<PersonageDto> getAll() {
return personageService.findAll();
}

@GetMapping("/{id}")
public PersonageDto getPersonageById(@PathVariable Long id) {
return personageService.findById(id);
}

@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public PersonageDto createPersonage(@RequestBody @Valid CreatePersonageRequestDto requestDto) {
return personageService.save(requestDto);
}

@PutMapping("/{id}")
public PersonageDto updatePersonage(@PathVariable Long id,
@RequestBody @Valid CreatePersonageRequestDto requestDto) {
return personageService.updateById(id, requestDto);
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{id}")
public void deletePersonage(@PathVariable Long id) {
personageService.deleteById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package mate.academy.rickandmorty.dto.external;

public record PersonageDataDto(
String id,
String name,
String status,
String gender) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mate.academy.rickandmorty.dto.external;

import java.util.List;
import lombok.Data;

@Data
public class PersonageResponseDataDto {
private List<PersonageDataDto> results;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mate.academy.rickandmorty.dto.internal;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreatePersonageRequestDto {
@NotBlank(message = "externalId is mandatory")
private String externalId;
@NotBlank(message = "name is mandatory")
private String name;
@NotBlank(message = "status is mandatory")
private String status;
@NotBlank(message = "gender is mandatory")
private String gender;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mate.academy.rickandmorty.dto.internal;

import lombok.Data;

@Data
public class PersonageDto {
private Long id;
private String externalId;
private String name;
private String status;
private String gender;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package mate.academy.rickandmorty.exception;

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
@NonNull HttpHeaders headers,
@NonNull HttpStatusCode status,
@NonNull WebRequest request) {
Map<String, Object> errors = new LinkedHashMap<>();
errors.put("timestamp", LocalDateTime.now());
errors.put("status", HttpStatus.BAD_REQUEST);
List<String> errorMessages = ex.getBindingResult().getAllErrors().stream()
.map(this::getErrorMessage)
.toList();
errors.put("errors", errorMessages);
return new ResponseEntity<>(errors, headers, HttpStatus.BAD_REQUEST);
}

private String getErrorMessage(ObjectError e) {
if (e instanceof FieldError fieldError) {
return fieldError.getField() + ": " + getMessage(e);
}
return getMessage(e);
}

private String getMessage(ObjectError e) {
return e.getDefaultMessage() != null
? e.getDefaultMessage()
: "Something went wrong, please check your input";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mate.academy.rickandmorty.exception;

public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package mate.academy.rickandmorty.mapper;

import mate.academy.rickandmorty.config.MapperConfig;
import mate.academy.rickandmorty.dto.external.PersonageDataDto;
import mate.academy.rickandmorty.dto.internal.CreatePersonageRequestDto;
import mate.academy.rickandmorty.dto.internal.PersonageDto;
import mate.academy.rickandmorty.model.Personage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper(config = MapperConfig.class)
public interface PersonageMapper {
PersonageDto toDto(Personage personage);

Personage toModel(CreatePersonageRequestDto requestDto);

@Mapping(target = "externalId", source = "id")
Personage toModelFromMeteData(PersonageDataDto dataDto);

Personage updatePersonageFromDto(
CreatePersonageRequestDto requestDto,
@MappingTarget Personage personage);
}
Loading

0 comments on commit fbcb8da

Please sign in to comment.