Skip to content

Commit

Permalink
Added solution to rick and morty API mate-academy#1
Browse files Browse the repository at this point in the history
  • Loading branch information
MilianCode committed May 27, 2024
1 parent c3bbe60 commit b1d7826
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 3 deletions.
63 changes: 61 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
Expand All @@ -41,7 +41,38 @@
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>

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

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

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
Expand All @@ -66,6 +97,34 @@
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
<sourceDirectories>src/main</sourceDirectories>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</path>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
Expand Down
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 MapStructConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package mate.academy.rickandmorty.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.service.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;

@Tag(name = "Rick and Morty characters",
description = "Endpoints for searching characters from Rick and Morty")
@RestController
@RequiredArgsConstructor
@RequestMapping("/characters")
public class CharacterController {
private final CharacterService characterService;

@Operation(summary = "Get character", description = "Get random character")
@GetMapping("/random")
public CharacterDto getRandomCharacter() {
return characterService.getRandomCharacter();
}

@Operation(summary = "Get characters by name",
description = "Get characters by name contains")
@GetMapping
public List<CharacterDto> findAllByNameContains(@RequestParam String name) {
return characterService.searchCharacters(name);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mate.academy.rickandmorty.dto.external;

public record CharacterMetadataDto(
Integer count,
Integer pages,
String next,
String prev
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.rickandmorty.dto.external;

import java.util.List;
import mate.academy.rickandmorty.dto.internal.CreateCharacterRequestDto;

public record CharacterResponseDataDto(
CharacterMetadataDto info,
List<CreateCharacterRequestDto> results
) {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.rickandmorty.dto.internal;

public record CharacterDto(
Long id,
String externalId,
String name,
String status,
String gender
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mate.academy.rickandmorty.dto.internal;

import com.fasterxml.jackson.annotation.JsonProperty;

public record CreateCharacterRequestDto(
@JsonProperty("id")
String externalId,
String name,
String status,
String gender
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mate.academy.rickandmorty.mapper;

import mate.academy.rickandmorty.config.MapStructConfig;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.dto.internal.CreateCharacterRequestDto;
import mate.academy.rickandmorty.model.CartoonCharacter;
import org.mapstruct.Mapper;

@Mapper(config = MapStructConfig.class)
public interface CharacterMapper {
CharacterDto toDto(CartoonCharacter character);

CartoonCharacter toModel(CreateCharacterRequestDto requestDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package mate.academy.rickandmorty.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
public class CartoonCharacter {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
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,15 @@
package mate.academy.rickandmorty.repository;

import java.util.List;
import mate.academy.rickandmorty.model.CartoonCharacter;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface CharacterRepository extends JpaRepository<CartoonCharacter, Long> {
List<CartoonCharacter> findAllByNameContainingIgnoreCase(String name);

@Query(value = "FROM CartoonCharacter ORDER BY RAND() LIMIT 1")
CartoonCharacter getRandom();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mate.academy.rickandmorty.service;

import java.util.List;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.dto.internal.CreateCharacterRequestDto;

public interface CharacterService {

List<CharacterDto> searchCharacters(String name);

CharacterDto getRandomCharacter();

CharacterDto save(CreateCharacterRequestDto requestDto);

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

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
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.external.CharacterResponseDataDto;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class CharactersClient {
private static final String BASE_URL = "https://rickandmortyapi.com/api/character";
private final ObjectMapper mapper;
private final CharacterService characterService;

@PostConstruct
public void dbInit() {
getCharactersInfo();
}

public void getCharactersInfo() {
HttpClient httpClient = HttpClient.newHttpClient();
String url = BASE_URL;
while (url != null) {
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(url))
.build();
try {
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString());
CharacterResponseDataDto characterResponseDataDto = mapper.readValue(
response.body(),
CharacterResponseDataDto.class);
characterResponseDataDto.results()
.stream().map(createCharacterRequestDto ->
characterService.save(createCharacterRequestDto))
.toList();
url = characterResponseDataDto.info().next();
} catch (IOException | InterruptedException e) {
throw new RuntimeException("Can't get data from API", e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package mate.academy.rickandmorty.service.impl;

import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.dto.internal.CreateCharacterRequestDto;
import mate.academy.rickandmorty.mapper.CharacterMapper;
import mate.academy.rickandmorty.model.CartoonCharacter;
import mate.academy.rickandmorty.repository.CharacterRepository;
import mate.academy.rickandmorty.service.CharacterService;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CharacterServiceImpl implements CharacterService {
private final CharacterMapper mapper;
private final CharacterRepository repository;

@Override
public List<CharacterDto> searchCharacters(String name) {
return repository.findAllByNameContainingIgnoreCase(name)
.stream()
.map(mapper::toDto)
.toList();
}

@Override
public CharacterDto getRandomCharacter() {
return mapper.toDto(repository.getRandom());
}

@Override
public CharacterDto save(CreateCharacterRequestDto requestDto) {
CartoonCharacter character = mapper.toModel(requestDto);
return mapper.toDto(repository.save(character));
}
}
6 changes: 6 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
spring.datasource.url=jdbc:mysql://localhost:3306/rickandmortydb
spring.datasource.username=root
spring.datasource.password=Matepass1!
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.h2.console.enabled=true

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ class ApplicationTests {
@Test
void contextLoads() {
}

}

0 comments on commit b1d7826

Please sign in to comment.