chore(release): first stable release #45

Merged
devitq merged 134 commits from develop into main 2026-05-23 07:28:37 +00:00
9 changed files with 144 additions and 27 deletions
Showing only changes of commit 649978f791 - Show all commits
@@ -61,6 +61,15 @@ class FilmRepository(
filmRowMapper,
)
override fun findByTitle(title: String): Film? {
val films = jdbc.query(
"SELECT id, title, description FROM films WHERE title = ?",
filmRowMapper,
title
)
return films.firstOrNull()
}
override fun deleteById(id: UUID) {
jdbc.update("DELETE FROM films WHERE id = ?", id)
}
@@ -8,13 +8,18 @@ import com.project.movienight.application.ports.input.CreateFilmUseCase
import com.project.movienight.application.ports.input.DeleteFilmUseCase
import com.project.movienight.application.ports.input.EditFilmCommand
import com.project.movienight.application.ports.input.EditFilmUseCase
import com.project.movienight.application.ports.input.GetAllFilmsUseCase
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
import com.project.movienight.application.ports.input.SearchFilmByTitleUseCase
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.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import java.util.UUID
@@ -25,6 +30,9 @@ class FilmController(
private val createFilmUseCase: CreateFilmUseCase,
private val editFilmUseCase: EditFilmUseCase,
private val deleteFilmUseCase: DeleteFilmUseCase,
private val getFilmByIdUseCase: GetFilmByIdUseCase,
private val getAllFilmsUseCase: GetAllFilmsUseCase,
private val searchFilmByTitleUseCase: SearchFilmByTitleUseCase,
) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@@ -48,11 +56,10 @@ class FilmController(
FilmResponse.fromDomain(
editFilmUseCase.edit(
id = id,
command =
EditFilmCommand(
title = request.title,
description = request.description,
),
command = EditFilmCommand(
title = request.title,
description = request.description,
),
),
)
@@ -61,4 +68,20 @@ class FilmController(
fun delete(
@PathVariable id: UUID,
) = deleteFilmUseCase.delete(id)
@GetMapping("/{id}")
fun getById(
@PathVariable id: UUID,
): FilmResponse =
FilmResponse.fromDomain(getFilmByIdUseCase.getById(id))
@GetMapping("/search")
fun searchByTitle(
@RequestParam title: String,
): FilmResponse? =
searchFilmByTitleUseCase.searchByTitle(title)?.let { FilmResponse.fromDomain(it) }
@GetMapping
fun getAll(): List<FilmResponse> =
getAllFilmsUseCase.getAll().map { FilmResponse.fromDomain(it) }
}
@@ -2,10 +2,13 @@ package com.project.movienight.adapters.web
import com.project.movienight.adapters.web.dto.request.CreateFilmLibraryRequest
import com.project.movienight.adapters.web.dto.response.FilmLibraryResponse
import com.project.movienight.adapters.web.dto.response.FilmResponse
import com.project.movienight.application.ports.input.AddFilmToLibraryCommand
import com.project.movienight.application.ports.input.AddFilmToLibraryUseCase
import com.project.movienight.application.ports.input.CreateFilmLibraryCommand
import com.project.movienight.application.ports.input.CreateFilmLibraryUseCase
import com.project.movienight.application.ports.input.GetAllFilmsUseCase
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
import com.project.movienight.application.ports.input.GetFilmLibraryQuery
import com.project.movienight.application.ports.input.GetFilmLibraryUseCase
import com.project.movienight.application.ports.input.RemoveFilmFromLibraryCommand
@@ -28,6 +31,8 @@ class FilmLibraryController(
private val addFilmToLibraryUseCase: AddFilmToLibraryUseCase,
private val removeFilmFromLibraryUseCase: RemoveFilmFromLibraryUseCase,
private val getFilmLibraryUseCase: GetFilmLibraryUseCase,
private val getFilmByIdUseCase: GetFilmByIdUseCase,
private val getAllFilmsUseCase: GetAllFilmsUseCase,
) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@@ -54,6 +59,19 @@ class FilmLibraryController(
),
)
@GetMapping("/films")
fun getAllFilmsInLibrary(
@PathVariable userId: UUID,
): List<FilmResponse> {
val library = getFilmLibraryUseCase.getLibrary(
GetFilmLibraryQuery(userId = userId)
)
val film = getFilmByIdUseCase.getById(library.filmId)
return listOf(FilmResponse.fromDomain(film))
}
@PostMapping("/films/{filmId}")
@ResponseStatus(HttpStatus.CREATED)
fun addFilm(
@@ -80,4 +98,19 @@ class FilmLibraryController(
filmId = filmId,
),
)
@GetMapping("/available-films")
fun getAvailableFilms(
@PathVariable userId: UUID,
): List<FilmResponse> {
val userLibrary = getFilmLibraryUseCase.getLibrary(
GetFilmLibraryQuery(userId = userId)
)
val allFilms = getAllFilmsUseCase.getAll()
val availableFilms = allFilms.filter { it.id != userLibrary.filmId }
return availableFilms.map { FilmResponse.fromDomain(it) }
}
}
@@ -8,8 +8,11 @@ import com.project.movienight.application.ports.input.CreateUserUseCase
import com.project.movienight.application.ports.input.DeleteUserUseCase
import com.project.movienight.application.ports.input.EditUserCommand
import com.project.movienight.application.ports.input.EditUserUseCase
import com.project.movienight.application.ports.input.GetAllUsersUseCase
import com.project.movienight.application.ports.input.GetUserByIdUseCase
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.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
@@ -25,6 +28,8 @@ class UserController(
private val createUserUseCase: CreateUserUseCase,
private val editUserUseCase: EditUserUseCase,
private val deleteUserUseCase: DeleteUserUseCase,
private val getUserByIdUseCase: GetUserByIdUseCase,
private val getAllUsersUseCase: GetAllUsersUseCase,
) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
@@ -40,6 +45,16 @@ class UserController(
),
)
@GetMapping
fun getAll(): List<UserResponse> =
getAllUsersUseCase.getAll().map { UserResponse.fromDomain(it) }
@GetMapping("/{id}")
fun getById(
@PathVariable id: UUID,
): UserResponse =
UserResponse.fromDomain(getUserByIdUseCase.getById(id))
@PatchMapping("/{id}")
fun edit(
@PathVariable id: UUID,
@@ -48,10 +63,9 @@ class UserController(
UserResponse.fromDomain(
editUserUseCase.edit(
id = id,
command =
EditUserCommand(
name = request.name,
),
command = EditUserCommand(
name = request.name,
),
),
)
@@ -27,3 +27,15 @@ data class EditFilmCommand(
interface DeleteFilmUseCase {
fun delete(id: UUID)
}
interface GetFilmByIdUseCase {
fun getById(id: UUID): Film
}
interface GetAllFilmsUseCase {
fun getAll(): List<Film>
}
interface SearchFilmByTitleUseCase {
fun searchByTitle(title: String): Film?
}
@@ -26,3 +26,11 @@ data class EditUserCommand(
interface DeleteUserUseCase {
fun delete(id: UUID)
}
interface GetUserByIdUseCase {
fun getById(id: UUID): User
}
interface GetAllUsersUseCase {
fun getAll(): List<User>
}
@@ -10,5 +10,7 @@ interface FilmRepositoryPort {
fun findAll(): List<Film>
fun findByTitle(title: String): Film?
fun deleteById(id: UUID)
}
@@ -5,6 +5,9 @@ import com.project.movienight.application.ports.input.CreateFilmUseCase
import com.project.movienight.application.ports.input.DeleteFilmUseCase
import com.project.movienight.application.ports.input.EditFilmCommand
import com.project.movienight.application.ports.input.EditFilmUseCase
import com.project.movienight.application.ports.input.GetAllFilmsUseCase
import com.project.movienight.application.ports.input.GetFilmByIdUseCase
import com.project.movienight.application.ports.input.SearchFilmByTitleUseCase
import com.project.movienight.application.ports.output.FilmRepositoryPort
import com.project.movienight.application.ports.output.IdGenerator
import com.project.movienight.config.FilmServiceProperties
@@ -21,7 +24,11 @@ class FilmService(
private val filmConfig: FilmServiceProperties,
) : CreateFilmUseCase,
EditFilmUseCase,
DeleteFilmUseCase {
DeleteFilmUseCase,
GetFilmByIdUseCase,
GetAllFilmsUseCase,
SearchFilmByTitleUseCase {
override fun create(command: CreateFilmCommand): Film {
if (filmConfig.isBlocked(command.title)) {
throw BlockedValueException(target = "Film", field = "title")
@@ -30,19 +37,15 @@ class FilmService(
throw BlockedValueException(target = "Film", field = "description")
}
val film =
Film(
id = idGenerator.generateId(),
title = command.title,
description = command.description,
)
val film = Film(
id = idGenerator.generateId(),
title = command.title,
description = command.description,
)
return filmRepository.save(film)
}
override fun edit(
id: UUID,
command: EditFilmCommand,
): Film {
override fun edit(id: UUID, command: EditFilmCommand): Film {
if (filmConfig.isBlocked(command.title)) {
throw BlockedValueException(target = "Film", field = "title")
}
@@ -51,15 +54,20 @@ class FilmService(
}
var film = filmRepository.findById(id) ?: throw EntityNotFoundException(entity = "Film", id = id.toString())
film = film.copy(title = command.title, description = command.description)
return filmRepository.save(film)
}
override fun delete(id: UUID) {
filmRepository.findById(id) ?: throw EntityNotFoundException(entity = "Film", id = id.toString())
filmRepository.deleteById(id)
}
override fun getById(id: UUID): Film {
return filmRepository.findById(id) ?: throw EntityNotFoundException(entity = "Film", id = id.toString())
}
override fun getAll(): List<Film> = filmRepository.findAll()
override fun searchByTitle(title: String): Film? = filmRepository.findByTitle(title)
}
@@ -5,6 +5,8 @@ import com.project.movienight.application.ports.input.CreateUserUseCase
import com.project.movienight.application.ports.input.DeleteUserUseCase
import com.project.movienight.application.ports.input.EditUserCommand
import com.project.movienight.application.ports.input.EditUserUseCase
import com.project.movienight.application.ports.input.GetAllUsersUseCase
import com.project.movienight.application.ports.input.GetUserByIdUseCase
import com.project.movienight.application.ports.output.IdGenerator
import com.project.movienight.application.ports.output.UserRepositoryPort
import com.project.movienight.config.UserServiceProperties
@@ -21,7 +23,10 @@ class UserService(
private val userConfig: UserServiceProperties,
) : CreateUserUseCase,
EditUserUseCase,
DeleteUserUseCase {
DeleteUserUseCase,
GetUserByIdUseCase,
GetAllUsersUseCase {
override fun create(command: CreateUserCommand): User {
if (userConfig.isBlocked(command.name)) {
throw BlockedValueException(target = "User", field = "name")
@@ -47,15 +52,18 @@ class UserService(
}
var user = userRepository.findById(id) ?: throw EntityNotFoundException(entity = "User", id = id.toString())
user = user.copy(name = command.name)
return userRepository.save(user)
}
override fun delete(id: UUID) {
userRepository.findById(id) ?: throw EntityNotFoundException(entity = "User", id = id.toString())
userRepository.deleteById(id)
}
override fun getById(id: UUID): User {
return userRepository.findById(id) ?: throw EntityNotFoundException(entity = "User", id = id.toString())
}
override fun getAll(): List<User> = userRepository.findAll()
}