Skip to content

Commit

Permalink
done
Browse files Browse the repository at this point in the history
  • Loading branch information
ChabVlad committed Sep 15, 2024
1 parent c3bbe60 commit c2fd61f
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package mate.academy.rickandmorty.config.mapper;

import mate.academy.rickandmorty.dto.external.CharacterResponseDataDto;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.model.CharacterRickAndMorty;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.NullValueCheckStrategy;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING,
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
implementationPackage = "<PACKAGE_NAME>.impl"
)
public interface CharacterMapper {
@Mapping(source = "id", target = "externalId")
CharacterRickAndMorty toCharacterModel(CharacterResponseDataDto responseDto);

CharacterDto toDto(CharacterRickAndMorty characterRickAndMorty);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mate.academy.rickandmorty.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
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.RickAndMortyClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/character")
@Tag(
name = "Rick and Morty controller",
description = "endpoints for getting characters")
public class RickAndMortyController {
private final RickAndMortyClient client;

@GetMapping("/random")
@Operation(
summary = "get random character",
description = "get random character",
responses = {@ApiResponse(
responseCode = "200",
description = "success"
)}
)
public CharacterDto getRandomCharacter() {
return client.getRandomCharacter();
}

@GetMapping("/by-name")
@Operation(
summary = "get all characters containing name",
description = "get all characters containing name",
responses = {@ApiResponse(
responseCode = "200",
description = "success"
)}
)
public List<CharacterDto> getCharacterByName(String name) {
return client.findByPartOfName(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.rickandmorty.dto.external;

import lombok.Data;

@Data
public class ApiInfoDto {
private Long count;
private Long pages;
private String next;
private String prev;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.rickandmorty.dto.external;

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

@Data
public class ApiResponseDto {
private ApiInfoDto info;
private List<CharacterResponseDataDto> results;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mate.academy.rickandmorty.dto.external;

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

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class CharacterResponseDataDto {
private Long id;
private String name;
private String gender;
private String status;

}
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 CharacterDto {
private Long id;
private Long externalId;
private String name;
private String gender;
private String status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mate.academy.rickandmorty.exception;

public class ApiResponseParsingException extends RuntimeException {
public ApiResponseParsingException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package mate.academy.rickandmorty.model;

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

@Entity
@Table(name = "characters")
@Getter
@Setter
public class CharacterRickAndMorty {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private Long externalId;

@Column(nullable = false)
private String name;

@Column(nullable = false)
private String gender;

@Column(nullable = false)
private String status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mate.academy.rickandmorty.repository;

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

@Repository
public interface CharacterRepository extends JpaRepository<CharacterRickAndMorty, Long> {
public long count();

public List<CharacterRickAndMorty> findByNameContaining(String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package mate.academy.rickandmorty.service;

public interface PullDataService {
void pullAllFromExternalApiToDb();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mate.academy.rickandmorty.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.config.mapper.CharacterMapper;
import mate.academy.rickandmorty.dto.external.ApiResponseDto;
import mate.academy.rickandmorty.dto.external.CharacterResponseDataDto;
import mate.academy.rickandmorty.exception.ApiResponseParsingException;
import mate.academy.rickandmorty.model.CharacterRickAndMorty;
import mate.academy.rickandmorty.repository.CharacterRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@RequiredArgsConstructor
@Service
public class PullDataServiceImpl implements PullDataService {
@Value("${external.api.url}")
private String baseUrl;
private final CharacterRepository characterRepository;
private final CharacterMapper mapper;
private final ObjectMapper objectMapper = new ObjectMapper();

@Override
@PostConstruct
public void pullAllFromExternalApiToDb() {
String nextPageUrl = baseUrl;
while (nextPageUrl != null) {
saveAllCharactersFromPage(nextPageUrl);
ApiResponseDto responseDto = getApiResponseDto(nextPageUrl);
nextPageUrl = responseDto.getInfo().getNext();
}

}

private ApiResponseDto getApiResponseDto(String url) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
try {
return objectMapper.readValue(response.getBody(), ApiResponseDto.class);
} catch (JsonProcessingException e) {
throw new ApiResponseParsingException("Failed to parse API response", e);
}
}

private void saveAllCharactersFromPage(String url) {
ApiResponseDto apiResponseDto = getApiResponseDto(url);
List<CharacterResponseDataDto> characterResponseDataDtos = apiResponseDto.getResults();
List<CharacterRickAndMorty> characterRicksAndMorties = characterResponseDataDtos
.stream()
.map(mapper::toCharacterModel)
.toList();

characterRepository.saveAll(characterRicksAndMorties);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.rickandmorty.service;

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

public interface RickAndMortyClient {
CharacterDto getRandomCharacter();

List<CharacterDto> findByPartOfName(String partOfName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package mate.academy.rickandmorty.service;

import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Random;
import lombok.RequiredArgsConstructor;
import mate.academy.rickandmorty.config.mapper.CharacterMapper;
import mate.academy.rickandmorty.dto.internal.CharacterDto;
import mate.academy.rickandmorty.model.CharacterRickAndMorty;
import mate.academy.rickandmorty.repository.CharacterRepository;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class RickAndMortyClientImpl implements RickAndMortyClient {
private final CharacterRepository characterRepository;
private final CharacterMapper characterMapper;

public CharacterDto getRandomCharacter() {
long totalCharacters = characterRepository.count();
Random random = new Random();
Long randomId = random.nextLong(totalCharacters);
CharacterRickAndMorty character = characterRepository.findById(randomId).orElseThrow(
() -> new EntityNotFoundException("Character not found"));
return characterMapper.toDto(character);

}

@Override
public List<CharacterDto> findByPartOfName(String partOfName) {
List<CharacterRickAndMorty> listOfCharacters =
characterRepository.findByNameContaining(partOfName);
return listOfCharacters
.stream()
.map(characterMapper::toDto)
.toList();
}
}
14 changes: 14 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
spring.application.name=Rick-and-Morty
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rick_and_morty?createDatabaseIfNotExist=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=pass

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.open-in-view=false

external.api.url=https://rickandmortyapi.com/api/character

springdoc.swagger-ui.syntaxHighlight.theme=nord
springdoc.swagger-ui.path=/swagger-ui.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
databaseChangeLog:
- changeSet:
id: create-characters-table
author: vlad
changes:
- createTable:
tableName: characters
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: external_id
type: BIGINT
constraints:
nullable: false
- column:
name: name
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: status
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: gender
type: VARCHAR(255)
constraints:
nullable: false

3 changes: 3 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
databaseChangeLog:
- include:
file: db/changelog/changes/01-create-characters-table.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class ApplicationTests {

@Test
void contextLoads() {

}

}

0 comments on commit c2fd61f

Please sign in to comment.