Skip to content
Merged
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
48 changes: 48 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM maven:3.9.11-amazoncorretto-21-alpine AS builder

# Set working directory
WORKDIR /app

COPY pom.xml ./
COPY lombok.config ./

# Set up Maven local repository for caching
ENV MAVEN_OPTS="-Dmaven.repo.local=/app/.m2/repository"

# Download dependencies (caching layer)
RUN mvn dependency:go-offline -B

# Copy source code
COPY src ./src

# Build the application (skip tests for faster build)
RUN mvn clean package -DskipTests -Dcheckstyle.skip=true

# Stage 2: Layers stage
FROM amazoncorretto:21.0.8-alpine AS layers
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
# Extract layers from the JAR for better caching in runtime
RUN java -Djarmode=layertools -jar app.jar extract

# Stage 3: Runtime stage
FROM amazoncorretto:21.0.8-alpine

# Set non-root user for security
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser

# Set working directory
WORKDIR /app

# Copy extracted layers from builder stage
COPY --from=layers /app/dependencies/ ./
COPY --from=layers /app/spring-boot-loader/ ./
COPY --from=layers /app/snapshot-dependencies/ ./
COPY --from=layers /app/application/ ./

# Expose the application port
EXPOSE 8080

# Entry point to run the application
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
13 changes: 13 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
filmorate:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
volumes:
- maven-repo:/app/.m2/repository
- ./db:/app/db

volumes:
maven-repo: {}
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,12 @@ public DirectorDto findById(@PathVariable @Positive long id) {

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Validated
public DirectorDto create(@RequestBody @Valid NewDirectorRequest request) {
log.trace("Create new director requested: {}", request);
return directorService.create(request);
}

@PutMapping
@Validated
public DirectorDto update(@RequestBody @Valid UpdateDirectorRequest request) {
log.trace("Update director requested: {}", request);
return directorService.update(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,12 @@ public void deleteById(@PathVariable @Positive long filmId) {

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Validated
public FilmDto create(@RequestBody @Valid NewFilmRequest request) {
log.trace("Create new film requested {}", request);
return filmService.create(request);
}

@PutMapping
@Validated
public FilmDto update(@RequestBody @Valid UpdateFilmRequest request) {
log.trace("Update film requested {}", request);
return filmService.update(request);
Expand All @@ -76,13 +74,8 @@ public Collection<FilmDto> searchFilms(@RequestParam @NotBlank String query,
@RequestParam(defaultValue = "title") String by) {
log.trace("Search films requested with query: {}, by: {}", query, by);

List<SearchFilmsBy> parsedInput = SearchFilmsBy.parseStr(by);

for (SearchFilmsBy searchFilmsBy : parsedInput) {
if (searchFilmsBy == null) {
throw new IllegalArgumentException("Unknown search params %s".formatted(by));
}
}
List<SearchFilmsBy> parsedInput = SearchFilmsBy.parseStrOrThrow(by, () ->
new IllegalArgumentException("Invalid search films requested: " + by));

return filmService.searchFilms(query, parsedInput);
}
Expand All @@ -106,10 +99,8 @@ public Collection<FilmDto> findPopular(@RequestParam(defaultValue = "10") @Posit
public Collection<FilmDto> findFilmsOfDirector(@PathVariable @Positive long directorId,
@RequestParam String sortBy) {

FilmsSortBy sortFilmsBy = FilmsSortBy.fromString(sortBy);
if (sortFilmsBy == null) {
throw new IllegalArgumentException("invalid sort by: %s".formatted(sortBy));
}
FilmsSortBy sortFilmsBy = FilmsSortBy.fromString(sortBy).orElseThrow(
() -> new IllegalArgumentException("invalid sort by: %s".formatted(sortBy)));

log.trace("Find films of director with id {} requested", directorId);
return filmService.findFilmsOfDirector(directorId, sortFilmsBy);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package ru.yandex.practicum.filmorate.controller;

import java.util.Optional;

public enum FilmsSortBy {
YEAR,
LIKES;

public static FilmsSortBy fromString(String sortBy) {
public static Optional<FilmsSortBy> fromString(String sortBy) {
return switch (sortBy.toLowerCase()) {
case "year" -> YEAR;
case "likes" -> LIKES;
default -> null;
case "year" -> Optional.of(YEAR);
case "likes" -> Optional.of(LIKES);
default -> Optional.empty();
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import lombok.Getter;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

@Getter
public enum SearchFilmsBy {
Expand All @@ -16,23 +19,24 @@ public enum SearchFilmsBy {
this.value = value;
}

public static List<SearchFilmsBy> parseStr(String value) {
public static <T extends Throwable> List<SearchFilmsBy> parseStrOrThrow(String value,
Supplier<T> exceptionSupplier) throws T {
String[] splittedStr = value.trim().toLowerCase().split(",");
List<SearchFilmsBy> parsedList = new ArrayList<>();

for (String str : splittedStr) {
SearchFilmsBy searchFilmsBy = SearchFilmsBy.fromString(str);
parsedList.add(searchFilmsBy);
parsedList.add(SearchFilmsBy.fromString(str)
.orElseThrow(exceptionSupplier));
}

return parsedList;
}

public static SearchFilmsBy fromString(String value) {
public static Optional<SearchFilmsBy> fromString(String value) {
return switch (value) {
case "title" -> SearchFilmsBy.TITLE;
case "director" -> SearchFilmsBy.DIRECTOR;
default -> null;
case "title" -> Optional.of(TITLE);
case "director" -> Optional.of(DIRECTOR);
default -> Optional.empty();
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,12 @@ public void deleteById(@PathVariable @Positive long userId) {

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@Validated
public UserDto create(@RequestBody @Valid NewUserRequest user) {
log.trace("create new user requested {}", user);
return userService.create(user);
}

@PutMapping
@Validated
public UserDto update(@RequestBody @Valid UpdateUserRequest user) {
log.trace("Update user requested {}", user);
return userService.update(user);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package ru.yandex.practicum.filmorate.dto.director;

import jakarta.validation.constraints.NotBlank;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;

@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class NewDirectorRequest {
@NotBlank
String name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package ru.yandex.practicum.filmorate.dto.event;

import lombok.Builder;
import lombok.Data;
import ru.yandex.practicum.filmorate.model.EventType;
import ru.yandex.practicum.filmorate.model.Operation;

@Data
@Builder
public class EventDto {
private Long timestamp;
private Long userId;
private EventType eventType;
private Operation operation;
private Long eventId;
private Long entityId;
public record EventDto(Long timestamp,
Long userId,
EventType eventType,
Operation operation,
Long eventId,
Long entityId) {

}
27 changes: 9 additions & 18 deletions src/main/java/ru/yandex/practicum/filmorate/dto/film/FilmDto.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
package ru.yandex.practicum.filmorate.dto.film;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.Singular;
import lombok.experimental.FieldDefaults;
import ru.yandex.practicum.filmorate.dto.director.DirectorDto;
import ru.yandex.practicum.filmorate.dto.genre.GenreDto;
import ru.yandex.practicum.filmorate.dto.mparating.MPARatingDto;

import java.time.LocalDate;
import java.util.List;

@Data
@Builder
@FieldDefaults(level = AccessLevel.PRIVATE)
public class FilmDto {
Long id;
String name;
String description;
LocalDate releaseDate;
Integer duration;
MPARatingDto mpa;
@Singular
List<GenreDto> genres;
List<DirectorDto> directors;
public record FilmDto(
Long id,
String name,
String description,
LocalDate releaseDate,
Integer duration,
MPARatingDto mpa,
List<GenreDto> genres,
List<DirectorDto> directors) {
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
package ru.yandex.practicum.filmorate.dto.review;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;

@Data
@Builder
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ReviewDto {
Long reviewId;
String content;
Boolean isPositive;
Long userId;
Long filmId;
Long useful;
public record ReviewDto(
Long reviewId,
String content,
Boolean isPositive,
Long userId,
Long filmId,
Long useful) {
}

22 changes: 6 additions & 16 deletions src/main/java/ru/yandex/practicum/filmorate/dto/user/UserDto.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
package ru.yandex.practicum.filmorate.dto.user;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;

import java.time.LocalDate;

@Data
@Builder
@FieldDefaults(level = AccessLevel.PRIVATE)
public class UserDto {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
long id;
String email;
String login;
String name;
LocalDate birthday;
public record UserDto(
long id,
String email,
String login,
String name,
LocalDate birthday) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
public class EventMapper {

public EventDto toEventDto(Event event) {
return EventDto.builder()
.timestamp(event.getTimestamp())
.userId(event.getUserId())
.eventType(event.getEventType())
.operation(event.getOperation())
.eventId(event.getEventId())
.entityId(event.getEntityId())
.build();
return new EventDto(
event.getTimestamp(),
event.getUserId(),
event.getEventType(),
event.getOperation(),
event.getEventId(),
event.getEntityId());
}
}

26 changes: 12 additions & 14 deletions src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,26 @@
import ru.yandex.practicum.filmorate.dto.film.FilmDto;
import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest;
import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest;
import ru.yandex.practicum.filmorate.dto.genre.GenreDto;
import ru.yandex.practicum.filmorate.model.Film;

@UtilityClass
public class FilmMapper {
public FilmDto toFilmDto(Film film) {
return FilmDto.builder()
.id(film.getId())
.description(film.getDescription())
.name(film.getName())
.releaseDate(film.getReleaseDate())
.duration(film.getDuration())
.mpa(MPARatingMapper.toMPARatingDto(film.getMpaRating()))
.genres(film.getGenres()
return new FilmDto(
film.getId(),
film.getName(),
film.getDescription(),
film.getReleaseDate(),
film.getDuration(),
MPARatingMapper.toMPARatingDto(film.getMpaRating()),
film.getGenres()
.stream()
.map(genre -> new GenreDto(genre.getId(), genre.getName()))
.toList())
.directors(film.getDirectors()
.map(GenreMapper::toGenreDto)
.toList(),
film.getDirectors()
.stream()
.map(DirectorMapper::toDirectorDto)
.toList())
.build();
.toList());
}

public Film toFilm(NewFilmRequest request) {
Expand Down
Loading