Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<maven.checkstyle.plugin.configLocation>
https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml
</maven.checkstyle.plugin.configLocation>
<lombok.mapstruct.binding.version>0.2.0</lombok.mapstruct.binding.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<dependencies>
<dependency>
Expand All @@ -32,6 +34,22 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
Expand All @@ -41,10 +59,41 @@
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<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>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mate.academy.rickandmorty.controller;

import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.character.CharacterDto;
import mate.academy.rickandmorty.service.character.CharacterService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/characters")
@RequiredArgsConstructor
public class CharacterController {
private final CharacterService characterService;

@GetMapping("/random")
public CharacterDto getRandom() {
return characterService.getRandomCharacter();
}

@GetMapping("/filter")
public List<CharacterDto> findByName(@RequestParam String name) {
return characterService.findByName(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.rickandmorty.dto;

import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import mate.academy.rickandmorty.dto.character.CharacterFromApiDto;

@Data
public class CharacterResponseDataDto {
private List<CharacterFromApiDto> results = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mate.academy.rickandmorty.dto.character;

import lombok.Data;

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

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class CharacterFromApiDto {
@JsonProperty("id")
private Long externalId;
private String name;
private String status;
private String gender;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.rickandmorty.mapper.character;

import mate.academy.rickandmorty.dto.character.CharacterDto;
import mate.academy.rickandmorty.dto.character.CharacterFromApiDto;
import mate.academy.rickandmorty.model.Character;

public interface CharacterMapper {
CharacterDto toDto(Character character);

Character toModel(CharacterFromApiDto character);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mate.academy.rickandmorty.mapper.character;

import mate.academy.rickandmorty.dto.character.CharacterDto;
import mate.academy.rickandmorty.dto.character.CharacterFromApiDto;
import mate.academy.rickandmorty.model.Character;
import org.springframework.stereotype.Component;

@Component
public class CharacterMapperImpl implements CharacterMapper {
@Override
public CharacterDto toDto(Character character) {
if (character == null) {
return null;
}

CharacterDto characterDto = new CharacterDto();

characterDto.setId(character.getId());
characterDto.setExternalId(character.getExternalId());
characterDto.setName(character.getName());
characterDto.setStatus(character.getStatus());
characterDto.setGender(character.getGender());

return characterDto;
}

@Override
public Character toModel(CharacterFromApiDto character) {
if (character == null) {
return null;
}

Character newCharacter = new Character();

newCharacter.setExternalId(character.getExternalId());
newCharacter.setName(character.getName());
newCharacter.setStatus(character.getStatus());
newCharacter.setGender(character.getGender());

return newCharacter;
}
}
21 changes: 21 additions & 0 deletions src/main/java/mate/academy/rickandmorty/model/Character.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package mate.academy.rickandmorty.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Data
@Entity
@Table(name = "characters")
public class Character {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long externalId;
private String name;
private String status;
private String gender;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mate.academy.rickandmorty.repository.character;

import java.util.List;
import mate.academy.rickandmorty.model.Character;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface CharacterRepository extends JpaRepository<Character, Long> {
@Query("Select c FROM character c WHERE LOWER (c.name)"
+ " LIKE LOWER(CONCAT('%', :searchTerm, '%'))")
List<Character> findByNameContainingIgnoreCase(@Param("searchTerm") String searchTerm);

@Query("FROM Character ORDER BY RAND() LIMIT 1")
Character getRandomCharacter();
Comment on lines +14 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getRandomCharacter method uses ORDER BY RAND() LIMIT 1, which is not standard JPQL syntax. JPQL does not support LIMIT or RAND() directly. Consider using native SQL or a different approach to fetch a random record.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package mate.academy.rickandmorty.service;

import java.util.List;
import mate.academy.rickandmorty.dto.CharacterResponseDataDto;
import mate.academy.rickandmorty.mapper.character.CharacterMapper;
import mate.academy.rickandmorty.model.Character;
import mate.academy.rickandmorty.repository.character.CharacterRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class AppStartupRunner implements CommandLineRunner {
private final CharacterClient characterClient;
private final CharacterMapper characterMapper;
private final CharacterRepository characterRepository;

public AppStartupRunner(CharacterClient characterClient,
CharacterMapper characterMapper,
CharacterRepository characterRepository) {
this.characterClient = characterClient;
this.characterMapper = characterMapper;
this.characterRepository = characterRepository;
}

@Override
public void run(String... args) throws Exception {
CharacterResponseDataDto characterResponseDataDto = characterClient.getAllCharacters();
List<Character> characters = characterResponseDataDto.getResults()
.stream()
.map(characterMapper::toModel)
.toList();
characterRepository.saveAll(characters);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package mate.academy.rickandmorty.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.CharacterResponseDataDto;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class CharacterClient {
private static final String URL = "https://rickandmortyapi.com/api/character";
private final ObjectMapper objectMapper;

public CharacterResponseDataDto getAllCharacters() {
CharacterResponseDataDto allCharacters = new CharacterResponseDataDto();

for (int currentPage = 1; currentPage <= 42; currentPage++) {
HttpClient client = HttpClient.newHttpClient();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new HttpClient instance inside the loop is inefficient. Consider moving HttpClient client = HttpClient.newHttpClient(); outside the loop to reuse the same client for all requests.


HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(URL + "?page=" + currentPage))
.build();

try {
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
CharacterResponseDataDto pageData = objectMapper
.readValue(response.body(), CharacterResponseDataDto.class);

allCharacters.getResults().addAll(pageData.getResults());
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
return allCharacters;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.rickandmorty.service.character;

import java.util.List;
import mate.academy.rickandmorty.dto.character.CharacterDto;

public interface CharacterService {
CharacterDto getRandomCharacter();

List<CharacterDto> findByName(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package mate.academy.rickandmorty.service.character;

import java.util.List;
import lombok.AllArgsConstructor;
import mate.academy.rickandmorty.dto.character.CharacterDto;
import mate.academy.rickandmorty.mapper.character.CharacterMapper;
import mate.academy.rickandmorty.repository.character.CharacterRepository;
import mate.academy.rickandmorty.service.CharacterClient;
import org.springframework.stereotype.Component;

@AllArgsConstructor
@Component
public class CharacterServiceImpl implements CharacterService {
private final CharacterRepository characterRepository;
private final CharacterMapper characterMapper;
private final CharacterClient characterClient;

@Override
public CharacterDto getRandomCharacter() {
return characterMapper.toDto(characterRepository.getRandomCharacter());
Comment on lines +19 to +20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getRandomCharacter method relies on the getRandomCharacter query in CharacterRepository, which uses non-standard JPQL syntax (ORDER BY RAND() LIMIT 1). Consider revising the repository method to ensure compatibility with JPA.

}

@Override
public List<CharacterDto> findByName(String name) {
return characterRepository.findByNameContainingIgnoreCase(name)
.stream()
.map(characterMapper::toDto)
.toList();
}
}
7 changes: 7 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
spring.datasource.url=jdbc:mysql://localhost:3306/rick_morty
spring.datasource.username=root
spring.datasource.password=123456789
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

server.port=8082
Loading