From 852ae46b78e4d0023aefba95bbfad814a71fea80 Mon Sep 17 00:00:00 2001 From: asavershin Date: Thu, 4 Apr 2024 10:13:45 +0300 Subject: [PATCH 1/8] Format to sun code style --- api/pom.xml | 1 + .../com/github/asavershin/api/ApiMain.java | 13 ++- .../in/services/image/ImageService.java | 22 ++++ .../services/image/impl/ImageServiceImpl.java | 82 ++++++++++--- .../in/services/image/impl/package-info.java | 5 + .../in/services/image/package-info.java | 5 + .../services/user/ApplicationCredentials.java | 21 +++- .../in/services/user/GetNewCredentials.java | 6 + .../GetNewCredentialsUsingRefreshToken.java | 6 + .../in/services/user/JwtService.java | 22 +++- .../user/impl/GetNewCredentialsImpl.java | 28 ++++- ...etNewCredentialsUsingRefreshTokenImpl.java | 32 ++++- .../in/services/user/impl/JwtServiceIml.java | 30 +++-- .../in/services/user/impl/package-info.java | 5 + .../in/services/user/package-info.java | 5 + .../api/application/out/CacheRepository.java | 23 +++- .../api/application/out/FileService.java | 17 +++ .../api/application/out/MinioService.java | 2 +- .../api/application/out/TokenRepository.java | 33 ++++++ .../api/application/out/package-info.java | 5 + .../api/common/NotFoundException.java | 7 +- .../asavershin/api/common/Validator.java | 109 ++++++++++++++++-- .../api/common/annotations/Command.java | 6 +- .../api/common/annotations/DomainService.java | 6 +- .../api/common/annotations/Query.java | 6 +- .../api/common/annotations/package-info.java | 5 + .../asavershin/api/common/package-info.java | 7 ++ .../api/config/AnnotationsConfig.java | 9 +- .../asavershin/api/config/AuthConfig.java | 10 +- .../asavershin/api/config/MinIOConfig.java | 17 ++- .../asavershin/api/config/RedisConfig.java | 20 +++- .../asavershin/api/config/package-info.java | 5 + .../api/config/properties/JwtProperties.java | 22 +++- .../config/properties/MinIOProperties.java | 19 ++- .../api/config/properties/UserProperties.java | 18 +++ .../api/config/properties/package-info.java | 6 + .../asavershin/api/domain/IsEntityFound.java | 5 +- .../api/domain/PartOfResources.java | 37 +++++- .../domain/ResourceOwnershipException.java | 15 ++- .../api/domain/image/DeleteImageOfUser.java | 6 + .../api/domain/image/GetImageOfUser.java | 7 ++ .../api/domain/image/GetPartImagesOfUser.java | 9 ++ .../asavershin/api/domain/image/Image.java | 67 ++++++++--- .../api/domain/image/ImageExtension.java | 55 +++++++-- .../asavershin/api/domain/image/ImageId.java | 17 ++- .../domain/image/ImageNameWithExtension.java | 67 ++++++++--- .../api/domain/image/ImageRepository.java | 29 ++++- .../asavershin/api/domain/image/MetaData.java | 43 +++++++ .../asavershin/api/domain/image/MetaInfo.java | 29 ----- .../api/domain/image/StoreImageOfUser.java | 5 + .../image/impl/DeleteImageOfUserImpl.java | 19 ++- .../domain/image/impl/GetImageOfUserImpl.java | 19 ++- .../image/impl/GetPartImagesOfUserImpl.java | 9 +- .../image/impl/StoreImageOfUserImpl.java | 9 +- .../api/domain/image/impl/package-info.java | 7 ++ .../api/domain/image/package-info.java | 5 + .../asavershin/api/domain/package-info.java | 8 ++ .../api/domain/user/AuthException.java | 16 ++- .../api/domain/user/AuthenticatedUser.java | 48 ++++++-- .../user/AuthenticatedUserRepository.java | 11 +- .../api/domain/user/Credentials.java | 36 ++++-- .../asavershin/api/domain/user/FullName.java | 28 ++++- .../GetPartOfImagesForAuthenticatedUser.java | 17 ++- .../api/domain/user/RegisterNewUser.java | 12 ++ .../api/domain/user/TryToLogin.java | 14 +++ .../asavershin/api/domain/user/User.java | 61 +++++++--- .../asavershin/api/domain/user/UserId.java | 25 +++- .../api/domain/user/UserRepository.java | 22 +++- ...tPartOfImagesForAuthenticatedUserImpl.java | 20 +++- .../domain/user/impl/RegisterNewUserImpl.java | 27 ++++- .../api/domain/user/impl/TryToLoginImpl.java | 23 +++- .../api/domain/user/impl/package-info.java | 7 ++ .../api/domain/user/package-info.java | 6 + .../controllers/AdviceController.java | 78 ++++++++++--- .../controllers/AuthController.java | 47 ++++++-- .../controllers/ImageController.java | 91 ++++++++++++--- .../controllers/dto/ExceptionBody.java | 18 ++- .../controllers/dto/UISuccessContainer.java | 6 + .../dto/image/GetImagesResponse.java | 27 +++-- .../controllers/dto/image/ImageResponse.java | 35 ++++-- .../dto/image/UploadImageResponse.java | 13 ++- .../controllers/dto/image/package-info.java | 5 + .../controllers/dto/package-info.java | 5 + .../dto/user/UserLoginRequest.java | 19 ++- .../dto/user/UserRegistrationRequest.java | 34 +++++- .../controllers/dto/user/package-info.java | 5 + .../controllers/controllers/package-info.java | 4 + .../in/security/CustomUserDetails.java | 3 +- .../in/security/JwtAuthenticationFilter.java | 44 ++++--- .../in/security/LogautHandlerImpl.java | 29 +++-- .../in/security/SecurityConfiguration.java | 50 ++++++-- .../in/security/UserDetailsServiceImpl.java | 24 +++- .../in/security/package-info.java | 5 + .../AuthenticatedUserRepositoryImpl.java | 22 +++- .../out/persistence/CacheRepositoryIml.java | 28 ----- .../out/persistence/ImageRepositoryImpl.java | 61 +++++++--- .../out/persistence/TokenRepositoryIml.java | 40 ------- .../out/persistence/UserRepositoryImpl.java | 19 ++- .../out/persistence/package-info.java | 5 + .../out/storage/CacheRepositoryIml.java | 45 ++++++++ .../out/storage/FileException.java | 7 +- .../out/storage/MinioServiceIml.java | 56 ++++++--- .../out/storage/TokenRepositoryIml.java | 67 +++++++++++ .../out/storage/package-info.java | 5 + .../github/asavershin/api/package-info.java | 6 + .../asavershin/api/common/ImageHelper.java | 10 +- .../asavershin/api/domaintest/ImageTest.java | 14 +-- .../api/integrations/ImageLogicTest.java | 6 + 108 files changed, 1949 insertions(+), 459 deletions(-) create mode 100644 api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/application/in/services/image/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/application/in/services/user/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/application/out/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/common/annotations/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/common/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/config/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/config/properties/UserProperties.java create mode 100644 api/src/main/java/com/github/asavershin/api/config/properties/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/image/MetaData.java delete mode 100644 api/src/main/java/com/github/asavershin/api/domain/image/MetaInfo.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/image/impl/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/image/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/user/impl/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/domain/user/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/in/security/package-info.java delete mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/CacheRepositoryIml.java delete mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/TokenRepositoryIml.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/CacheRepositoryIml.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/TokenRepositoryIml.java create mode 100644 api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/package-info.java create mode 100644 api/src/main/java/com/github/asavershin/api/package-info.java diff --git a/api/pom.xml b/api/pom.xml index e145ce1..38e9914 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -256,4 +256,5 @@ + \ No newline at end of file diff --git a/api/src/main/java/com/github/asavershin/api/ApiMain.java b/api/src/main/java/com/github/asavershin/api/ApiMain.java index 5c2aba3..0ed4b22 100644 --- a/api/src/main/java/com/github/asavershin/api/ApiMain.java +++ b/api/src/main/java/com/github/asavershin/api/ApiMain.java @@ -1,11 +1,22 @@ package com.github.asavershin.api; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + @SpringBootApplication public class ApiMain { - public static void main(String[] args) { + /** + * The main method that starts the Spring Boot application. + * + * @param args the command-line arguments + */ + public static void main(final String[] args) { SpringApplication.run(ApiMain.class, args); } + + private void foo() { + throw new UnsupportedOperationException(); + } } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/image/ImageService.java b/api/src/main/java/com/github/asavershin/api/application/in/services/image/ImageService.java index de452c7..d2c4f5d 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/image/ImageService.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/image/ImageService.java @@ -5,7 +5,29 @@ import org.springframework.web.multipart.MultipartFile; public interface ImageService { + /** + * Stores an image for a given user. + * + * @param userId the user who owns the image + * @param multipartFile the image file to be stored + * @return the unique identifier of the stored image + */ ImageId storeImage(UserId userId, MultipartFile multipartFile); + /** + * Deletes an image by its unique identifier. Validate that + * image belongs to user. + * + * @param userId the user who owns the image + * @param imageId the unique identifier of the image to be deleted + */ void deleteImageByImageId(UserId userId, ImageId imageId); + /** + * Downloads an image by its unique identifier. Validate that + * image belongs to user. + * + * @param imageId the unique identifier of the image to be downloaded + * @param userId the user who owns the image + * @return the byte array representation of the image + */ byte[] downloadImage(ImageId imageId, UserId userId); } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java index 6f35ae9..0dcfe82 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java @@ -2,39 +2,71 @@ import com.github.asavershin.api.application.in.services.image.ImageService; import com.github.asavershin.api.application.out.MinioService; -import com.github.asavershin.api.common.Validator; -import com.github.asavershin.api.domain.image.*; +import com.github.asavershin.api.domain.image.DeleteImageOfUser; +import com.github.asavershin.api.domain.image.GetImageOfUser; +import com.github.asavershin.api.domain.image.Image; +import com.github.asavershin.api.domain.image.ImageId; +import com.github.asavershin.api.domain.image.ImageNameWithExtension; +import com.github.asavershin.api.domain.image.MetaData; +import com.github.asavershin.api.domain.image.StoreImageOfUser; import com.github.asavershin.api.domain.user.UserId; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.util.Arrays; +import java.io.Serializable; import java.util.List; -import java.util.Objects; import java.util.UUID; @Service @RequiredArgsConstructor -@Slf4j -public class ImageServiceImpl implements ImageService { +public class ImageServiceImpl implements ImageService, Serializable { + /** + * The MinioService is used to interact with the Minio storage service. + */ private final MinioService minioService; + + /** + * The GetImageOfUser service is used to retrieve an image + * by its ID and user ID. + */ private final GetImageOfUser getImageOfUser; + + /** + * The DeleteImageOfUser service is used to delete an image + * by its ID and user ID. + */ private final DeleteImageOfUser deleteImageOfUser; + + /** + * The StoreImageOfUser service is used to store an image + * with its metadata and associate it with a user. + */ private final StoreImageOfUser storeImageOfUser; + + /** + * Method not marked as final to allow Spring + * to create a proxy first for transactional purposes. + * @param userId the user who owns the image + * @param multipartFile the image file to be stored + * @return ID of new stored image + */ @Override @Transactional - public ImageId storeImage(UserId userId, MultipartFile multipartFile) { - log.info("Store image: {}", multipartFile.getOriginalFilename()); - var metaInfo = new MetaInfo( + public ImageId storeImage(final UserId userId, + final MultipartFile multipartFile) { + var metaInfo = new MetaData( ImageNameWithExtension - .fromOriginalFileName(multipartFile.getOriginalFilename()), + .fromOriginalFileName( + multipartFile.getOriginalFilename() + ), multipartFile.getSize() ); - log.info("Store image: check on ex"); - var imageId = new ImageId(UUID.fromString(minioService.saveFile(multipartFile))); + // TODO Why minio service store before postgreSQL? + var imageId = new ImageId( + UUID.fromString(minioService.saveFile(multipartFile)) + ); storeImageOfUser.storeImageOfUser( new Image( imageId, @@ -45,17 +77,35 @@ public ImageId storeImage(UserId userId, MultipartFile multipartFile) { return imageId; } + /** + * Method not marked as final to allow Spring + * * to create a proxy first for transactional purposes. + * @param userId the user who owns the image + * @param imageId the unique identifier of the image to be deleted + */ @Override @Transactional - public void deleteImageByImageId(UserId userId, ImageId imageId) { + public void deleteImageByImageId(final UserId userId, + final ImageId imageId) { deleteImageOfUser.removeImageOfUser(imageId, userId); minioService.deleteFiles(List.of(imageId.value().toString())); } + /** + * Method not marked as final to allow Spring make CGLIB proxy. + * @param imageId the unique identifier of the image to be downloaded + * @param userId the user who owns the image + * @return bytes of the image + */ @Override - public byte[] downloadImage(ImageId imageId, UserId userId) { + public byte[] downloadImage(final ImageId imageId, + final UserId userId) { return minioService.getFile( - getImageOfUser.getImageOfUser(userId, imageId).imageId().value().toString() + getImageOfUser.getImageOfUser( + userId, + imageId + ) + .imageId().value().toString() ); } } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/package-info.java b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/package-info.java new file mode 100644 index 0000000..98dee3f --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains implementation application services for images. + * @author asavershin + */ +package com.github.asavershin.api.application.in.services.image.impl; diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/image/package-info.java b/api/src/main/java/com/github/asavershin/api/application/in/services/image/package-info.java new file mode 100644 index 0000000..9480e7d --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/image/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains application services for images. + * @author asavershin + */ +package com.github.asavershin.api.application.in.services.image; diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/ApplicationCredentials.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/ApplicationCredentials.java index e8e288a..8ab9109 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/ApplicationCredentials.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/ApplicationCredentials.java @@ -4,11 +4,26 @@ @Getter public class ApplicationCredentials { + /** + * The access token for user. + */ private final String accessToken; + + /** + * The refresh token for user. + */ private final String refreshToken; - public ApplicationCredentials(String accessToken, String refreshToken) { - this.accessToken = accessToken; - this.refreshToken = refreshToken; + /** + * Constructs an instance of {@link ApplicationCredentials} + * with the provided access token and refresh token. + * + * @param aAccessToken The access token for user. + * @param aRefreshToken The refresh token for user. + */ + public ApplicationCredentials(final String aAccessToken, + final String aRefreshToken) { + this.accessToken = aAccessToken; + this.refreshToken = aRefreshToken; } } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentials.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentials.java index 7765672..cf788e9 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentials.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentials.java @@ -4,5 +4,11 @@ @FunctionalInterface public interface GetNewCredentials { + /** + * Generates new credentials for the user based on the provided credentials. + * + * @param credentials The current credentials of the user. + * @return The new credentials for the user. + */ ApplicationCredentials get(Credentials credentials); } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentialsUsingRefreshToken.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentialsUsingRefreshToken.java index f0ed0cc..1cdda6b 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentialsUsingRefreshToken.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/GetNewCredentialsUsingRefreshToken.java @@ -4,5 +4,11 @@ @FunctionalInterface public interface GetNewCredentialsUsingRefreshToken { + /** + * Generates new credentials using the provided refresh token. + * + * @param credentials The credentials that contain the refresh token. + * @return The new credentials generated using the refresh token. + */ ApplicationCredentials get(Credentials credentials); } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/JwtService.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/JwtService.java index 02e3646..ee436d2 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/JwtService.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/JwtService.java @@ -4,11 +4,27 @@ public interface JwtService { + /** + * Generates an access token for the provided credentials. + * + * @param credentials The credentials used to generate the access token. + * @return A string representing the generated access token. + */ String generateAccessToken(Credentials credentials); - String generateRefreshToken( - Credentials credentials - ); + /** + * Generates a refresh token for the provided credentials. + * + * @param credentials The credentials used to generate the refresh token. + * @return A string representing the generated refresh token. + */ + String generateRefreshToken(Credentials credentials); + /** + * Extracts the subject from the provided JWT. + * + * @param jwt The JWT from which the subject should be extracted. + * @return A string representing the extracted subject. + */ String extractSub(String jwt); } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsImpl.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsImpl.java index 75a5838..71b306f 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsImpl.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsImpl.java @@ -15,20 +15,38 @@ @RequiredArgsConstructor @Slf4j public class GetNewCredentialsImpl implements GetNewCredentials { + /** + * Dependency for get,save,delete tokens in redis. + */ private final TokenRepository tokenRepository; + /** + * Domain service that allow user try to log in. + */ private final TryToLogin tryToLogin; + /** + * Application service that allow to do some manipulations with JWT tokens. + */ private final JwtService jwtService; + /** + * Property that contains secret and access, refresh expirations. + */ private final JwtProperties jwtProperties; @Override - public ApplicationCredentials get(Credentials credentials) { + public final ApplicationCredentials get(final Credentials credentials) { var authenticatedUser = tryToLogin.login(credentials); - var accessToken = jwtService.generateAccessToken(authenticatedUser.userCredentials()); - var refreshToken = jwtService.generateRefreshToken(authenticatedUser.userCredentials()); + var accessToken = jwtService + .generateAccessToken(authenticatedUser.userCredentials()); + var refreshToken = jwtService + .generateRefreshToken(authenticatedUser.userCredentials()); var email = authenticatedUser.userCredentials().email(); tokenRepository.deleteAllTokensByUserEmail(email); - tokenRepository.saveRefreshToken(email, refreshToken, jwtProperties.getRefreshExpiration()); - tokenRepository.saveAccessToken(email, accessToken, jwtProperties.getAccessExpiration()); + tokenRepository.saveRefreshToken(email, + refreshToken, + jwtProperties.getRefreshExpiration()); + tokenRepository.saveAccessToken(email, + accessToken, + jwtProperties.getAccessExpiration()); return new ApplicationCredentials(accessToken, refreshToken); } } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsUsingRefreshTokenImpl.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsUsingRefreshTokenImpl.java index 1d16949..7a057b0 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsUsingRefreshTokenImpl.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/GetNewCredentialsUsingRefreshTokenImpl.java @@ -11,18 +11,40 @@ @Service @RequiredArgsConstructor -public class GetNewCredentialsUsingRefreshTokenImpl implements GetNewCredentialsUsingRefreshToken { +public class GetNewCredentialsUsingRefreshTokenImpl + implements GetNewCredentialsUsingRefreshToken { + /** + * The repository for storing and retrieving tokens. + */ private final TokenRepository tokenRepository; + + /** + * The service for generating and validating JWT tokens. + */ private final JwtService jwtService; - private final JwtProperties jwtProperties; + /** + * The properties containing the configuration for JWT tokens. + */ + private final JwtProperties jwtProperties; + /** + * Not final to allow spring use proxy. + */ @Override - public ApplicationCredentials get(Credentials credentials) { + public ApplicationCredentials get(final Credentials credentials) { tokenRepository.deleteAllTokensByUserEmail(credentials.email()); var at = jwtService.generateAccessToken(credentials); var rt = jwtService.generateRefreshToken(credentials); - tokenRepository.saveAccessToken(credentials.email(), at, jwtProperties.getAccessExpiration()); - tokenRepository.saveRefreshToken(credentials.email(), rt, jwtProperties.getRefreshExpiration()); + tokenRepository.saveAccessToken( + credentials.email(), + at, + jwtProperties.getAccessExpiration() + ); + tokenRepository.saveRefreshToken( + credentials.email(), + rt, + jwtProperties.getRefreshExpiration() + ); return new ApplicationCredentials(at, rt); } } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/JwtServiceIml.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/JwtServiceIml.java index c229ea7..675b938 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/JwtServiceIml.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/JwtServiceIml.java @@ -2,7 +2,6 @@ import com.github.asavershin.api.application.in.services.user.JwtService; import com.github.asavershin.api.config.properties.JwtProperties; -import com.github.asavershin.api.domain.user.AuthenticatedUser; import com.github.asavershin.api.domain.user.Credentials; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @@ -18,20 +17,30 @@ @RequiredArgsConstructor public class JwtServiceIml implements JwtService { + /** + * The JwtProperties object contains the properties + * for generating and validating JWT tokens. + */ private final JwtProperties jwtProperties; - + /** + * Not final to allow spring use proxy. + */ @Override - public String generateAccessToken(Credentials credentials) { + public String generateAccessToken(final Credentials credentials) { return buildToken(credentials, jwtProperties.getAccessExpiration()); } - + /** + * Not final to allow spring use proxy. + */ @Override - public String generateRefreshToken(Credentials credentials) { + public String generateRefreshToken(final Credentials credentials) { return buildToken(credentials, jwtProperties.getRefreshExpiration()); } - + /** + * Not final to allow spring use proxy. + */ @Override - public String extractSub(String jwt) { + public String extractSub(final String jwt) { return Jwts.parserBuilder() .setSigningKey(getSignInKey()) .build() @@ -40,12 +49,15 @@ public String extractSub(String jwt) { .getSubject(); } - private String buildToken(Credentials credentials, long expiration) { + private String buildToken(final Credentials credentials, + final long expiration) { return Jwts .builder() .setSubject(credentials.email()) .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) + .setExpiration( + new Date(System.currentTimeMillis() + expiration) + ) .signWith(getSignInKey(), SignatureAlgorithm.HS256) .compact(); } diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/package-info.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/package-info.java new file mode 100644 index 0000000..b0db315 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/impl/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains implementation application services for users. + * @author asavershin + */ +package com.github.asavershin.api.application.in.services.user.impl; diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/user/package-info.java b/api/src/main/java/com/github/asavershin/api/application/in/services/user/package-info.java new file mode 100644 index 0000000..6bdc8a8 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/user/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains application services for users. + * @author asavershin + */ +package com.github.asavershin.api.application.in.services.user; diff --git a/api/src/main/java/com/github/asavershin/api/application/out/CacheRepository.java b/api/src/main/java/com/github/asavershin/api/application/out/CacheRepository.java index 7ba8d35..8c22291 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/CacheRepository.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/CacheRepository.java @@ -1,9 +1,28 @@ package com.github.asavershin.api.application.out; public interface CacheRepository { + /** + * Adds a cache entry with the specified key, + * token, and expiration time. + * + * @param key the unique identifier for the cache entry + * @param token the token associated with the cache entry + * @param expiration the time in milliseconds after + * which the cache entry will expire + */ void addCache(String key, String token, long expiration); - + /** + * Retrieves the cache entry associated with the specified key. + * + * @param key the unique identifier for the cache entry + * @return the token associated with the cache entry, + * or null if the entry does not exist or has expired + */ String getCache(String key); - + /** + * Deletes the cache entry associated with the specified key. + * + * @param key the unique identifier for the cache entry + */ void deleteCache(String key); } diff --git a/api/src/main/java/com/github/asavershin/api/application/out/FileService.java b/api/src/main/java/com/github/asavershin/api/application/out/FileService.java index fb837cb..03d3d05 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/FileService.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/FileService.java @@ -3,7 +3,24 @@ import java.util.List; public interface FileService { + /** + * Saves a file to the storage. + * + * @param file The file object to be saved. + * @return The saved file object. + */ T saveFile(K file); + /** + * Retrieves the content of a file from the storage. + * + * @param link The unique identifier or link of the file. + * @return The content of the file as a byte array. + */ byte[] getFile(String link); + /** + * Deletes multiple files from the storage. + * + * @param files A list of file links to be deleted. + */ void deleteFiles(List files); } diff --git a/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java b/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java index b829a63..5fb906e 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java @@ -2,5 +2,5 @@ import org.springframework.web.multipart.MultipartFile; -public interface MinioService extends FileService{ +public interface MinioService extends FileService { } diff --git a/api/src/main/java/com/github/asavershin/api/application/out/TokenRepository.java b/api/src/main/java/com/github/asavershin/api/application/out/TokenRepository.java index d57436b..2f03cb0 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/TokenRepository.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/TokenRepository.java @@ -1,13 +1,46 @@ package com.github.asavershin.api.application.out; public interface TokenRepository { + /** + * Retrieves the access token for the given email. + * + * @param email the email of the user + * @return the access token for the given email + */ String getAccessToken(String email); + /** + * Retrieves the refresh token for the given email. + * + * @param email the email of the user + * @return the refresh token for the given email + */ String getRefreshToken(String email); + /** + * Saves the refresh token for the given user with + * the specified JWT token and expiration time. + * + * @param username the username of the user + * @param jwtToken the JWT token to be saved + * @param expiration the expiration time of the token + */ void saveRefreshToken(String username, String jwtToken, Long expiration); + /** + * Saves the access token for the given user with + * the specified JWT token and expiration time. + * + * @param username the username of the user + * @param jwtToken the JWT token to be saved + * @param expiration the expiration time of the token + */ void saveAccessToken(String username, String jwtToken, Long expiration); + /** + * Deletes all tokens associated with the given user email. + * + * @param username the username of the user + */ void deleteAllTokensByUserEmail(String username); } diff --git a/api/src/main/java/com/github/asavershin/api/application/out/package-info.java b/api/src/main/java/com/github/asavershin/api/application/out/package-info.java new file mode 100644 index 0000000..c70aea2 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/application/out/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains storage interfaces that are not domain specific. + * @author asavershin + */ +package com.github.asavershin.api.application.out; diff --git a/api/src/main/java/com/github/asavershin/api/common/NotFoundException.java b/api/src/main/java/com/github/asavershin/api/common/NotFoundException.java index 2584611..a2a8110 100644 --- a/api/src/main/java/com/github/asavershin/api/common/NotFoundException.java +++ b/api/src/main/java/com/github/asavershin/api/common/NotFoundException.java @@ -1,7 +1,10 @@ package com.github.asavershin.api.common; -public class NotFoundException extends RuntimeException{ - public NotFoundException(String message){ +public class NotFoundException extends RuntimeException { + /** + * @param message specifies information about the object not found + */ + public NotFoundException(final String message) { super(message); } } diff --git a/api/src/main/java/com/github/asavershin/api/common/Validator.java b/api/src/main/java/com/github/asavershin/api/common/Validator.java index afd6770..f0f70dc 100644 --- a/api/src/main/java/com/github/asavershin/api/common/Validator.java +++ b/api/src/main/java/com/github/asavershin/api/common/Validator.java @@ -2,45 +2,138 @@ import java.util.Objects; -public class Validator { - public static void assertArgumentLength(String aString, int aMinimum, int aMaximum, String aMessage) { +/** + * A utility class for performing basic validation checks. + * + * @author asavershin + */ +public abstract class Validator { + + /** + * Asserts that the given string has a length within + * the specified range. + * + * @param aString the string to validate + * @param aMinimum the minimum length + * @param aMaximum the maximum length + * @param aMessage the message to throw if the length is invalid + * @throws IllegalArgumentException if the length of the string + * is less than {@code aMinimum} or greater than {@code aMaximum} + */ + public static void assertArgumentLength(final String aString, + final int aMinimum, + final int aMaximum, + final String aMessage) { int length = aString.length(); if (length < aMinimum || length > aMaximum) { throw new IllegalArgumentException(aMessage); } } - public static void assertArgumentLength(String aString, int aMinimum, String aMessage) { + /** + * Asserts that the given string has a length greater than or equal + * to the specified minimum. + * + * @param aString the string to validate + * @param aMinimum the minimum length + * @param aMessage the message to throw if the length is invalid + * @throws IllegalArgumentException if the length of the string is + * less than {@code aMinimum} + */ + public static void assertArgumentLength(final String aString, + final int aMinimum, + final String aMessage) { int length = aString.length(); if (length < aMinimum) { throw new IllegalArgumentException(aMessage); } } - public static void assertStringFormat(String email, String regex, String aMessage) { + /** + * Asserts that the given string matches the specified regular + * expression. + * + * @param email the string to validate + * @param regex the regular expression to match against + * @param aMessage the message to throw if the string does not + * match the regular expression + * @throws IllegalArgumentException if the string does not + * match the regular expression + */ + public static void assertStringFormat(final String email, + final String regex, + final String aMessage) { if (!email.matches(regex)) { throw new IllegalArgumentException(aMessage); } } - public static void assertArrayLength(Object[] array, Integer minLength, String aMessage) { + /** + * Asserts that the given array has a length greater than or + * equal to the specified minimum. + * + * @param array the array to validate + * @param minLength the minimum length + * @param aMessage the message to throw if the length of the + * array is less than {@code minLength} + * @throws IllegalArgumentException if the length of the array + * is less than {@code minLength} + */ + public static void assertArrayLength(final Object[] array, + final Integer minLength, + final String aMessage) { if (array.length < minLength) { throw new IllegalArgumentException(aMessage); } } - public static void assertLongSize(Long value, Long minLength, Long maxLength, String aMessage) { + /** + * Asserts that the given value is within the specified range. + * + * @param value the value to validate + * @param minLength the minimum length + * @param maxLength the maximum length + * @param aMessage the message to throw if the value is outside + * the specified range + * @throws IllegalArgumentException if the value is less than + * {@code minLength} or greater than {@code maxLength} + */ + public static void assertLongSize(final Long value, + final Long minLength, + final Long maxLength, + final String aMessage) { if (value < minLength || value > maxLength) { throw new IllegalArgumentException(aMessage); } } - public static void assertLongSize(Long value, Long minLength, String aMessage) { + /** + * Asserts that the given value is greater than or equal to the + * specified minimum. + * + * @param value the value to validate + * @param minLength the minimum length + * @param aMessage the message to throw if the value is less + * than {@code minLength} + * @throws IllegalArgumentException if the value is less + * than {@code minLength} + */ + public static void assertLongSize(final Long value, + final Long minLength, + final String aMessage) { if (value < minLength) { throw new IllegalArgumentException(aMessage); } } - public static void assertNotFound(Object object, String aMessage) { + /** + * Asserts that the given object is not null. + * + * @param object the object to validate + * @param aMessage the message to throw if the object is null + * @throws IllegalArgumentException if the object is null + */ + public static void assertNotFound(final Object object, + final String aMessage) { if (Objects.isNull(object)) { throw new NotFoundException(aMessage); } diff --git a/api/src/main/java/com/github/asavershin/api/common/annotations/Command.java b/api/src/main/java/com/github/asavershin/api/common/annotations/Command.java index 0725e3a..51b88d9 100644 --- a/api/src/main/java/com/github/asavershin/api/common/annotations/Command.java +++ b/api/src/main/java/com/github/asavershin/api/common/annotations/Command.java @@ -1,6 +1,10 @@ package com.github.asavershin.api.common.annotations; -import java.lang.annotation.*; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Inherited; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/api/src/main/java/com/github/asavershin/api/common/annotations/DomainService.java b/api/src/main/java/com/github/asavershin/api/common/annotations/DomainService.java index 6b0666c..30f2f55 100644 --- a/api/src/main/java/com/github/asavershin/api/common/annotations/DomainService.java +++ b/api/src/main/java/com/github/asavershin/api/common/annotations/DomainService.java @@ -1,6 +1,10 @@ package com.github.asavershin.api.common.annotations; -import java.lang.annotation.*; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Inherited; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/api/src/main/java/com/github/asavershin/api/common/annotations/Query.java b/api/src/main/java/com/github/asavershin/api/common/annotations/Query.java index 1d1483d..1b83bf0 100644 --- a/api/src/main/java/com/github/asavershin/api/common/annotations/Query.java +++ b/api/src/main/java/com/github/asavershin/api/common/annotations/Query.java @@ -1,6 +1,10 @@ package com.github.asavershin.api.common.annotations; -import java.lang.annotation.*; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Inherited; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/api/src/main/java/com/github/asavershin/api/common/annotations/package-info.java b/api/src/main/java/com/github/asavershin/api/common/annotations/package-info.java new file mode 100644 index 0000000..15967d2 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/common/annotations/package-info.java @@ -0,0 +1,5 @@ +/** + * This package annotations for classes that characterize their activities. + * @author asavershin + */ +package com.github.asavershin.api.common.annotations; diff --git a/api/src/main/java/com/github/asavershin/api/common/package-info.java b/api/src/main/java/com/github/asavershin/api/common/package-info.java new file mode 100644 index 0000000..74606bc --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/common/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains code that could be useful for coding. + * It contains validators and exceptions that often found. + * It annotations for classes that characterize their activities. + * @author asavershin + */ +package com.github.asavershin.api.common; diff --git a/api/src/main/java/com/github/asavershin/api/config/AnnotationsConfig.java b/api/src/main/java/com/github/asavershin/api/config/AnnotationsConfig.java index 216bfe6..6a084c2 100644 --- a/api/src/main/java/com/github/asavershin/api/config/AnnotationsConfig.java +++ b/api/src/main/java/com/github/asavershin/api/config/AnnotationsConfig.java @@ -11,9 +11,12 @@ @ComponentScan( basePackages = "com.github.asavershin.api", includeFilters = { - @ComponentScan.Filter(type = FilterType.ANNOTATION, value = DomainService.class), - @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Query.class), - @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Command.class) + @ComponentScan.Filter(type = FilterType.ANNOTATION, + value = DomainService.class), + @ComponentScan.Filter(type = FilterType.ANNOTATION, + value = Query.class), + @ComponentScan.Filter(type = FilterType.ANNOTATION, + value = Command.class) } ) public class AnnotationsConfig { diff --git a/api/src/main/java/com/github/asavershin/api/config/AuthConfig.java b/api/src/main/java/com/github/asavershin/api/config/AuthConfig.java index d72aeb3..69da912 100644 --- a/api/src/main/java/com/github/asavershin/api/config/AuthConfig.java +++ b/api/src/main/java/com/github/asavershin/api/config/AuthConfig.java @@ -3,17 +3,17 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @RequiredArgsConstructor public class AuthConfig { + /** + * Configures a BCryptPasswordEncoder bean for password encoding. + * + * @return a new instance of BCryptPasswordEncoder + */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/api/src/main/java/com/github/asavershin/api/config/MinIOConfig.java b/api/src/main/java/com/github/asavershin/api/config/MinIOConfig.java index 7bb2900..3249974 100644 --- a/api/src/main/java/com/github/asavershin/api/config/MinIOConfig.java +++ b/api/src/main/java/com/github/asavershin/api/config/MinIOConfig.java @@ -7,14 +7,29 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Configuration class for MinIO client. + * + * @author Asavershin + */ @Configuration @RequiredArgsConstructor @Slf4j public class MinIOConfig { + + /** + * The MinIO properties. + */ private final MinIOProperties minioProperties; + + /** + * Creates a MinioClient instance. + * + * @return A MinioClient instance configured with the provided + * MinIO properties. + */ @Bean public MinioClient minioClient() { - log.info("MinIOConfigLog"); log.info(minioProperties.toString()); return MinioClient.builder() .endpoint(minioProperties.getUrl()) diff --git a/api/src/main/java/com/github/asavershin/api/config/RedisConfig.java b/api/src/main/java/com/github/asavershin/api/config/RedisConfig.java index 5003bf1..5b920ef 100644 --- a/api/src/main/java/com/github/asavershin/api/config/RedisConfig.java +++ b/api/src/main/java/com/github/asavershin/api/config/RedisConfig.java @@ -7,14 +7,30 @@ import org.springframework.data.redis.serializer.GenericToStringSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +/** + * Configuration class for setting up the RedisTemplate. + * + * @author Asavershin + */ @Configuration public class RedisConfig { + + /** + * Creates a new instance of {@link RedisTemplate} with the provided + * {@link RedisConnectionFactory}. + * + * @param connectionFactory the RedisConnectionFactory to use + * @return a new instance of {@link RedisTemplate} + */ @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + public RedisTemplate redisTemplate( + final RedisConnectionFactory connectionFactory) { final RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(new GenericToStringSerializer<>(Object.class)); + template.setValueSerializer( + new GenericToStringSerializer<>(Object.class) + ); return template; } } diff --git a/api/src/main/java/com/github/asavershin/api/config/package-info.java b/api/src/main/java/com/github/asavershin/api/config/package-info.java new file mode 100644 index 0000000..3d5fb9e --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/config/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains configs for infrastructure of service. + * @author asavershin + */ +package com.github.asavershin.api.config; diff --git a/api/src/main/java/com/github/asavershin/api/config/properties/JwtProperties.java b/api/src/main/java/com/github/asavershin/api/config/properties/JwtProperties.java index 22f8438..5cb2e2b 100644 --- a/api/src/main/java/com/github/asavershin/api/config/properties/JwtProperties.java +++ b/api/src/main/java/com/github/asavershin/api/config/properties/JwtProperties.java @@ -1,16 +1,30 @@ package com.github.asavershin.api.config.properties; import lombok.Data; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data +@ConfigurationProperties(prefix = "jwt") public class JwtProperties { - @Value("${jwt.secret}") + /** + * Secret key used for signing JWT tokens. + */ private String secret; - @Value("${jwt.access-expiration}") + + /** + * Expiration time for access tokens in milliseconds. + */ private Long accessExpiration; - @Value("${jwt.refresh-expiration}") + + /** + * Expiration time for refresh tokens in milliseconds. + */ private Long refreshExpiration; + + /** + * Constants for the start of the token in the Authorization header. + */ + public static final int START_OF_TOKEN = 7; } diff --git a/api/src/main/java/com/github/asavershin/api/config/properties/MinIOProperties.java b/api/src/main/java/com/github/asavershin/api/config/properties/MinIOProperties.java index 7369083..805e730 100644 --- a/api/src/main/java/com/github/asavershin/api/config/properties/MinIOProperties.java +++ b/api/src/main/java/com/github/asavershin/api/config/properties/MinIOProperties.java @@ -1,8 +1,8 @@ package com.github.asavershin.api.config.properties; -import lombok.*; +import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; @Component @@ -10,8 +10,23 @@ @ConfigurationProperties(prefix = "minio") @NoArgsConstructor public class MinIOProperties { + /** + * The name of the bucket in the MinIO service. + */ private String bucket; + + /** + * The URL of the MinIO service. + */ private String url; + + /** + * The username for the MinIO service. + */ private String user; + + /** + * The password for the MinIO service. + */ private String password; } diff --git a/api/src/main/java/com/github/asavershin/api/config/properties/UserProperties.java b/api/src/main/java/com/github/asavershin/api/config/properties/UserProperties.java new file mode 100644 index 0000000..a4bbd51 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/config/properties/UserProperties.java @@ -0,0 +1,18 @@ +package com.github.asavershin.api.config.properties; + +public final class UserProperties { + /** + * Minimum password length. + */ + public static final int MIN_PASSWORD_LENGTH = 8; + /** + * Maximum firstname length. + */ + public static final int MAX_FIRSTNAME_LENGTH = 20; + /** + * Maximum lastname length. + */ + public static final int MAX_LASTNAME_LENGTH = 20; + + private UserProperties() { } +} diff --git a/api/src/main/java/com/github/asavershin/api/config/properties/package-info.java b/api/src/main/java/com/github/asavershin/api/config/properties/package-info.java new file mode 100644 index 0000000..1109013 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/config/properties/package-info.java @@ -0,0 +1,6 @@ +/** + * This package contains parameters that can be used in different + * parts of the application. The values are taken from .yml/.properties file + * @author asavershin + */ +package com.github.asavershin.api.config.properties; diff --git a/api/src/main/java/com/github/asavershin/api/domain/IsEntityFound.java b/api/src/main/java/com/github/asavershin/api/domain/IsEntityFound.java index 9aca1af..837edcf 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/IsEntityFound.java +++ b/api/src/main/java/com/github/asavershin/api/domain/IsEntityFound.java @@ -3,7 +3,10 @@ import com.github.asavershin.api.common.Validator; public abstract class IsEntityFound { - protected void isEntityFound(Object entity, String entityName, String idName, String entityId){ + protected final void isEntityFound(final Object entity, + final String entityName, + final String idName, + final String entityId) { Validator.assertNotFound(entity, entityName + " with " + idName + entityId + " not found"); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/PartOfResources.java b/api/src/main/java/com/github/asavershin/api/domain/PartOfResources.java index 0c8262f..1c0f0ba 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/PartOfResources.java +++ b/api/src/main/java/com/github/asavershin/api/domain/PartOfResources.java @@ -4,12 +4,37 @@ import java.util.Objects; +/** + * A record representing a part of resources to be fetched from a + * data source. + * It contains the page number and the page size for pagination purposes. + * + * @param pageNumber The zero-based index of the first record + * to retrieve. + * @param pageSize The maximum number of records to retrieve. + */ public record PartOfResources(Long pageNumber, Long pageSize) { - public PartOfResources{ - Objects.requireNonNull(pageNumber, "PageNumber must not be empty"); - Objects.requireNonNull(pageSize, "PageSize must not be empty"); - Validator.assertLongSize(pageNumber, 0L, "PageNumber must not be negative"); - if(pageSize <= 0) - throw new IllegalArgumentException("PageSize must be positive"); + + /** + * Constructor for the PartOfResources record. + * + * @param pageNumber The zero-based index of the first record + * to retrieve. + * @param pageSize The maximum number of records to retrieve. + * @throws IllegalArgumentException if the pageSize is less than + * or equal to zero. + */ + public PartOfResources { + Objects.requireNonNull(pageNumber, + "PageNumber must not be empty"); + Objects.requireNonNull(pageSize, + "PageSize must not be empty"); + Validator.assertLongSize(pageNumber, + 0L, "PageNumber must not be negative"); + Validator.assertLongSize( + pageSize, + 1L, + "PageSize must be positive" + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/ResourceOwnershipException.java b/api/src/main/java/com/github/asavershin/api/domain/ResourceOwnershipException.java index b9395ee..3db43d2 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/ResourceOwnershipException.java +++ b/api/src/main/java/com/github/asavershin/api/domain/ResourceOwnershipException.java @@ -1,7 +1,20 @@ package com.github.asavershin.api.domain; +/** + * Represents an exception that is thrown + * when there is an issue with resource ownership. + * For example, a user requests someone else's picture + * @author Asavershin + */ public class ResourceOwnershipException extends RuntimeException { - public ResourceOwnershipException(String message) { + + /** + * Constructs a new instance of the ResourceOwnershipException + * with the specified error message. + * + * @param message the error message + */ + public ResourceOwnershipException(final String message) { super(message); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/DeleteImageOfUser.java b/api/src/main/java/com/github/asavershin/api/domain/image/DeleteImageOfUser.java index 4b425b0..404c0ab 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/DeleteImageOfUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/DeleteImageOfUser.java @@ -4,5 +4,11 @@ @FunctionalInterface public interface DeleteImageOfUser { + /** + * Removes the specified image of the specified user. + * + * @param imageId the unique identifier of the image to be removed + * @param userId the unique identifier of the user who owns the image + */ void removeImageOfUser(ImageId imageId, UserId userId); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/GetImageOfUser.java b/api/src/main/java/com/github/asavershin/api/domain/image/GetImageOfUser.java index ce7911c..a116ae6 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/GetImageOfUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/GetImageOfUser.java @@ -4,5 +4,12 @@ @FunctionalInterface public interface GetImageOfUser { + /** + * Method that retrieves an image of a user. + * + * @param userId the unique identifier of the user + * @param imageId the unique identifier of the image + * @return the image of the specified user + */ Image getImageOfUser(UserId userId, ImageId imageId); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/GetPartImagesOfUser.java b/api/src/main/java/com/github/asavershin/api/domain/image/GetPartImagesOfUser.java index 534413f..db0cb2b 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/GetPartImagesOfUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/GetPartImagesOfUser.java @@ -7,5 +7,14 @@ @FunctionalInterface public interface GetPartImagesOfUser { + /** + * This interface represents a function that retrieves a list of + * images for a specific user and part of resources. + * + * @param userId the unique identifier of the user + * @param partOfResources pagination + * @return a list of images belonging to the specified + * user and part of resources as pagination + */ List get(UserId userId, PartOfResources partOfResources); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/Image.java b/api/src/main/java/com/github/asavershin/api/domain/image/Image.java index b6a9c90..f8c7063 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/Image.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/Image.java @@ -1,6 +1,5 @@ package com.github.asavershin.api.domain.image; -import com.github.asavershin.api.domain.IsEntityFound; import com.github.asavershin.api.domain.ResourceOwnershipException; import com.github.asavershin.api.domain.user.UserId; import lombok.EqualsAndHashCode; @@ -13,36 +12,68 @@ @Getter @EqualsAndHashCode public class Image { + /** + * The unique identifier for the image. + */ private ImageId imageId; - private MetaInfo metaInfo; + + /** + * The meta information associated with the image. + */ + private MetaData metaInfo; + + /** + * The user who owns the image. + */ private UserId userId; - public Image(ImageId imageId, MetaInfo metaInfo, UserId userId){ - setImageId(imageId); - setMetaInfo(metaInfo); - setUserId(userId); + + /** + * Constructor for creating a new Image object. + * + * @param aImageId the unique identifier for the image + * @param aMetaInfo the meta information associated with the image + * @param aUserId the user who owns the image + */ + public Image(final ImageId aImageId, + final MetaData aMetaInfo, + final UserId aUserId) { + setImageId(aImageId); + setMetaInfo(aMetaInfo); + setUserId(aUserId); } - public Image belongsToUser(UserId userId){ - if (!this.userId.equals(userId)){ + /** + * Checks if the given user owns the image. + * + * @param aUserId the user to check ownership for + * @return the same image instance if the user owns the image, + * otherwise throws a {@link ResourceOwnershipException} + * @throws ResourceOwnershipException if the image does not belong + * to the given user + */ + public Image belongsToUser(final UserId aUserId) { + if (!this.userId.equals(aUserId)) { throw new ResourceOwnershipException( - "Image with id " + imageId.value().toString()+ " does not belong to user with id " + userId.value().toString() + "Image with id " + imageId.value().toString() + + " does not belong to user with id " + + aUserId.value().toString() ); } return this; } - private void setImageId(ImageId imageId) { - Objects.requireNonNull(imageId, "ImageId must not be null"); - this.imageId = imageId; + private void setImageId(final ImageId aImageId) { + Objects.requireNonNull(aImageId, "ImageId must not be null"); + this.imageId = aImageId; } - private void setMetaInfo(MetaInfo metaInfo) { - Objects.requireNonNull(metaInfo, "MetaInfo must not be null"); - this.metaInfo = metaInfo; + private void setMetaInfo(final MetaData aMetaInfo) { + Objects.requireNonNull(aMetaInfo, "MetaInfo must not be null"); + this.metaInfo = aMetaInfo; } - private void setUserId(UserId userId) { - Objects.requireNonNull(userId, "UserId must not be null"); - this.userId = userId; + private void setUserId(final UserId aUserId) { + Objects.requireNonNull(aUserId, "UserId must not be null"); + this.userId = aUserId; } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/ImageExtension.java b/api/src/main/java/com/github/asavershin/api/domain/image/ImageExtension.java index 91b163f..2b367f1 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/ImageExtension.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/ImageExtension.java @@ -1,20 +1,39 @@ package com.github.asavershin.api.domain.image; -import lombok.EqualsAndHashCode; - import java.util.Map; import java.util.stream.Stream; import static java.util.stream.Collectors.toMap; + +/** + * Enum representing different image file extensions. + * + * @author asavershin + */ public enum ImageExtension { + /** + * Represents the JPG image file extension. + */ JPG(".jpg"), + + /** + * Represents the PNG image file extension. + */ PNG(".png"), - JPEG(".jpeg"); + /** + * Represents the JPEG image file extension. + */ + JPEG(".jpeg"); + /** + * Represents the string image file extension + * in format .extension. + */ private final String extension; - private ImageExtension(String extension){ - this.extension = extension; + + ImageExtension(final String aExtension) { + this.extension = aExtension; } @Override @@ -22,13 +41,29 @@ public String toString() { return extension; } - private static final Map stringToEnum + /** + * Map that stores a string representation of an extension in keys, + * and their corresponding ENUM in values. + */ + private static final Map STRING_TO_ENUM = Stream.of(values()).collect(toMap(Object::toString, e -> e)); - public static ImageExtension fromString(String extension){ - var imageExtension = stringToEnum.get(extension); - if(imageExtension == null){ - throw new IllegalArgumentException("Invalid extension: " + extension); + /** + * Converts a string representation of an image extension + * to the corresponding enum instance. + * + * @param extension the string representation of the image extension + * @return the enum instance corresponding to the given + * string representation + * @throws IllegalArgumentException if the given string representation + * does not match any known image extension + */ + public static ImageExtension fromString(final String extension) { + var imageExtension = STRING_TO_ENUM.get(extension); + if (imageExtension == null) { + throw new IllegalArgumentException( + "Invalid extension: " + extension + ); } return imageExtension; } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/ImageId.java b/api/src/main/java/com/github/asavershin/api/domain/image/ImageId.java index 690ac67..f6ef6c7 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/ImageId.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/ImageId.java @@ -3,11 +3,26 @@ import java.util.Objects; import java.util.UUID; +/** + * A value object representing an image ID. + * + * @param value The unique identifier for the image. + */ public record ImageId(UUID value) { + /** + * Constructs an ImageId instance with the provided UUID. + * + * @param value The unique identifier for the image. + * @throws NullPointerException if the provided value is null. + */ public ImageId { Objects.requireNonNull(value, "Image ID must not be null"); } - + /** + * Generates a new, unique ImageId. + * + * @return A new ImageId instance with a randomly generated UUID. + */ public static ImageId nextIdentity() { return new ImageId(UUID.randomUUID()); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/ImageNameWithExtension.java b/api/src/main/java/com/github/asavershin/api/domain/image/ImageNameWithExtension.java index c3e321e..5b0f88f 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/ImageNameWithExtension.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/ImageNameWithExtension.java @@ -6,41 +6,82 @@ import java.util.Arrays; import java.util.Objects; + @Getter @EqualsAndHashCode -public class ImageNameWithExtension { +public final class ImageNameWithExtension { + /** + * The maximum length of an image name. + */ private static final int MAX_IMAGE_NAME = 50; + + /** + * The image name. + */ private final String imageName; + + /** + * The image extension. + */ private final ImageExtension imageExtension; - private ImageNameWithExtension(String imageName, ImageExtension imageExt){ - this.imageName = imageName; - this.imageExtension = imageExt; + /** + * Constructs an {@code ImageNameWithExtension} object. + * + * @param aImageName the image name without last extension + * @param aImageExt the image extension + */ + private ImageNameWithExtension(final String aImageName, + final ImageExtension aImageExt) { + this.imageName = aImageName; + this.imageExtension = aImageExt; } - - public static ImageNameWithExtension fromOriginalFileName(String originalFileName){ + /** + * Creates an {@code ImageNameWithExtension} + * object from an original file name. + * + * @param originalFileName the original file name + * @return the created {@code ImageNameWithExtension} object + */ + public static ImageNameWithExtension fromOriginalFileName( + final String originalFileName + ) { notNullValidate(originalFileName); String[] parts = originalFileName.split("\\."); Validator.assertArrayLength(parts, 2, "Incorrect image format"); - var extension = ImageExtension.fromString("." + parts[parts.length-1]); - var imageName = String.join(".", Arrays.copyOfRange(parts, 0, parts.length - 1)); + var extension = ImageExtension + .fromString("." + parts[parts.length - 1]); + var imageName = String + .join(".", Arrays.copyOfRange(parts, 0, parts.length - 1)); lengthNameValidate(imageName); return new ImageNameWithExtension(imageName, extension); } - public static ImageNameWithExtension founded(String imageName, String extension) { + + /** + * Creates an {@code ImageNameWithExtension} + * object that founded in repository from an image name and an extension. + * + * @param imageName the image name + * @param extension the image extension + * @return the created {@code ImageNameWithExtension} object + */ + public static ImageNameWithExtension founded( + final String imageName, + final String extension) { notNullValidate(imageName); lengthNameValidate(imageName); - return new ImageNameWithExtension(imageName, ImageExtension.fromString(extension)); + return new ImageNameWithExtension(imageName, + ImageExtension.fromString(extension)); } - private static void notNullValidate(String name){ + private static void notNullValidate(final String name) { Objects.requireNonNull(name, "ImageName must not be null"); } - private static void lengthNameValidate(String name){ - Validator.assertArgumentLength(name,0, MAX_IMAGE_NAME, + private static void lengthNameValidate(final String name) { + Validator.assertArgumentLength(name, 0, MAX_IMAGE_NAME, "ImageName must be " + 0 + "-" + MAX_IMAGE_NAME + " in length"); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/ImageRepository.java b/api/src/main/java/com/github/asavershin/api/domain/image/ImageRepository.java index 6669ca9..5998b6b 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/ImageRepository.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/ImageRepository.java @@ -6,10 +6,35 @@ import java.util.List; public interface ImageRepository { + /** + * Saves an Image object to the database. + * + * @param image the Image object to be saved + */ void save(Image image); - List findImagesByUserId(UserId userId, PartOfResources partOfResources); + /** + * Finds all Images associated with the given UserId and PartOfResources. + * + * @param userId the UserId of the user whose images are to be retrieved + * @param partOfResources the PartOfResources to filter the images by + * @return a list of Images associated with the given UserId + * and PartOfResources + */ + List findImagesByUserId(UserId userId, + PartOfResources partOfResources); + /** + * Finds an Image by its ImageId. + * + * @param imageId the ImageId of the Image to be retrieved + * @return the Image object with the given ImageId, or null if not found + */ Image findImageByImageId(ImageId imageId); - void deleteImageByImageId(Image ImageId); + /** + * Deletes an Image by its ImageId. + * + * @param imageId the ImageId of the Image to be deleted + */ + void deleteImageByImageId(Image imageId); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/MetaData.java b/api/src/main/java/com/github/asavershin/api/domain/image/MetaData.java new file mode 100644 index 0000000..1a34051 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/image/MetaData.java @@ -0,0 +1,43 @@ +package com.github.asavershin.api.domain.image; + +import com.github.asavershin.api.common.Validator; + +import java.util.Objects; + +public record MetaData(ImageNameWithExtension imageNameWithExtension, + Long imageSize) { + /** + * The maximum length of an image name. + */ + private static final int MAX_IMAGE_NAME = 50; + /** + * Size is in bytes. 10MB + */ + private static final long MAX_IMAGE_SIZE = 10485760; + + /** + * Constructs a new instance of MetaInfo + * and validates the provided image name and size. + */ + public MetaData { + validateImageName(imageNameWithExtension); + validateImageSize(imageSize); + } + + private void validateImageName( + final ImageNameWithExtension aImageNameWithExtension + ) { + Objects.requireNonNull(aImageNameWithExtension, + "Name and extension must not be null"); + Validator.assertArgumentLength(aImageNameWithExtension.toString(), + 0, MAX_IMAGE_NAME, + "Image name must be " + + "0-" + MAX_IMAGE_NAME + " in length"); + } + + private void validateImageSize(final Long aImageSize) { + Objects.requireNonNull(aImageSize, "Image size must not be null"); + Validator.assertLongSize(aImageSize, 0L, MAX_IMAGE_SIZE, + "Image size must be " + "0-" + MAX_IMAGE_SIZE); + } +} diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/MetaInfo.java b/api/src/main/java/com/github/asavershin/api/domain/image/MetaInfo.java deleted file mode 100644 index 3fc7fd3..0000000 --- a/api/src/main/java/com/github/asavershin/api/domain/image/MetaInfo.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.asavershin.api.domain.image; - -import com.github.asavershin.api.common.Validator; - -import java.util.Objects; - -public record MetaInfo(ImageNameWithExtension imageNameWithExtension, Long imageSize) { - private static final int MAX_IMAGE_NAME = 50; - /** - * @param imageName - * Size is in bytes. 10MB - */ - private static final long MAX_IMAGE_SIZE = 10485760; - - public MetaInfo { - validateImageName(imageNameWithExtension); - validateImageSize(imageSize); - } - - private void validateImageName(ImageNameWithExtension imageNameWithExtension) { - Objects.requireNonNull(imageNameWithExtension, "Name and extension must not be null"); - } - - private void validateImageSize(Long imageSize){ - Objects.requireNonNull(imageSize, "Image size must not be null"); - Validator.assertLongSize(imageSize, 0L, MAX_IMAGE_SIZE, - "Image size must be " + "0-" + MAX_IMAGE_SIZE + " in length"); - } -} diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/StoreImageOfUser.java b/api/src/main/java/com/github/asavershin/api/domain/image/StoreImageOfUser.java index e292586..ab0d3d4 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/StoreImageOfUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/StoreImageOfUser.java @@ -2,5 +2,10 @@ @FunctionalInterface public interface StoreImageOfUser { + /** + * Stores the provided image of a user. + * + * @param image The image to be stored. + */ void storeImageOfUser(Image image); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/impl/DeleteImageOfUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/image/impl/DeleteImageOfUserImpl.java index fffc7e8..569b793 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/impl/DeleteImageOfUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/impl/DeleteImageOfUserImpl.java @@ -10,13 +10,26 @@ @Command @RequiredArgsConstructor -public class DeleteImageOfUserImpl extends IsEntityFound implements DeleteImageOfUser { +public class DeleteImageOfUserImpl extends IsEntityFound + implements DeleteImageOfUser { + /** + * The ImageRepository dependency for accessing image data. + */ private final ImageRepository imageRepository; + + /** + * Not final to allow spring use proxy. + */ @Override - public void removeImageOfUser(ImageId imageId, UserId userId) { + public void removeImageOfUser( + final ImageId imageId, + final UserId userId + ) { var image = imageRepository.findImageByImageId(imageId); isEntityFound(image, "Image", "Id", imageId.value().toString()); image.belongsToUser(userId); - imageRepository.deleteImageByImageId(imageRepository.findImageByImageId(imageId)); + imageRepository.deleteImageByImageId( + imageRepository.findImageByImageId(imageId) + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetImageOfUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetImageOfUserImpl.java index 23b8074..674e5ac 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetImageOfUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetImageOfUserImpl.java @@ -11,12 +11,25 @@ @DomainService @RequiredArgsConstructor -public class GetImageOfUserImpl extends IsEntityFound implements GetImageOfUser { +public class GetImageOfUserImpl extends IsEntityFound + implements GetImageOfUser { + /** + * The ImageRepository dependency for accessing image data. + */ private final ImageRepository imageRepository; + /** + * Not final to allow spring use proxy. + */ @Override - public Image getImageOfUser(UserId userId, ImageId imageId) { + public Image getImageOfUser(final UserId userId, + final ImageId imageId) { var image = imageRepository.findImageByImageId(imageId); - isEntityFound(image, "Image", "Id", imageId.value().toString()); + isEntityFound( + image, + "Image", + "Id", + imageId.value().toString() + ); image.belongsToUser(userId); return image; } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetPartImagesOfUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetPartImagesOfUserImpl.java index 34de1c8..9a85c58 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetPartImagesOfUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/impl/GetPartImagesOfUserImpl.java @@ -13,9 +13,16 @@ @Query @RequiredArgsConstructor public class GetPartImagesOfUserImpl implements GetPartImagesOfUser { + /** + * The ImageRepository dependency for accessing image data. + */ private final ImageRepository imageRepository; + /** + * Not final to allow spring use proxy. + */ @Override - public List get(UserId userId, PartOfResources partOfResources) { + public List get(final UserId userId, + final PartOfResources partOfResources) { return imageRepository.findImagesByUserId(userId, partOfResources); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/impl/StoreImageOfUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/image/impl/StoreImageOfUserImpl.java index 6712b25..d48c082 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/image/impl/StoreImageOfUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/image/impl/StoreImageOfUserImpl.java @@ -9,10 +9,15 @@ @Command @RequiredArgsConstructor public class StoreImageOfUserImpl implements StoreImageOfUser { + /** + * The ImageRepository dependency for accessing image data. + */ private final ImageRepository imageRepository; - + /** + * Not final to allow spring use proxy. + */ @Override - public void storeImageOfUser(Image image) { + public void storeImageOfUser(final Image image) { imageRepository.save(image); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/impl/package-info.java b/api/src/main/java/com/github/asavershin/api/domain/image/impl/package-info.java new file mode 100644 index 0000000..65e29ee --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/image/impl/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains implementations of domain services, + * queries, commands for image entity. + * + * @author asavershin + */ +package com.github.asavershin.api.domain.image.impl; diff --git a/api/src/main/java/com/github/asavershin/api/domain/image/package-info.java b/api/src/main/java/com/github/asavershin/api/domain/image/package-info.java new file mode 100644 index 0000000..3e4dfaa --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/image/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains the domain model classes related to image processing. + * @author asavershin + */ +package com.github.asavershin.api.domain.image; diff --git a/api/src/main/java/com/github/asavershin/api/domain/package-info.java b/api/src/main/java/com/github/asavershin/api/domain/package-info.java new file mode 100644 index 0000000..9ee4b6d --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/package-info.java @@ -0,0 +1,8 @@ +/** + * This package contains the entities, value objects + * entity repositories, queries, commands and domain + * services used in the application. + * + * @author Asavershin + */ +package com.github.asavershin.api.domain; diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/AuthException.java b/api/src/main/java/com/github/asavershin/api/domain/user/AuthException.java index 4203fe8..e6f8635 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/AuthException.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/AuthException.java @@ -1,7 +1,21 @@ package com.github.asavershin.api.domain.user; +/** + * Represents an exception that occurs during authentication process. + * + * @author asavershin + */ public class AuthException extends RuntimeException { - public AuthException(String message){ + + /** + * Constructs an instance of AuthException with + * the specified detail message. + * + * @param message the detail message. + * The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public AuthException(final String message) { super(message); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUser.java b/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUser.java index e6c4184..28bfc2e 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUser.java @@ -1,6 +1,5 @@ package com.github.asavershin.api.domain.user; -import com.github.asavershin.api.domain.IsEntityFound; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -10,26 +9,51 @@ @Getter @ToString @EqualsAndHashCode -public class AuthenticatedUser { +public final class AuthenticatedUser { + /** + * The unique identifier of the user. + */ private UserId userId; + + /** + * The credentials of the user. + */ private Credentials userCredentials; - private AuthenticatedUser(UserId userId, Credentials userCredentials) { - setUserId(userId); - setUserCredentials(userCredentials); + /** + * Constructs an instance of AuthenticatedUser + * with the provided userId and userCredentials. + * + * @param aUserId the unique identifier of the user + * @param aUserCredentials the credentials of the user + */ + private AuthenticatedUser(final UserId aUserId, + final Credentials aUserCredentials) { + setUserId(aUserId); + setUserCredentials(aUserCredentials); } - public static AuthenticatedUser founded(UserId userId, Credentials credentials) { + /** + * Creates a new instance of AuthenticatedUser that becomes from repository + * with the provided userId and userCredentials. + * + * @param userId the unique identifier of the user + * @param credentials the credentials of the user + * @return a new instance of AuthenticatedUser + */ + public static AuthenticatedUser founded(final UserId userId, + final Credentials credentials) { return new AuthenticatedUser(userId, credentials); } - private void setUserId(UserId userId) { - Objects.requireNonNull(userId, "UserId must not be null"); - this.userId = userId; + private void setUserId(final UserId aUserId) { + Objects.requireNonNull(aUserId, "UserId must not be null"); + this.userId = aUserId; } - private void setUserCredentials(Credentials userCredentials) { - Objects.requireNonNull(userCredentials, "UserCredentials must not be null"); - this.userCredentials = userCredentials; + private void setUserCredentials(final Credentials aUserCredentials) { + Objects.requireNonNull(aUserCredentials, + "UserCredentials must not be null"); + this.userCredentials = aUserCredentials; } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUserRepository.java b/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUserRepository.java index 037c05e..5ac94d9 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUserRepository.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/AuthenticatedUserRepository.java @@ -1,7 +1,14 @@ package com.github.asavershin.api.domain.user; -import java.util.Optional; - +/** + * Represents a repository for finding authenticated users by their email. + */ public interface AuthenticatedUserRepository { + /** + * Finds an authenticated user by their email. + * + * @param email the email of the user to find + * @return the authenticated user with the given email, or null if not found + */ AuthenticatedUser findByEmail(String email); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/Credentials.java b/api/src/main/java/com/github/asavershin/api/domain/user/Credentials.java index e593614..96a59e4 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/Credentials.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/Credentials.java @@ -6,28 +6,48 @@ import java.util.Objects; public record Credentials(String email, String password) { + /** + * The maximum length of an email. + */ private static final int MAX_EMAIL_LENGTH = 50; + + /** + * The minimum length of an email. + */ private static final int MIN_EMAIL_LENGTH = 5; + + /** + * The minimum length of a password. + */ private static final int MIN_PASSWORD_LENGTH = 8; + /** + * Constructs a new instance of Credentials. + * Validates the email and password before creating the instance. + */ public Credentials { validateEmail(email); validatePassword(password); } - private void validateEmail(String email) { - Objects.requireNonNull(email, "Email must not be null"); - Validator.assertArgumentLength(email, + private void validateEmail(final String aEmail) { + Objects.requireNonNull(aEmail, "Email must not be null"); + Validator.assertArgumentLength(aEmail, MIN_EMAIL_LENGTH, MAX_EMAIL_LENGTH, - "Email must be " + MIN_EMAIL_LENGTH + "-" + MAX_EMAIL_LENGTH + " in length"); + "Email must be " + + MIN_EMAIL_LENGTH + "-" + + MAX_EMAIL_LENGTH + " in length"); - Validator.assertStringFormat(email, "[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\\.[A-Za-z0-9.-]+", + Validator.assertStringFormat(aEmail, + "[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\\.[A-Za-z0-9.-]+", "Email is not in the correct format"); } - private void validatePassword(String password) { - Objects.requireNonNull(password, "Password must not be null"); - Validator.assertArgumentLength(password, MIN_PASSWORD_LENGTH, "Password must be greater than " + MIN_PASSWORD_LENGTH); + private void validatePassword(final String aPassword) { + Objects.requireNonNull(aPassword, "Password must not be null"); + Validator.assertArgumentLength(aPassword, + MIN_PASSWORD_LENGTH, + "Password must be greater than " + MIN_PASSWORD_LENGTH); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/FullName.java b/api/src/main/java/com/github/asavershin/api/domain/user/FullName.java index 1190c92..162a715 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/FullName.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/FullName.java @@ -1,23 +1,45 @@ package com.github.asavershin.api.domain.user; - import com.github.asavershin.api.common.Validator; import java.util.Objects; +/** + * A value object representing a user's full name, + * consisting of a first name and a last name. + * The length of each name is limited to 20 characters. + * + * @param firstname The first name of the user + * @param lastname The last name of the user + */ public record FullName(String firstname, String lastname) { + /** + * The maximum length of a first name. + */ private static final int MAX_FIRST_NAME_LENGTH = 20; + /** + * The maximum length of a last name. + */ private static final int MAX_LAST_NAME_LENGTH = 20; + /** + * Constructs a new instance of FullName and validates the length of + * the first and last names. + */ public FullName { validateName(firstname, MAX_FIRST_NAME_LENGTH); validateName(lastname, MAX_LAST_NAME_LENGTH); } - private void validateName(String name, int maxLength) { + private void validateName(final String name, final int maxLength) { Objects.requireNonNull(name, "Firstname must not be null"); - Validator.assertArgumentLength(name, 0, MAX_FIRST_NAME_LENGTH, "Name must be " + "0-" + maxLength + " in length"); + Validator.assertArgumentLength( + name, + 0, + MAX_FIRST_NAME_LENGTH, + "Name must be " + "0-" + maxLength + " in length" + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/GetPartOfImagesForAuthenticatedUser.java b/api/src/main/java/com/github/asavershin/api/domain/user/GetPartOfImagesForAuthenticatedUser.java index 946eefa..a4ede32 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/GetPartOfImagesForAuthenticatedUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/GetPartOfImagesForAuthenticatedUser.java @@ -5,7 +5,22 @@ import java.util.List; +/** + * This interface represents a function that retrieves a list of images + * for a given authenticated user and a specific part of resources. + * + */ @FunctionalInterface public interface GetPartOfImagesForAuthenticatedUser { - public List get(UserId userId, PartOfResources partOfResources); + /** + * Retrieves a list of images for a given authenticated user and + * a specific part of resources. + * + * @param userId the unique identifier of the authenticated user + * @param partOfResources the specific part of resources + * for which the images are requested + * @return a list of images associated with the specified + * user and part of resources + */ + List get(UserId userId, PartOfResources partOfResources); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/RegisterNewUser.java b/api/src/main/java/com/github/asavershin/api/domain/user/RegisterNewUser.java index a3caf54..a3a4b5f 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/RegisterNewUser.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/RegisterNewUser.java @@ -1,6 +1,18 @@ package com.github.asavershin.api.domain.user; +/** + * A functional interface that represents a method to register a new user. + * + */ @FunctionalInterface public interface RegisterNewUser { + /** + * Registers a new user with the provided full name and credentials. + * + * @param fullName The FullName value object + * containing the user's full name. + * @param credentials The Credentials value object + * containing the user's credentials. + */ void register(FullName fullName, Credentials credentials); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/TryToLogin.java b/api/src/main/java/com/github/asavershin/api/domain/user/TryToLogin.java index a6a17f8..374164d 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/TryToLogin.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/TryToLogin.java @@ -1,6 +1,20 @@ package com.github.asavershin.api.domain.user; +/** + * A functional interface representing a login operation for user + * using login, password. + * + * @author asavershin + * @since 1.0 + */ @FunctionalInterface public interface TryToLogin { + /** + * Attempts to log the user in using the provided credentials. + * + * @param credentials The credentials to use for login. + * @return An instance of {@link AuthenticatedUser} + * if the login is successful, otherwise null. + */ AuthenticatedUser login(Credentials credentials); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/User.java b/api/src/main/java/com/github/asavershin/api/domain/user/User.java index 061f25a..d1b2133 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/User.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/User.java @@ -1,3 +1,9 @@ +/** + * User class represents a user entity in the system. + * It contains the user's unique identifier, full name, and credentials. + * + * @author asavershin + */ package com.github.asavershin.api.domain.user; import lombok.EqualsAndHashCode; @@ -8,32 +14,53 @@ @ToString @EqualsAndHashCode -public class User{ - @Getter +@Getter +public class User { + /** + * The unique identifier for the user. + */ private UserId userId; - @Getter + + /** + * The full name of the user. + */ private FullName userFullName; - @Getter + + /** + * The credentials of the user. + */ private Credentials userCredentials; - public User(UserId userId, FullName userFullName, Credentials userCredentials) { - setUserId(userId); - setUserCredentials(userCredentials); - setUserFullName(userFullName); + /** + * Constructor for User class. + * + * @param id the unique identifier for the user + * @param fullname the full name of the user + * @param credentials the credentials of the user + */ + public User(final UserId id, + final FullName fullname, + final Credentials credentials) { + setUserId(id); + setUserCredentials(credentials); + setUserFullName(fullname); } - private void setUserId(UserId userId) { - Objects.requireNonNull(userId, "UserId must not be null"); - this.userId = userId; + private void setUserId(final UserId id) { + Objects.requireNonNull(id, + "UserId must not be null"); + this.userId = id; } - private void setUserFullName(FullName userFullName) { - Objects.requireNonNull(userFullName, "UserFullName must not be null"); - this.userFullName = userFullName; + private void setUserFullName(final FullName fullName) { + Objects.requireNonNull(fullName, + "UserFullName must not be null"); + this.userFullName = fullName; } - private void setUserCredentials(Credentials userCredentials) { - Objects.requireNonNull(userCredentials, "UserCredentials must not be null"); - this.userCredentials = userCredentials; + private void setUserCredentials(final Credentials credentials) { + Objects.requireNonNull(credentials, + "UserCredentials must not be null"); + this.userCredentials = credentials; } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/UserId.java b/api/src/main/java/com/github/asavershin/api/domain/user/UserId.java index 1f672dd..c463fcf 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/UserId.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/UserId.java @@ -1,15 +1,36 @@ +/** + * A unique identifier for a user. + * + * @author asavershin + */ package com.github.asavershin.api.domain.user; -import lombok.EqualsAndHashCode; - import java.util.Objects; import java.util.UUID; +/** + * A value object representing a unique identifier for a user. + * + * @param value The unique identifier for the user. + */ public record UserId(UUID value) { + + /** + * Constructs a new instance of {@code UserId} with the provided + * unique identifier. + * + * @param value The unique identifier for the user. + * @throws NullPointerException if the provided value is null. + */ public UserId { Objects.requireNonNull(value, "User ID must not be null"); } + /** + * Generates a new unique identifier for a user. + * + * @return A new unique identifier for a user. + */ public static UserId nextIdentity() { return new UserId(UUID.randomUUID()); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/UserRepository.java b/api/src/main/java/com/github/asavershin/api/domain/user/UserRepository.java index df27a0c..e21f666 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/UserRepository.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/UserRepository.java @@ -1,9 +1,27 @@ package com.github.asavershin.api.domain.user; -import java.util.Optional; - +/** + * UserRepository interface provides methods to interact with the User + * data. + * + * @author Tabnine + */ public interface UserRepository { + + /** + * Saves a new User object to the repository. + * + * @param newUser the User object to be saved + */ void save(User newUser); + /** + * Checks if a User with the given email already exists in the + * repository. + * + * @param email the email of the User to be checked + * @return true if a User with the given email already exists, + * false otherwise + */ boolean existByUserEmail(String email); } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/impl/GetPartOfImagesForAuthenticatedUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/user/impl/GetPartOfImagesForAuthenticatedUserImpl.java index bf6a572..d3eb2e8 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/impl/GetPartOfImagesForAuthenticatedUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/impl/GetPartOfImagesForAuthenticatedUserImpl.java @@ -10,12 +10,28 @@ import java.util.List; +/** + * Implementation of the {@link GetPartOfImagesForAuthenticatedUser} interface. + * This class provides methods to retrieve a part of + * images for an authenticated user. + * + * @author asavershin + */ @Query @RequiredArgsConstructor -public class GetPartOfImagesForAuthenticatedUserImpl implements GetPartOfImagesForAuthenticatedUser { +public class GetPartOfImagesForAuthenticatedUserImpl + implements GetPartOfImagesForAuthenticatedUser { + /** + * The ImageRepository dependency for fetching images. + */ private final ImageRepository imageRepository; + + /** + * Not final to allow spring use proxy. + */ @Override - public List get(UserId userId, PartOfResources partOfResources) { + public List get(final UserId userId, + final PartOfResources partOfResources) { return imageRepository.findImagesByUserId(userId, partOfResources); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/impl/RegisterNewUserImpl.java b/api/src/main/java/com/github/asavershin/api/domain/user/impl/RegisterNewUserImpl.java index 4a2984b..82fe96f 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/impl/RegisterNewUserImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/impl/RegisterNewUserImpl.java @@ -1,38 +1,53 @@ package com.github.asavershin.api.domain.user.impl; import com.github.asavershin.api.common.annotations.Command; +import com.github.asavershin.api.domain.user.Credentials; +import com.github.asavershin.api.domain.user.FullName; +import com.github.asavershin.api.domain.user.RegisterNewUser; +import com.github.asavershin.api.domain.user.User; +import com.github.asavershin.api.domain.user.UserId; +import com.github.asavershin.api.domain.user.UserRepository; import org.springframework.security.crypto.password.PasswordEncoder; -import com.github.asavershin.api.domain.user.*; -import com.github.asavershin.api.domain.user.RegisterNewUser; import lombok.RequiredArgsConstructor; @Command @RequiredArgsConstructor public class RegisterNewUserImpl implements RegisterNewUser { + /** + * Dependency that allow crud with User entity. + */ private final UserRepository userRepository; + /** + * is used to encode and decode passwords securely. + */ private final PasswordEncoder passwordEncoder; + /** + * Not final to allow spring use proxy. + */ @Override - public void register(FullName fullName, Credentials credentials) { + public void register(final FullName fullName, + final Credentials credentials) { checkEmailForUnique(credentials.email()); userRepository.save( new User( UserId.nextIdentity(), fullName, - new Credentials(credentials.email(), protectPassword(credentials.password())) + new Credentials(credentials.email(), + protectPassword(credentials.password())) ) ); } - private void checkEmailForUnique(String email) { + private void checkEmailForUnique(final String email) { if (userRepository.existByUserEmail(email)) { throw new IllegalArgumentException("Email is not unique"); } } - private String protectPassword(String unprotectedPassword) { + private String protectPassword(final String unprotectedPassword) { return passwordEncoder.encode(unprotectedPassword); } } diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/impl/TryToLoginImpl.java b/api/src/main/java/com/github/asavershin/api/domain/user/impl/TryToLoginImpl.java index be33cb2..3823a7d 100644 --- a/api/src/main/java/com/github/asavershin/api/domain/user/impl/TryToLoginImpl.java +++ b/api/src/main/java/com/github/asavershin/api/domain/user/impl/TryToLoginImpl.java @@ -13,17 +13,28 @@ @DomainService @RequiredArgsConstructor public class TryToLoginImpl extends IsEntityFound implements TryToLogin { - + /** + * The {@link PasswordEncoder} + * is used to encode and decode passwords securely. + */ private final PasswordEncoder passwordEncoder; + /** + * The {@link AuthenticatedUserRepository} is used + * to retrieve user data from the database. + */ private final AuthenticatedUserRepository authenticatedUserRepository; - + /** + * Not final to allow spring use proxy. + */ @Override - public AuthenticatedUser login(Credentials credentials) { + public AuthenticatedUser login(final Credentials credentials) { - var authenticatedUser = authenticatedUserRepository.findByEmail(credentials.email()); + var authenticatedUser = authenticatedUserRepository + .findByEmail(credentials.email()); - isEntityFound(authenticatedUser,"User", "email", credentials.email()); - if(passwordEncoder.matches(credentials.password(), authenticatedUser.userCredentials().password())) { + isEntityFound(authenticatedUser, "User", "email", credentials.email()); + if (passwordEncoder.matches(credentials.password(), + authenticatedUser.userCredentials().password())) { return authenticatedUser; } throw new AuthException("Wrong password"); diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/impl/package-info.java b/api/src/main/java/com/github/asavershin/api/domain/user/impl/package-info.java new file mode 100644 index 0000000..a591eda --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/user/impl/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains implementations of domain services, + * queries, commands for user entity. + * + * @author asavershin + */ +package com.github.asavershin.api.domain.user.impl; diff --git a/api/src/main/java/com/github/asavershin/api/domain/user/package-info.java b/api/src/main/java/com/github/asavershin/api/domain/user/package-info.java new file mode 100644 index 0000000..859ec6c --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/domain/user/package-info.java @@ -0,0 +1,6 @@ +/** + * This package contains the domain model for the user entity. + * + * @author asavershin + */ +package com.github.asavershin.api.domain.user; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AdviceController.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AdviceController.java index 8b4fa5b..eb5e5e7 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AdviceController.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AdviceController.java @@ -7,69 +7,117 @@ import com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.UISuccessContainer; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpStatus; import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; import java.util.Objects; @RestControllerAdvice -@Slf4j public class AdviceController { - + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler({ResourceOwnershipException.class, AuthException.class}) @ResponseStatus(HttpStatus.UNAUTHORIZED) - public UISuccessContainer handleValidationException(RuntimeException ex) { + public UISuccessContainer handleValidationException( + final RuntimeException ex + ) { return new UISuccessContainer(false, ex.getMessage()); } + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler({NotFoundException.class}) @ResponseStatus(HttpStatus.NO_CONTENT) - public UISuccessContainer handleNotFoundException(RuntimeException ex) { + public UISuccessContainer handleNotFoundException( + final RuntimeException ex + ) { return new UISuccessContainer(false, ex.getMessage()); } + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler({Exception.class}) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public UISuccessContainer handleInnerException(Exception ex) { + public UISuccessContainer handleInnerException(final Exception ex) { + ex.printStackTrace(); return new UISuccessContainer(false, "Very bad exception"); } + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler({IllegalArgumentException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) - public UISuccessContainer handleIllegalImageExtension(IllegalArgumentException ex) { - log.info("IllegaArgumentException: " + ex); + public UISuccessContainer handleIllegalImageExtension( + final IllegalArgumentException ex + ) { return new UISuccessContainer(false, ex.getMessage()); } + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ExceptionBody handleValidationException(MethodArgumentNotValidException ex) { - log.info("handleValidationException"); + public ExceptionBody handleValidationException( + final MethodArgumentNotValidException ex + ) { var exceptionBody = new ExceptionBody("Validation failed: "); Map body = new HashMap<>(); body.put("errors", ex.getBindingResult().getAllErrors().stream() - .map(DefaultMessageSourceResolvable::getDefaultMessage).filter(Objects::nonNull) + .map( + DefaultMessageSourceResolvable + ::getDefaultMessage + ) + .filter(Objects::nonNull) .toList()); exceptionBody.setErrors(body); return exceptionBody; } + /** + * Not final to allows Spring use proxy. + * + * @param ex is handled exception + * @return Exception representation for user. + */ @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ExceptionBody handleConstraintViolationException(ConstraintViolationException ex) { - log.info("handleConstraintViolationException"); + public ExceptionBody handleConstraintViolationException( + final ConstraintViolationException ex + ) { var exceptionBody = new ExceptionBody("Validation failed: "); Map body = new HashMap<>(); body.put("errors", ex.getConstraintViolations().stream() - .map(ConstraintViolation::getMessage).filter(Objects::nonNull) + .map(ConstraintViolation::getMessage) + .filter(Objects::nonNull) .toList()); exceptionBody.setErrors(body); return exceptionBody; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AuthController.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AuthController.java index 35c3cf6..e654795 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AuthController.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/AuthController.java @@ -11,7 +11,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -23,30 +22,56 @@ @Tag(name = "auth", description = "Аутентификация и регистрация") @RequiredArgsConstructor public class AuthController { - + /** + * Dependency that allows to register new user. + */ private final RegisterNewUser register; + /** + * Dependency that allows to get new tokens during authentication. + */ private final GetNewCredentials getNewCredentials; - private final GetNewCredentialsUsingRefreshToken getNewCredentialsUsingRefreshToken; + /** + * Dependency that allows to get new tokens using refresh token. + */ + private final GetNewCredentialsUsingRefreshToken getRefreshToken; + /** + * Not final to allows Spring use proxy. + * @param request DTO that contains user data like email, password + * firstname, etc. + */ @PostMapping("/register") @Operation(description = "Регистрация нового пользователя") public void register( - @RequestBody @Valid UserRegistrationRequest userRegistrationRequest + final @RequestBody @Valid UserRegistrationRequest request ) { - register.register(userRegistrationRequest.ToFullName(), - userRegistrationRequest.toCredentials()); + register.register(request.toFullName(), + request.toCredentials()); } + /** + * Not final to allows Spring use proxy. + * @param userLoginRequest DTO that represents login, password. + * @return DTO that contains access, refresh tokens + */ @PostMapping("/login") @Operation(description = "Аутентификация пользователя") - public ApplicationCredentials login(@RequestBody @Valid UserLoginRequest userLoginRequest) { - + public ApplicationCredentials login( + final @RequestBody @Valid UserLoginRequest userLoginRequest + ) { return getNewCredentials.get(userLoginRequest.toCredentials()); } - + /** + * Not final to allows Spring use proxy. + * @param user Is param that injects by spring and contains + * current authenticated spring user. + * @return DTO that contains access, refresh tokens. + */ @PostMapping("/refresh-token") @Operation(description = "Использовать рефреш токен") - public ApplicationCredentials refreshToken(@AuthenticationPrincipal CustomUserDetails user) { - return getNewCredentialsUsingRefreshToken.get(user.authenticatedUser().userCredentials()); + public ApplicationCredentials refreshToken( + final @AuthenticationPrincipal CustomUserDetails user + ) { + return getRefreshToken.get(user.authenticatedUser().userCredentials()); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/ImageController.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/ImageController.java index cfc5229..88d7211 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/ImageController.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/ImageController.java @@ -11,9 +11,14 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.util.UUID; @@ -23,47 +28,99 @@ @Tag(name = "image", description = "Работа с изображениями") @RequiredArgsConstructor public class ImageController { - + /** + * Domain query that allows you take images of specific user. + */ private final GetPartOfImagesForAuthenticatedUser getImages; + /** + * Application service that contains logic for images get, post, delete. + * It contains some others dependencies for databases and jwt service. + */ private final ImageService imageService; + /** + * Not final to allows Spring use proxy. + * + * @param user Is param that injects by spring and contains + * current authenticated spring user. + * @param pageNumber Page number for pagination in DB + * @param pageSize Page size for pagination in DB + * @return List of images for specific user. + */ @GetMapping("/images") @Operation(description = "Получить изображения пользователя") - public GetImagesResponse getImages(@AuthenticationPrincipal CustomUserDetails user, - Long pageNumber, - Long pageSize) { - return GetImagesResponse.GetImagesResponseFromImages( - getImages.get(user.authenticatedUser().userId(), new PartOfResources(pageNumber, pageSize)) + public GetImagesResponse getImages( + final @AuthenticationPrincipal CustomUserDetails user, + final Long pageNumber, + final Long pageSize + ) { + return GetImagesResponse.getImagesResponseFromImages( + getImages.get(user.authenticatedUser().userId(), + new PartOfResources(pageNumber, pageSize) + ) ); } + /** + * Not final to allows Spring use proxy. + * + * @param user Is param that injects by spring and contains + * current authenticated spring user. + * @param file Is image with data and metadata. + * @return ImageId of the new image + */ @PostMapping @Operation(description = "Загрузить новую картинку") public UploadImageResponse uploadImage( - @AuthenticationPrincipal CustomUserDetails user, - @RequestPart("file") MultipartFile file + final @AuthenticationPrincipal CustomUserDetails user, + final @RequestPart("file") MultipartFile file ) { - return new UploadImageResponse(imageService.storeImage(user.authenticatedUser().userId(), file)); + return new UploadImageResponse( + imageService.storeImage( + user.authenticatedUser().userId(), + file + ) + ); } + /** + * Not final to allows Spring use proxy. + * + * @param user Is param that injects by spring and contains + * current authenticated spring user. + * @param imageId The id of the image + * @return The response that contains status of response(true, false) + * and message for user about successful. + */ @DeleteMapping("/{image-id}") @Operation(description = "Удалить картинку") public UISuccessContainer deleteImage( - @AuthenticationPrincipal CustomUserDetails user, - @PathVariable("image-id") String imageId + final @AuthenticationPrincipal CustomUserDetails user, + final @PathVariable("image-id") String imageId ) { - imageService.deleteImageByImageId(user.authenticatedUser().userId(), new ImageId(UUID.fromString(imageId))); + imageService.deleteImageByImageId( + user.authenticatedUser().userId(), + new ImageId(UUID.fromString(imageId)) + ); return new UISuccessContainer( true, - "Image with id " + imageId +" deleted successfully" + "Image with id " + imageId + " deleted successfully" ); } + /** + * Not final to allows Spring use proxy. + * + * @param user Is param that injects by spring and contains + * current authenticated spring user. + * @param imageId The id of the image + * @return bytes of the image + */ @GetMapping("/{image-id}") @Operation(description = "Получить картинку по id") public byte[] downloadImage( - @AuthenticationPrincipal CustomUserDetails user, - @PathVariable("image-id") String imageId + final @AuthenticationPrincipal CustomUserDetails user, + final @PathVariable("image-id") String imageId ) { return imageService.downloadImage( new ImageId(UUID.fromString(imageId)), diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/ExceptionBody.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/ExceptionBody.java index 3c094b6..12b74e1 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/ExceptionBody.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/ExceptionBody.java @@ -10,14 +10,24 @@ @Setter @AllArgsConstructor public class ExceptionBody { - + /** + * Message for user about exception. + */ private String message; + /** + * Map of error for a lot of exceptions. + */ private Map errors; - + /** + * Constructs an instance of {@link ExceptionBody} + * with the provided message. + * + * @param aMessage Message for user about exception. + */ public ExceptionBody( - final String message + final String aMessage ) { - this.message = message; + this.message = aMessage; } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/UISuccessContainer.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/UISuccessContainer.java index f7ab7b7..c80f9a8 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/UISuccessContainer.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/UISuccessContainer.java @@ -6,6 +6,12 @@ @Getter @AllArgsConstructor public class UISuccessContainer { + /** + * Indicates whether the operation was successful. + */ private boolean success; + /** + * The message to be displayed to the user. + */ private String message; } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/GetImagesResponse.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/GetImagesResponse.java index 2b7d65a..7792a11 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/GetImagesResponse.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/GetImagesResponse.java @@ -1,22 +1,33 @@ package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.image; import com.github.asavershin.api.domain.image.Image; -import lombok.AllArgsConstructor; import lombok.Getter; import java.util.List; -import java.util.stream.Collectors; @Getter -public class GetImagesResponse { - private List images; - private GetImagesResponse(List images){ - this.images = images; +public final class GetImagesResponse { + /** + * List of metadata of images. + */ + private final List images; + + private GetImagesResponse(final List aImages) { + this.images = aImages; } - public static GetImagesResponse GetImagesResponseFromImages(List images) { + + /** + * Static fabric that map List of images to list of DTO. + * + * @param images List of images from domain + * @return Instance of GetImagesResponse + */ + public static GetImagesResponse getImagesResponseFromImages( + final List images + ) { return new GetImagesResponse( images.stream() - .map(ImageResponse::imageResponseFromEntity).toList() + .map(ImageResponse::imageResponseFromEntity).toList() ); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/ImageResponse.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/ImageResponse.java index 7b2a6a8..9600724 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/ImageResponse.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/ImageResponse.java @@ -4,18 +4,35 @@ import lombok.Getter; @Getter -public class ImageResponse { - private String ImageId; - private String ImageName; - private Long imageSize; +public final class ImageResponse { + /** + * UUID of the image. + */ + private final String imageId; + /** + * Image name in format "name.extension". + */ + private final String imageName; + /** + * Count of image bytes. + */ + private final Long imageSize; - private ImageResponse(String ImageId, String ImageName, Long imageSize){ - this.ImageId = ImageId; - this.ImageName = ImageName; - this.imageSize = imageSize; + private ImageResponse(final String aImageId, + final String aImageName, + final Long aImageSize) { + this.imageId = aImageId; + this.imageName = aImageName; + this.imageSize = aImageSize; } - public static ImageResponse imageResponseFromEntity(Image image){ + /** + * Static fabric for creating ImageResponse from image entity. + * + * @param image is domain entity + * @return DTO + */ + public static ImageResponse imageResponseFromEntity(final Image image) { return new ImageResponse( image.imageId().value().toString(), image.metaInfo().imageNameWithExtension().toString(), diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/UploadImageResponse.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/UploadImageResponse.java index 165a742..3e05d1a 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/UploadImageResponse.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/UploadImageResponse.java @@ -1,13 +1,18 @@ package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.image; import com.github.asavershin.api.domain.image.ImageId; -import lombok.AllArgsConstructor; import lombok.Getter; @Getter public class UploadImageResponse { - private String imageId; - public UploadImageResponse(ImageId imageId){ - this.imageId = imageId.value().toString(); + /** + * UUID of the image. + */ + private final String imageId; + /** + * @param aImageId UUID of the image + */ + public UploadImageResponse(final ImageId aImageId) { + this.imageId = aImageId.value().toString(); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/package-info.java new file mode 100644 index 0000000..d513554 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/image/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains image DTO's for HTTP reuqests/respons. + * @author asavershin + */ +package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.image; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/package-info.java new file mode 100644 index 0000000..417722c --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains DTO's for HTTP reuqests/respons. + * @author asavershin + */ +package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserLoginRequest.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserLoginRequest.java index d3ca761..03abb31 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserLoginRequest.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserLoginRequest.java @@ -10,15 +10,30 @@ @Setter @Getter public class UserLoginRequest { + /** + * Minimum password length. + */ + private static final int MIN_PASSWORD_LENGTH = 8; + /** + * User email using as login. + */ @NotEmpty(message = "Не заполнен email") @Email(message = "Некорректная почта") private String userEmail; + /** + * User password. + */ @NotEmpty(message = "Не заполнен пароль") - @Size(min = 8, message = "Длина пароля должна быть не менее 8") + @Size(min = MIN_PASSWORD_LENGTH, + message = "Длина пароля должна быть не менее 8") private String userPassword; - public Credentials toCredentials() { + /** + * Fabric using for mapping DTO to credentials. + * @return User credentials + */ + public final Credentials toCredentials() { return new Credentials(userEmail, userPassword); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserRegistrationRequest.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserRegistrationRequest.java index 0165581..55132c4 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserRegistrationRequest.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/UserRegistrationRequest.java @@ -1,6 +1,7 @@ package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.user; +import com.github.asavershin.api.config.properties.UserProperties; import com.github.asavershin.api.domain.user.Credentials; import com.github.asavershin.api.domain.user.FullName; import jakarta.validation.constraints.Email; @@ -12,28 +13,51 @@ @Setter public class UserRegistrationRequest { + + /** + * User firstname. + */ @NotEmpty(message = "Не заполнено имя") - @Size(min = 1, max = 20, message = "Недопустимая длина имени") + @Size(min = 1, max = UserProperties.MAX_FIRSTNAME_LENGTH, + message = "Недопустимая длина имени") private String userFirstname; + /** + * User lastname. + */ @NotEmpty(message = "Не заполнена фамилия") - @Size(min = 1, max = 20, message = "Недопустимая длина фамилии") + @Size(min = 1, max = UserProperties.MAX_LASTNAME_LENGTH, + message = "Недопустимая длина фамилии") private String userLastname; - + /** + * User email using as login. + */ @NotEmpty(message = "Не заполнен email") @Email(message = "Некорректная почта") @Getter private String userEmail; + /** + * User password. + */ @NotEmpty(message = "Не заполнен пароль") - @Size(min = 8, message = "Длина пароля должна быть не менее 8") + @Size(min = UserProperties.MIN_PASSWORD_LENGTH, + message = "Длина пароля должна быть не менее 8") @Getter private String userPassword; - public FullName ToFullName() { + /** + * Fabric method to create FullName from DTO. + * @return FullName value object + */ + public FullName toFullName() { return new FullName(userFirstname, userLastname); } + /** + * Fabric method to create credentials. + * @return Credentials value object + */ public Credentials toCredentials() { return new Credentials(userEmail, userPassword); } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/package-info.java new file mode 100644 index 0000000..c81e2bd --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/dto/user/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains user DTO's for HTTP reuqests/respons. + * @author asavershin + */ +package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers.dto.user; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/package-info.java new file mode 100644 index 0000000..0b178e4 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/impl/controllers/controllers/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains controllers that handle http requests. + */ +package com.github.asavershin.api.infrastructure.in.impl.controllers.controllers; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/CustomUserDetails.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/CustomUserDetails.java index a14e57f..0a130ef 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/CustomUserDetails.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/CustomUserDetails.java @@ -7,7 +7,8 @@ import java.util.Collection; import java.util.List; -public record CustomUserDetails(AuthenticatedUser authenticatedUser) implements UserDetails { +public record CustomUserDetails(AuthenticatedUser authenticatedUser) + implements UserDetails { @Override public Collection getAuthorities() { diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/JwtAuthenticationFilter.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/JwtAuthenticationFilter.java index f41e747..81b6bea 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/JwtAuthenticationFilter.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/JwtAuthenticationFilter.java @@ -2,13 +2,12 @@ import com.github.asavershin.api.application.out.TokenRepository; import com.github.asavershin.api.application.in.services.user.JwtService; -import com.github.asavershin.api.domain.user.Credentials; +import com.github.asavershin.api.config.properties.JwtProperties; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; @@ -21,24 +20,37 @@ @Component @RequiredArgsConstructor -@Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { - + /** + * JwtService dependency injection. + * + * @see com.github.asavershin.api.application.in.services.user.JwtService + */ private final JwtService jwtService; + + /** + * UserDetailsService dependency injection. + * + * @see com.github.asavershin.api.application.out.TokenRepository + */ private final UserDetailsService userDetailsService; + + /** + * TokenRepository dependency injection. + * + * @see com.github.asavershin.api.application.out.TokenRepository + */ private final TokenRepository tokenRepository; @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain + protected final void doFilterInternal( + final @NonNull HttpServletRequest request, + final @NonNull HttpServletResponse response, + final @NonNull FilterChain filterChain ) throws ServletException, IOException { - log.info("Start JWT authentication filter"); var path = request.getServletPath(); if (path.contains("/register") || path.contains("/login")) { - log.info("REGISTER OR LOGIN"); filterChain.doFilter(request, response); return; } @@ -48,26 +60,22 @@ protected void doFilterInternal( filterChain.doFilter(request, response); return; } - jwt = authHeader.substring(7); + jwt = authHeader.substring(JwtProperties.START_OF_TOKEN); var email = jwtService.extractSub(jwt); String token; var pathContainsRefreshToken = path.contains("/refresh-token"); - if(pathContainsRefreshToken){ - log.info("Refresh token getting"); + if (pathContainsRefreshToken) { token = tokenRepository.getRefreshToken(email); - }else{ - log.info("Access token getting"); + } else { token = tokenRepository.getAccessToken(email); } - log.info("Token: " + token); - log.info("JWT: " + jwt); if (!token.equals(jwt)) { tokenRepository.deleteAllTokensByUserEmail(email); return; } - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + var authToken = new UsernamePasswordAuthenticationToken( userDetailsService.loadUserByUsername(email), null, null diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/LogautHandlerImpl.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/LogautHandlerImpl.java index 9808396..34eefa0 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/LogautHandlerImpl.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/LogautHandlerImpl.java @@ -2,6 +2,7 @@ import com.github.asavershin.api.application.out.TokenRepository; import com.github.asavershin.api.application.in.services.user.JwtService; +import com.github.asavershin.api.config.properties.JwtProperties; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -12,22 +13,34 @@ @Service @RequiredArgsConstructor public class LogautHandlerImpl implements LogoutHandler { - - private final TokenRepository tokenService; + /** + * TokenRepository dependency. + */ + private final TokenRepository tokenRepository; + /** + * JwtService dependency. + */ private final JwtService jwtService; + /** + * Not final to allow spring using proxy. + * + * @param request the HTTP request + * @param response the HTTP response + * @param authentication the current principal details + */ @Override public void logout( - HttpServletRequest request, - HttpServletResponse response, - Authentication authentication + final HttpServletRequest request, + final HttpServletResponse response, + final Authentication authentication ) { final String authHeader = request.getHeader("Authorization"); final String jwt; - if (authHeader == null ||!authHeader.startsWith("Bearer ")) { + if (authHeader == null || !authHeader.startsWith("Bearer ")) { return; } - jwt = authHeader.substring(7); - tokenService.deleteAllTokensByUserEmail("asd"); + jwt = authHeader.substring(JwtProperties.START_OF_TOKEN); + tokenRepository.deleteAllTokensByUserEmail(jwtService.extractSub(jwt)); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/SecurityConfiguration.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/SecurityConfiguration.java index a718077..b39bcdc 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/SecurityConfiguration.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/SecurityConfiguration.java @@ -3,7 +3,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -20,7 +19,9 @@ @RequiredArgsConstructor @EnableMethodSecurity public class SecurityConfiguration { - + /** + * Routes that will not be subject to spring security. + */ private static final String[] WHITE_LIST_URL = { "api/v1/auth/register", "api/v1/auth/login", @@ -35,11 +36,35 @@ public class SecurityConfiguration { "/webjars/**", "/swagger-ui.html", "/docs"}; + /** + * A reference to the JwtAuthenticationFilter instance. + */ private final JwtAuthenticationFilter jwtAuthFilter; + + /** + * A reference to the LogoutHandler instance. + */ private final LogoutHandler logoutHandler; + /** + * Creates a SecurityFilterChain instance, + * which is used to secure the application. + * It configures the security filter chain to disable CSRF protection, + * allow access to the specified URLs without authentication, + * and add the JwtAuthenticationFilter + * before the UsernamePasswordAuthenticationFilter. + * It also configures the logout functionality + * to use the specified logout handler and clear + * the security context after logout. + * + * @param http the HttpSecurity instance to configure + * @return the SecurityFilterChain instance + * @throws Exception if an error occurs during configuration + */ @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain( + final HttpSecurity http + ) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(req -> @@ -48,15 +73,22 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .anyRequest() .authenticated() ) - .sessionManagement(session -> session.sessionCreationPolicy(NEVER)) - .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) + .sessionManagement( + session -> session.sessionCreationPolicy(NEVER) + ) + .addFilterBefore( + jwtAuthFilter, + UsernamePasswordAuthenticationFilter.class + ) .logout(logout -> logout.logoutUrl("/auth/logout") .addLogoutHandler(logoutHandler) - .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext()) - ) - ; - + .logoutSuccessHandler( + (request, response, authentication) + -> SecurityContextHolder + .clearContext() + ) + ); return http.build(); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/UserDetailsServiceImpl.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/UserDetailsServiceImpl.java index cebe0f0..b34c970 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/UserDetailsServiceImpl.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/UserDetailsServiceImpl.java @@ -10,10 +10,30 @@ @Service @RequiredArgsConstructor -public class UserDetailsServiceImpl extends IsEntityFound implements UserDetailsService { +public class UserDetailsServiceImpl + extends IsEntityFound implements UserDetailsService { + /** + * The AuthenticatedUserRepository + * dependency is used to fetch the User entity from the database. + */ private final AuthenticatedUserRepository repository; + /** + * The loadUserByUsername method is an implementation + * of the UserDetailsService interface. + * It is responsible for loading a UserDetails object + * based on the provided username. + * Not final to allow spring use proxy. + * + * @param username The username of the User to be loaded. + * @return A UserDetails object + * representing the User with the provided username. + * @throws UsernameNotFoundException If the User with + * the provided username is not found. + */ @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + public UserDetails loadUserByUsername( + final String username + ) throws UsernameNotFoundException { var authenticatedUser = repository.findByEmail(username); isEntityFound(authenticatedUser, "User", "email", username); return new CustomUserDetails(authenticatedUser); diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/package-info.java new file mode 100644 index 0000000..3f89ddd --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/in/security/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains logic for security like filter chains, + * user details ant others. + */ +package com.github.asavershin.api.infrastructure.in.security; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/AuthenticatedUserRepositoryImpl.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/AuthenticatedUserRepositoryImpl.java index 3402186..e381f04 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/AuthenticatedUserRepositoryImpl.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/AuthenticatedUserRepositoryImpl.java @@ -14,10 +14,18 @@ @Repository @RequiredArgsConstructor -public class AuthenticatedUserRepositoryImpl implements AuthenticatedUserRepository, RecordMapper { +public class AuthenticatedUserRepositoryImpl + implements AuthenticatedUserRepository, + RecordMapper { + /** + * The DSLContext object is used to interact with the database. + */ private final DSLContext dslContext; + /** + * Not final to allow spring use proxy. + */ @Override - public AuthenticatedUser findByEmail(String email) { + public AuthenticatedUser findByEmail(final String email) { return dslContext.select( USERS.USER_ID, USERS.USER_EMAIL, @@ -28,12 +36,16 @@ public AuthenticatedUser findByEmail(String email) { .fetchOne(this); } - + /** + * Not final to allow spring use proxy. + */ @Override - public AuthenticatedUser map(Record record) { + public AuthenticatedUser map(final Record record) { var userId = record.get(USERS.USER_ID); var email = record.get(USERS.USER_EMAIL); var password = record.get(USERS.USER_PASSWORD); - return AuthenticatedUser.founded(new UserId(userId), new Credentials(email, password)); + return AuthenticatedUser.founded( + new UserId(userId), new Credentials(email, password) + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/CacheRepositoryIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/CacheRepositoryIml.java deleted file mode 100644 index 48ea8ba..0000000 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/CacheRepositoryIml.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.asavershin.api.infrastructure.out.persistence; - -import com.github.asavershin.api.application.out.CacheRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Repository; - -import java.util.concurrent.TimeUnit; - -@Repository -@RequiredArgsConstructor -public class CacheRepositoryIml implements CacheRepository { - private final RedisTemplate redisTemplate; - @Override - public void addCache(String key, String token, long expiration) { - redisTemplate.opsForValue().set(key, token, expiration, TimeUnit.MILLISECONDS); - } - - @Override - public String getCache(String key) { - return (String) redisTemplate.opsForValue().get(key); - } - - @Override - public void deleteCache(String key) { - redisTemplate.delete(key); - } -} diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/ImageRepositoryImpl.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/ImageRepositoryImpl.java index d462f00..3cddeba 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/ImageRepositoryImpl.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/ImageRepositoryImpl.java @@ -1,7 +1,11 @@ package com.github.asavershin.api.infrastructure.out.persistence; import com.github.asavershin.api.domain.PartOfResources; -import com.github.asavershin.api.domain.image.*; +import com.github.asavershin.api.domain.image.Image; +import com.github.asavershin.api.domain.image.ImageId; +import com.github.asavershin.api.domain.image.ImageNameWithExtension; +import com.github.asavershin.api.domain.image.ImageRepository; +import com.github.asavershin.api.domain.image.MetaData; import com.github.asavershin.api.domain.user.UserId; import lombok.RequiredArgsConstructor; import org.jooq.DSLContext; @@ -16,17 +20,28 @@ @Repository @RequiredArgsConstructor -public class ImageRepositoryImpl implements ImageRepository, RecordMapper { +public class ImageRepositoryImpl + implements ImageRepository, RecordMapper { + /** + * The DSLContext object is used to interact with the database. + */ private final DSLContext dslContext; + /** + * Not final to allow spring use proxy. + */ @Override - public void save(Image image) { + public void save(final Image image) { dslContext.insertInto(IMAGE) .set(IMAGE.IMAGE_ID, image.imageId().value()) - .set(IMAGE.IMAGE_NAME, image.metaInfo().imageNameWithExtension().imageName()) + .set( + IMAGE.IMAGE_NAME, + image.metaInfo().imageNameWithExtension().imageName() + ) .set(IMAGE.IMAGE_SIZE, image.metaInfo().imageSize()) .set(IMAGE.IMAGE_EXTENSION, - image.metaInfo().imageNameWithExtension().imageExtension().toString()) + image.metaInfo().imageNameWithExtension() + .imageExtension().toString()) .execute(); dslContext.insertInto(USER_IMAGES) .set(USER_IMAGES.IMAGE_ID, image.imageId().value()) @@ -34,34 +49,44 @@ public void save(Image image) { .execute(); } + /** + * Not final to allow spring use proxy. + */ @Override - public List findImagesByUserId(UserId userId, PartOfResources partOfResources) { + public List findImagesByUserId(final UserId userId, + final PartOfResources page) { return dslContext.select(IMAGE.fields()).select(USER_IMAGES.USER_ID) .from(IMAGE) .join(USER_IMAGES).using(IMAGE.IMAGE_ID) - .offset(partOfResources.pageNumber() * partOfResources.pageSize()) - .limit(partOfResources.pageSize()) + .offset(page.pageNumber() * page.pageSize()) + .limit(page.pageSize()) .fetch(this); } - + /** + * Not final to allow spring use proxy. + */ @Override - public Image findImageByImageId(ImageId imageId) { + public Image findImageByImageId(final ImageId imageId) { return dslContext.select(IMAGE.fields()).select(USER_IMAGES.USER_ID) .from(IMAGE) .join(USER_IMAGES).using(IMAGE.IMAGE_ID) .where(IMAGE.IMAGE_ID.eq(imageId.value())) .fetchOne(this); } - + /** + * Not final to allow spring use proxy. + */ @Override - public void deleteImageByImageId(Image imageId) { + public void deleteImageByImageId(final Image imageId) { dslContext.deleteFrom(IMAGE) .where(IMAGE.IMAGE_ID.eq(imageId.imageId().value())) .execute(); } - + /** + * Not final to allow spring use proxy. + */ @Override - public Image map(Record record) { + public Image map(final Record record) { var imageId = record.get(IMAGE.IMAGE_ID); var imageName = record.get(IMAGE.IMAGE_NAME); var userId = record.get(USER_IMAGES.USER_ID); @@ -69,7 +94,13 @@ public Image map(Record record) { var imageExtension = record.get(IMAGE.IMAGE_EXTENSION); return new Image( new ImageId(imageId), - new MetaInfo(ImageNameWithExtension.founded(imageName, imageExtension), imageSize), + new MetaData( + ImageNameWithExtension.founded( + imageName, + imageExtension + ), + imageSize + ), new UserId(userId) ); } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/TokenRepositoryIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/TokenRepositoryIml.java deleted file mode 100644 index 63095fd..0000000 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/TokenRepositoryIml.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.asavershin.api.infrastructure.out.persistence; - -import com.github.asavershin.api.application.out.CacheRepository; -import com.github.asavershin.api.application.out.TokenRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class TokenRepositoryIml implements TokenRepository { - private final String refreshKey = "REFRESH_TOKEN_"; - private final String accessKey = "ACCESS_TOKEN_"; - private final CacheRepository cacheRepository; - - @Override - public String getAccessToken(String email){ - return cacheRepository.getCache(accessKey + email); - } - - @Override - public String getRefreshToken(String email){ - return cacheRepository.getCache(refreshKey + email); - } - - @Override - public void saveRefreshToken(String username, String jwtToken, Long expiration) { - cacheRepository.addCache(refreshKey + username, jwtToken, expiration); - } - - @Override - public void saveAccessToken(String username, String jwtToken, Long expiration) { - cacheRepository.addCache(accessKey + username, jwtToken, expiration); - } - - @Override - public void deleteAllTokensByUserEmail(String username) { - cacheRepository.deleteCache(refreshKey + username); - cacheRepository.deleteCache(accessKey+ username); - } -} diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/UserRepositoryImpl.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/UserRepositoryImpl.java index a20bac2..4b43728 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/UserRepositoryImpl.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/UserRepositoryImpl.java @@ -12,11 +12,16 @@ @Repository @RequiredArgsConstructor public class UserRepositoryImpl implements UserRepository { - + /** + * The DSLContext object is used to interact with the database. + */ private final DSLContext dslContext; + /** + * Not final to allow spring use proxy. + */ @Override - public void save(User newUser) { + public void save(final User newUser) { UsersRecord userRecord = dslContext.newRecord(USERS); userRecord.setUserId(newUser.userId().value()); userRecord.setUserFirstname(newUser.userCredentials().email()); @@ -28,9 +33,13 @@ public void save(User newUser) { .set(userRecord) .execute(); } - + /** + * Not final to allow spring use proxy. + */ @Override - public boolean existByUserEmail(String email) { - return dslContext.fetchExists(USERS, USERS.USER_EMAIL.eq(email)); + public boolean existByUserEmail(final String email) { + return dslContext.fetchExists( + USERS, USERS.USER_EMAIL.eq(email) + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/package-info.java new file mode 100644 index 0000000..5ee66da --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/persistence/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains SQL repositories. + * @author asavershin + */ +package com.github.asavershin.api.infrastructure.out.persistence; diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/CacheRepositoryIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/CacheRepositoryIml.java new file mode 100644 index 0000000..51e0422 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/CacheRepositoryIml.java @@ -0,0 +1,45 @@ +package com.github.asavershin.api.infrastructure.out.storage; + +import com.github.asavershin.api.application.out.CacheRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import java.util.concurrent.TimeUnit; + +@Repository +@RequiredArgsConstructor +public class CacheRepositoryIml implements CacheRepository { + /** + * Redis template is used to interact with redis server. + */ + private final RedisTemplate redisTemplate; + + /** + * Not final to allow spring use proxy. + */ + @Override + public void addCache(final String key, + final String token, + final long expiration) { + redisTemplate.opsForValue().set( + key, token, expiration, TimeUnit.MILLISECONDS + ); + } + + /** + * Not final to allow spring use proxy. + */ + @Override + public String getCache(final String key) { + return (String) redisTemplate.opsForValue().get(key); + } + + /** + * Not final to allow spring use proxy. + */ + @Override + public void deleteCache(final String key) { + redisTemplate.delete(key); + } +} diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/FileException.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/FileException.java index 9904ffe..e7c3567 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/FileException.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/FileException.java @@ -1,7 +1,12 @@ package com.github.asavershin.api.infrastructure.out.storage; public class FileException extends RuntimeException { - public FileException(String s) { + /** + * Constructs a new FileException with the specified detail message. + * + * @param s the detail message + */ + public FileException(final String s) { super(s); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java index 8eef78b..87454b2 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java @@ -3,8 +3,12 @@ import com.github.asavershin.api.application.out.MinioService; import com.github.asavershin.api.config.properties.MinIOProperties; -import io.minio.*; -import lombok.RequiredArgsConstructor; +import io.minio.BucketExistsArgs; +import io.minio.GetObjectArgs; +import io.minio.MakeBucketArgs; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import io.minio.RemoveObjectArgs; import lombok.SneakyThrows; import org.apache.commons.compress.utils.IOUtils; import org.springframework.stereotype.Service; @@ -16,20 +20,39 @@ @Service public class MinioServiceIml implements MinioService { + /** + * The MinioClient is used to interact with the MinIO server. + */ private final MinioClient minioClient; + /** + * The MinIO properties from .yml. + */ private final MinIOProperties minioProperties; - - public MinioServiceIml(MinioClient minioClient, MinIOProperties minioProperties) { - this.minioClient = minioClient; - this.minioProperties = minioProperties; + /** + * Constructor for {@link MinioServiceIml}. + * + * @param aMinioClient The {@link MinioClient} + * instance to interact with the MinIO server. + * @param aMinioProperties The {@link MinIOProperties} + * instance containing the configuration + * for the MinIO server. + */ + public MinioServiceIml(final MinioClient aMinioClient, + final MinIOProperties aMinioProperties) { + this.minioClient = aMinioClient; + this.minioProperties = aMinioProperties; createBucket(); } - + /** + * Not final to allow spring use proxy. + */ @Override public String saveFile(final MultipartFile image) { if (!bucketExists(minioProperties.getBucket())) { - throw new FileException("File upload failed: bucket does not exist"); + throw new FileException( + "File upload failed: bucket does not exist" + ); } if (image.isEmpty() || image.getOriginalFilename() == null) { @@ -46,14 +69,17 @@ public String saveFile(final MultipartFile image) { saveImage(inputStream, link); return link; } - + /** + * Not final to allow spring use proxy. + */ @Override public byte[] getFile(final String link) { if (link == null) { throw new FileException("File download failed: link is nullable"); } try { - return IOUtils.toByteArray(minioClient.getObject(GetObjectArgs.builder() + return IOUtils.toByteArray( + minioClient.getObject(GetObjectArgs.builder() .bucket(minioProperties.getBucket()) .object(link) .build())); @@ -61,7 +87,9 @@ public byte[] getFile(final String link) { throw new FileException("File download failed: " + e.getMessage()); } } - + /** + * Not final to allow spring use proxy. + */ @Override public void deleteFiles(final List links) { if (links == null || links.isEmpty()) { @@ -112,7 +140,9 @@ private String generateFileName() { } @SneakyThrows(Exception.class) - private boolean bucketExists(String bucketName) { - return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + private boolean bucketExists(final String bucketName) { + return minioClient.bucketExists( + BucketExistsArgs.builder().bucket(bucketName).build() + ); } } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/TokenRepositoryIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/TokenRepositoryIml.java new file mode 100644 index 0000000..6344c09 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/TokenRepositoryIml.java @@ -0,0 +1,67 @@ +package com.github.asavershin.api.infrastructure.out.storage; + +import com.github.asavershin.api.application.out.CacheRepository; +import com.github.asavershin.api.application.out.TokenRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class TokenRepositoryIml implements TokenRepository { + /** + * The prefix for access tokens in the cache. + */ + private final String accessKey = "ACCESS_TOKEN_"; + + /** + * The prefix for refresh tokens in the cache. + */ + private final String refreshKey = "REFRESH_TOKEN_"; + + /** + * CacheRepository instance for storing and retrieving tokens. + */ + private final CacheRepository cacheRepository; + + /** + * Not final to allow spring use proxy. + */ + @Override + public String getAccessToken(final String email) { + return cacheRepository.getCache(accessKey + email); + } + + /** + * Not final to allow spring use proxy. + */ + @Override + public String getRefreshToken(final String email) { + return cacheRepository.getCache(refreshKey + email); + } + /** + * Not final to allow spring use proxy. + */ + @Override + public void saveRefreshToken(final String username, + final String jwtToken, + final Long expiration) { + cacheRepository.addCache(refreshKey + username, jwtToken, expiration); + } + /** + * Not final to allow spring use proxy. + */ + @Override + public void saveAccessToken(final String username, + final String jwtToken, + final Long expiration) { + cacheRepository.addCache(accessKey + username, jwtToken, expiration); + } + /** + * Not final to allow spring use proxy. + */ + @Override + public void deleteAllTokensByUserEmail(final String username) { + cacheRepository.deleteCache(refreshKey + username); + cacheRepository.deleteCache(accessKey + username); + } +} diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/package-info.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/package-info.java new file mode 100644 index 0000000..a094d40 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/package-info.java @@ -0,0 +1,5 @@ +/** + * This package contains impl of noSQL storages. + * @author asavershin + */ +package com.github.asavershin.api.infrastructure.out.storage; diff --git a/api/src/main/java/com/github/asavershin/api/package-info.java b/api/src/main/java/com/github/asavershin/api/package-info.java new file mode 100644 index 0000000..a3a5220 --- /dev/null +++ b/api/src/main/java/com/github/asavershin/api/package-info.java @@ -0,0 +1,6 @@ +/** + * This package contains the API for the application. + * Implementation of a microservice that directly interacts with the user. + * @author asavershin + */ +package com.github.asavershin.api; diff --git a/api/src/test/java/com/github/asavershin/api/common/ImageHelper.java b/api/src/test/java/com/github/asavershin/api/common/ImageHelper.java index 004de07..6cd6c04 100644 --- a/api/src/test/java/com/github/asavershin/api/common/ImageHelper.java +++ b/api/src/test/java/com/github/asavershin/api/common/ImageHelper.java @@ -2,7 +2,7 @@ import com.github.asavershin.api.domain.image.ImageId; import com.github.asavershin.api.domain.image.ImageNameWithExtension; -import com.github.asavershin.api.domain.image.MetaInfo; +import com.github.asavershin.api.domain.image.MetaData; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; @@ -11,12 +11,12 @@ public class ImageHelper { public static ImageId imageId(){ return ImageId.nextIdentity(); } - public static MetaInfo metaInfo1(){ - return new MetaInfo(ImageNameWithExtension.fromOriginalFileName("image.jpg"), 1L); + public static MetaData metaInfo1(){ + return new MetaData(ImageNameWithExtension.fromOriginalFileName("image.jpg"), 1L); } - public static MetaInfo metaInfo3(){ - return new MetaInfo(ImageNameWithExtension.fromOriginalFileName("image3.jpg"), 3L); + public static MetaData metaInfo3(){ + return new MetaData(ImageNameWithExtension.fromOriginalFileName("image3.jpg"), 3L); } public static MultipartFile multipartFile1() { diff --git a/api/src/test/java/com/github/asavershin/api/domaintest/ImageTest.java b/api/src/test/java/com/github/asavershin/api/domaintest/ImageTest.java index 24d597c..0790394 100644 --- a/api/src/test/java/com/github/asavershin/api/domaintest/ImageTest.java +++ b/api/src/test/java/com/github/asavershin/api/domaintest/ImageTest.java @@ -5,7 +5,7 @@ import com.github.asavershin.api.domain.ResourceOwnershipException; import com.github.asavershin.api.domain.image.Image; import com.github.asavershin.api.domain.image.ImageId; -import com.github.asavershin.api.domain.image.MetaInfo; +import com.github.asavershin.api.domain.image.MetaData; import com.github.asavershin.api.domain.user.UserId; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -16,7 +16,7 @@ public class ImageTest { void testAddNewImage() { //Given ImageId imageId = ImageHelper.imageId(); - MetaInfo metaInfo = ImageHelper.metaInfo1(); + MetaData metaInfo = ImageHelper.metaInfo1(); UserId userId = UserHelper.UserId(); //When @@ -32,7 +32,7 @@ void testAddNewImage() { void testFoundedImage() { //Given ImageId imageId = ImageHelper.imageId(); - MetaInfo metaInfo = ImageHelper.metaInfo1(); + MetaData metaInfo = ImageHelper.metaInfo1(); UserId userId = UserHelper.UserId(); // When Image image = new Image(imageId, metaInfo, userId); @@ -46,7 +46,7 @@ void testFoundedImage() { void testBelongsToUser() { // Given ImageId imageId = ImageHelper.imageId(); - MetaInfo metaInfo = ImageHelper.metaInfo1(); + MetaData metaInfo = ImageHelper.metaInfo1(); UserId userId = UserHelper.UserId(); UserId otherUserId = UserHelper.UserId(); @@ -66,10 +66,10 @@ void testEquals() { var imageId = ImageId.nextIdentity(); var userId = UserId.nextIdentity(); - MetaInfo metaInfo1 = ImageHelper.metaInfo1(); + MetaData metaInfo1 = ImageHelper.metaInfo1(); Image image1 = new Image(imageId, metaInfo1, userId); - MetaInfo metaInfo2 = ImageHelper.metaInfo1(); + MetaData metaInfo2 = ImageHelper.metaInfo1(); Image image2 = new Image(imageId, metaInfo2, userId); assertTrue(image1.equals(image2)); @@ -80,7 +80,7 @@ void testEquals() { assertFalse(image1.equals(null)); ImageId imageId3 = ImageId.nextIdentity(); - MetaInfo metaInfo3 = ImageHelper.metaInfo3(); + MetaData metaInfo3 = ImageHelper.metaInfo3(); UserId userId3 = UserHelper.UserId(); Image image3 = new Image(imageId3, metaInfo3, userId3); diff --git a/api/src/test/java/com/github/asavershin/api/integrations/ImageLogicTest.java b/api/src/test/java/com/github/asavershin/api/integrations/ImageLogicTest.java index a1ca5f8..48b77a0 100644 --- a/api/src/test/java/com/github/asavershin/api/integrations/ImageLogicTest.java +++ b/api/src/test/java/com/github/asavershin/api/integrations/ImageLogicTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @@ -78,6 +79,8 @@ public void clearDB(){ e.printStackTrace(); } } + @Autowired + private ApplicationContext applicationContext; @Test public void storeImageTest(){ @@ -91,6 +94,8 @@ public void storeImageTest(){ var user = authenticatedUserRepository.findByEmail(credentials.email()); // When var imageId = imageService.storeImage(user.userId(), ImageHelper.multipartFile1()); + log.info("asdasd"); + log.info(applicationContext.getBean(ImageService.class, "foo").getClass().toString()); // Then var image = imageRepository.findImageByImageId(imageId); @@ -117,6 +122,7 @@ public void storeImageWithIllegalExtensionTest(){ registerNewUser.register(fullName,credentials); var user = authenticatedUserRepository.findByEmail(credentials.email()); + assertNotNull(user); // When var ex = assertThrows(IllegalArgumentException.class, () -> imageService.storeImage(user.userId(), ImageHelper.multipartFileWithIllegalException())); From efa5e0c53bf83f948a3fad82a07e5b82900edfce Mon Sep 17 00:00:00 2001 From: asavershin Date: Thu, 4 Apr 2024 21:23:33 +0300 Subject: [PATCH 2/8] Add checkstyle in pom --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index a9fba13..ebe7e31 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ 22 UTF-8 1.18.30 + 3.3.1 @@ -46,4 +47,14 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + + From c51666e5f6466d1ecb2ca722cd76c725148afd14 Mon Sep 17 00:00:00 2001 From: asavershin Date: Thu, 4 Apr 2024 21:29:20 +0200 Subject: [PATCH 3/8] Set up on 21 java, add action for compile --- .github/workflows/JavaMavenCI.yml | 24 ++++++++++++++++++++++++ api/Dockerfile | 2 +- api/pom.xml | 4 ++-- pom.xml | 4 ++-- 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/JavaMavenCI.yml diff --git a/.github/workflows/JavaMavenCI.yml b/.github/workflows/JavaMavenCI.yml new file mode 100644 index 0000000..889e948 --- /dev/null +++ b/.github/workflows/JavaMavenCI.yml @@ -0,0 +1,24 @@ +name: Java CI with Maven + +on: + push: + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Compile with maven + run: mvn clean compile + + - name: Update dependency graph + uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index fd4a080..79aace5 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:22 +FROM openjdk:21 WORKDIR /app diff --git a/api/pom.xml b/api/pom.xml index 38e9914..8fbee14 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -12,8 +12,8 @@ api - 22 - 22 + 21 + 21 UTF-8 3.19.3 2.1.0 diff --git a/pom.xml b/pom.xml index ebe7e31..8a09635 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,8 @@ - 22 - 22 + 21 + 21 UTF-8 1.18.30 3.3.1 From 1dee4afc522b790546ed7934cc8602cb7cddb44e Mon Sep 17 00:00:00 2001 From: asavershin Date: Thu, 4 Apr 2024 22:36:07 +0300 Subject: [PATCH 4/8] Update action --- .github/workflows/JavaMavenCI.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/JavaMavenCI.yml b/.github/workflows/JavaMavenCI.yml index 889e948..2adaf03 100644 --- a/.github/workflows/JavaMavenCI.yml +++ b/.github/workflows/JavaMavenCI.yml @@ -10,9 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@v4 - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' From 9ac81e9b95bdec87cb76fa11b67b144aea142aeb Mon Sep 17 00:00:00 2001 From: asavershin Date: Fri, 5 Apr 2024 11:29:07 +0200 Subject: [PATCH 5/8] Action for jacoco --- .github/workflows/JavaMavenCI.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/JavaMavenCI.yml b/.github/workflows/JavaMavenCI.yml index 2adaf03..82a23f5 100644 --- a/.github/workflows/JavaMavenCI.yml +++ b/.github/workflows/JavaMavenCI.yml @@ -1,7 +1,6 @@ name: Java CI with Maven on: - push: pull_request: jobs: @@ -22,4 +21,28 @@ jobs: run: mvn clean compile - name: Update dependency graph - uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 \ No newline at end of file + uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 + + testing: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Compile & Test with maven + run: mvn clean test + - name: Add coverage to PR + id: jacoco + uses: madrapps/jacoco-report@v1.6.1 + with: + paths: | + ${{ github.workspace }}/**/target/site/jacoco/*.xml + token: ${{ secrets.TEST_SECRET }} + min-coverage-overall: 50 \ No newline at end of file From 4a55d55dd34a90a1cf42fd8a9e06141a24dee82a Mon Sep 17 00:00:00 2001 From: asavershin Date: Fri, 5 Apr 2024 12:56:25 +0200 Subject: [PATCH 6/8] Add checkstyle & docker push image --- .github/workflows/JavaMavenCI.yml | 71 +++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/.github/workflows/JavaMavenCI.yml b/.github/workflows/JavaMavenCI.yml index 82a23f5..1688bcf 100644 --- a/.github/workflows/JavaMavenCI.yml +++ b/.github/workflows/JavaMavenCI.yml @@ -23,6 +23,21 @@ jobs: - name: Update dependency graph uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 + checkstyle: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Compile with maven + run: mvn checkstyle:check + testing: runs-on: ubuntu-latest permissions: @@ -36,8 +51,10 @@ jobs: java-version: '21' distribution: 'temurin' cache: maven - - name: Compile & Test with maven - run: mvn clean test + - name: Compile maven project + run: mvn clean compile + - name: Test maven project + run: mvn test - name: Add coverage to PR id: jacoco uses: madrapps/jacoco-report@v1.6.1 @@ -45,4 +62,52 @@ jobs: paths: | ${{ github.workspace }}/**/target/site/jacoco/*.xml token: ${{ secrets.TEST_SECRET }} - min-coverage-overall: 50 \ No newline at end of file + min-coverage-overall: 50 + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: jacoco-report + path: ${{ github.workspace }}/**/target/site/jacoco/ + + - name: Fail PR if overall coverage is less than 80% + if: ${{ steps.jacoco.outputs.coverage-overall < 50.0 }} + uses: actions/github-script@v6 + with: + script: | + core.setFailed('Overall coverage is less than 80%!') + + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - + name: Package + run: mvn clean package + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + - + name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./api/. + push: true + tags: asavershin/api:latest From 06016e56ad1056004535aa5635524311571dc87d Mon Sep 17 00:00:00 2001 From: asavershin Date: Fri, 5 Apr 2024 13:20:17 +0200 Subject: [PATCH 7/8] Update testing action --- .github/workflows/JavaMavenCI.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/JavaMavenCI.yml b/.github/workflows/JavaMavenCI.yml index 1688bcf..690e6e9 100644 --- a/.github/workflows/JavaMavenCI.yml +++ b/.github/workflows/JavaMavenCI.yml @@ -63,18 +63,19 @@ jobs: ${{ github.workspace }}/**/target/site/jacoco/*.xml token: ${{ secrets.TEST_SECRET }} min-coverage-overall: 50 + min-coverage-changed-files: 50 - name: Upload coverage report uses: actions/upload-artifact@v4 with: name: jacoco-report path: ${{ github.workspace }}/**/target/site/jacoco/ - - name: Fail PR if overall coverage is less than 80% + - name: Fail PR if overall coverage is less than 50% if: ${{ steps.jacoco.outputs.coverage-overall < 50.0 }} uses: actions/github-script@v6 with: script: | - core.setFailed('Overall coverage is less than 80%!') + core.setFailed('Overall coverage is less than 50%!') docker: runs-on: ubuntu-latest From 673ec012d50e685ca7d2c24c3ac1530973b64e50 Mon Sep 17 00:00:00 2001 From: asavershin Date: Sun, 7 Apr 2024 09:32:31 +0300 Subject: [PATCH 8/8] Save file using minio after postgresql --- .../in/services/image/impl/ImageServiceImpl.java | 7 ++----- .../asavershin/api/application/out/FileService.java | 9 +++++---- .../api/application/out/MinioService.java | 2 +- .../infrastructure/out/storage/MinioServiceIml.java | 13 +++---------- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java index 0dcfe82..eb2fc9a 100644 --- a/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java +++ b/api/src/main/java/com/github/asavershin/api/application/in/services/image/impl/ImageServiceImpl.java @@ -17,7 +17,6 @@ import java.io.Serializable; import java.util.List; -import java.util.UUID; @Service @RequiredArgsConstructor @@ -63,10 +62,7 @@ public ImageId storeImage(final UserId userId, ), multipartFile.getSize() ); - // TODO Why minio service store before postgreSQL? - var imageId = new ImageId( - UUID.fromString(minioService.saveFile(multipartFile)) - ); + var imageId = ImageId.nextIdentity(); storeImageOfUser.storeImageOfUser( new Image( imageId, @@ -74,6 +70,7 @@ public ImageId storeImage(final UserId userId, userId ) ); + minioService.saveFile(multipartFile, imageId.value().toString()); return imageId; } diff --git a/api/src/main/java/com/github/asavershin/api/application/out/FileService.java b/api/src/main/java/com/github/asavershin/api/application/out/FileService.java index 03d3d05..2691519 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/FileService.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/FileService.java @@ -2,14 +2,15 @@ import java.util.List; -public interface FileService { +public interface FileService { /** * Saves a file to the storage. * - * @param file The file object to be saved. - * @return The saved file object. + * @param file The file object to be saved. + * @param filename The name of the file to be saved. + * Must be unique. */ - T saveFile(K file); + void saveFile(K file, String filename); /** * Retrieves the content of a file from the storage. * diff --git a/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java b/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java index 5fb906e..93835cb 100644 --- a/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java +++ b/api/src/main/java/com/github/asavershin/api/application/out/MinioService.java @@ -2,5 +2,5 @@ import org.springframework.web.multipart.MultipartFile; -public interface MinioService extends FileService { +public interface MinioService extends FileService { } diff --git a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java index 87454b2..055f46b 100644 --- a/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java +++ b/api/src/main/java/com/github/asavershin/api/infrastructure/out/storage/MinioServiceIml.java @@ -16,7 +16,6 @@ import java.io.InputStream; import java.util.List; -import java.util.UUID; @Service public class MinioServiceIml implements MinioService { @@ -47,7 +46,7 @@ public MinioServiceIml(final MinioClient aMinioClient, * Not final to allow spring use proxy. */ @Override - public String saveFile(final MultipartFile image) { + public void saveFile(final MultipartFile image, final String filename) { if (!bucketExists(minioProperties.getBucket())) { throw new FileException( @@ -58,7 +57,6 @@ public String saveFile(final MultipartFile image) { if (image.isEmpty() || image.getOriginalFilename() == null) { throw new FileException("File must have name"); } - var link = generateFileName(); InputStream inputStream; try { inputStream = image.getInputStream(); @@ -66,8 +64,7 @@ public String saveFile(final MultipartFile image) { throw new FileException("File upload failed: " + e.getMessage()); } - saveImage(inputStream, link); - return link; + saveFile(inputStream, filename); } /** * Not final to allow spring use proxy. @@ -124,7 +121,7 @@ private void createBucket() { } @SneakyThrows - private void saveImage( + private void saveFile( final InputStream inputStream, final String fileName ) { @@ -135,10 +132,6 @@ private void saveImage( .build()); } - private String generateFileName() { - return UUID.randomUUID().toString(); - } - @SneakyThrows(Exception.class) private boolean bucketExists(final String bucketName) { return minioClient.bucketExists(