From c39659ebe59f5646ca1ecaab2f2ccdba0fb2ce4f Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:17:42 +0300 Subject: [PATCH 01/11] Added findByExistUniqueLink method to find a unique link. --- .../urlshortener/link/LinkService.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java index 2e45d91..69921f1 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Service; import java.util.List; + import java.util.Objects; import java.util.UUID; @@ -125,6 +126,24 @@ public void deleteById(UUID id) { //TODO: needs test linkRepository.deleteById(id); } + /** + * Searches for a unique existing link by a short link. + * If an active link is found for the specified short link, returns that link. + * + * @param shortLink A string representing the short link to be searched. + * @return The active link found for the specified short link. + * @throws NoLinkFoundByShortLinkException If no link was found by the short link. + * @throws NullLinkPropertyException If the found link does not have the ACTIVE status. + */ + public Link findByExistUniqueLink(String shortLink) { + Link existingLink = linkRepository.findByShortLink(shortLink).orElseThrow(NoLinkFoundByShortLinkException::new); + if (existingLink.getStatus() == LinkStatus.ACTIVE) { + return existingLink; + } else { + throw new NullLinkPropertyException(); + } + } + /** * Throws a DeletedLinkException if the link has been marked as deleted. * From 37bc7860e654b151ec4a5c135fd2c38d95d604e6 Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:23:11 +0300 Subject: [PATCH 02/11] Added custom annotation UrlShortValidator with used to validate short URLs. --- .../urlshortener/link/UrlShortValidator.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidator.java diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidator.java b/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidator.java new file mode 100644 index 0000000..1ef347f --- /dev/null +++ b/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidator.java @@ -0,0 +1,40 @@ +package com.linkurlshorter.urlshortener.link; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; +/** + * An annotation {@link UrlShortValidator} used to validate short URLs. + * Can be applied to fields, methods, or other annotations. + * Uses the {@link UrlShortValidatorImpl} implementation to perform the validation. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Constraint(validatedBy = UrlShortValidatorImpl.class) +public @interface UrlShortValidator { + + /** + * The message to be used to inform about the failed validation. + * Default: "". + * + * @return Error message. + */ + String message() default ""; + + /** + * Groups to which this constraint belongs. Default: empty array. + * + * @return Constraint groups. + */ + Class[] groups() default {}; + + /** + * Parameters that can be used to configure the constraint. + * Default: an empty array. + * + * @return The parameters of the constraint. + */ + Class[] payload() default {}; +} From 90ff83de5954ad37c7e80bd4c1a8b8f92b28e4a2 Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:23:50 +0300 Subject: [PATCH 03/11] Added custom annotation UrlShortValidatorImpl with used to validate short URLs. --- .../link/UrlShortValidatorImpl.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidatorImpl.java diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidatorImpl.java b/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidatorImpl.java new file mode 100644 index 0000000..f8ca669 --- /dev/null +++ b/src/main/java/com/linkurlshorter/urlshortener/link/UrlShortValidatorImpl.java @@ -0,0 +1,46 @@ +package com.linkurlshorter.urlshortener.link; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; + +/** + * The UrlShortValidatorImpl class implements the {@link ConstraintValidator} interface for annotating an UrlShortValidator. + * Used to validate short links to ensure that they are unique and active. + * + * @author Vlas Pototskyi + */ +@RequiredArgsConstructor +public class UrlShortValidatorImpl implements ConstraintValidator { + private final LinkService linkService; + + /** + * Checks if the short link is unique and active. + * + * @param shortLink A string representing the short link to be validated. + * @param context The context to be validated. + * @return true if the short link is unique and active; false otherwise. + * @throws NullPointerException if the link is not found in the database. + */ + @Override + public boolean isValid(String shortLink, ConstraintValidatorContext context) { + Link link = linkService.findByShortLink(shortLink); + Objects.requireNonNull(link, "This link cannot be null!"); + + if (link.getStatus() == LinkStatus.INACTIVE) { + context.buildConstraintViolationWithTemplate("This link is inactive!") + .addConstraintViolation(); + return false; + } + + Link existLink = linkService.findByExistUniqueLink(shortLink); + if (existLink != null) { + context.buildConstraintViolationWithTemplate("This link already exists!") + .addConstraintViolation(); + return false; + } + return true; + } +} From 6e124e1cb60179e99d2207f2b2c2a09b05c2bfc1 Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:25:24 +0300 Subject: [PATCH 04/11] The EndTimeLinkValidator annotation has been added to check the link end time. --- .../link/EndTimeLinkValidator.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidator.java diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidator.java b/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidator.java new file mode 100644 index 0000000..48843fa --- /dev/null +++ b/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidator.java @@ -0,0 +1,44 @@ +package com.linkurlshorter.urlshortener.link; + + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +/** + * The {@link EndTimeLinkValidator} annotation is used to validate the link's end time. + * This annotation can be applied to fields, methods, or other annotations. + * It uses the UrlShortValidatorImpl implementation to perform the validation. + * + * @author Vlas Pototskyi + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Constraint(validatedBy = UrlShortValidatorImpl.class) +public @interface EndTimeLinkValidator { + + /** + * The message to be used to inform about the failed validation. + * Default: "Link time is not valid!". + * + * @return Error message. + */ + String message() default "Link time is not valid!"; + + /** + * Groups to which this constraint belongs. Default: empty array. + * + * @return Constraint groups. + */ + Class[] groups() default {}; + + /** + * Parameters that can be used to configure the constraint. + * Default: an empty array. + * + * @return The parameters of the constraint. + */ + Class[] payload() default {}; +} From b8ac5fbaff2de9f578262ce28e28e8fec36e0139 Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:27:07 +0300 Subject: [PATCH 05/11] Added the EndTimeLinkValidatorImpl class to implement custom annotation. --- .../link/EndTimeLinkValidatorImpl.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidatorImpl.java diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidatorImpl.java b/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidatorImpl.java new file mode 100644 index 0000000..7170f00 --- /dev/null +++ b/src/main/java/com/linkurlshorter/urlshortener/link/EndTimeLinkValidatorImpl.java @@ -0,0 +1,28 @@ +package com.linkurlshorter.urlshortener.link; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.time.LocalDateTime; + +/** + * The EndTimeLinkValidatorImpl class implements the {@link ConstraintValidator} interface + * for annotating an EndTimeLinkValidator. It is used to validate the LocalDateTime value to check + * that the time to which the link is valid is correct. + * + * @author Vlas Pototskyi + */ +public class EndTimeLinkValidatorImpl implements ConstraintValidator { + /** + * Checks if the time to which the reference is valid is greater than the current time. + * + * @param value LocalDateTime The value to be validated. + * @param context The context to validate. + * @return true if the time to which the reference is valid is greater than the current time; false otherwise. + */ + @Override + public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) { + LocalDateTime currentTime = LocalDateTime.now(); + return value.isAfter(currentTime); + } +} From f3701511b1645f22cdf644811ac9c626b22f4106 Mon Sep 17 00:00:00 2001 From: ArtemPoliakov Date: Wed, 17 Apr 2024 12:19:46 +0300 Subject: [PATCH 06/11] added link controller modifying methods, also reformatted some files to match checkstyle --- .../urlshortener/link/LinkController.java | 3 ++ .../urlshortener/link/LinkInfoResponse.java | 9 ++++++ .../link/LinkInfoResponseMapper.java | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponseMapper.java diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java index aecbf2e..90c12f2 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java @@ -18,6 +18,7 @@ import java.util.UUID; import java.util.stream.Collectors; + /** * Controller for Link-related operations such as create, delete, update and get info + statistics * @@ -36,6 +37,8 @@ public class LinkController { private final EntityManager entityManager; private final LinkInfoDtoMapper linkDtoMapper; + private final LinkInfoResponseMapper infoResponseMapper; + /** * Controller method for creating a new link. *

diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponse.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponse.java index 170c52a..92be514 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponse.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponse.java @@ -7,6 +7,15 @@ import java.util.List; +/** + * Represents a response containing information about a link. + * This class provides various properties related to a link, such as its ID, long link, + * short link, creation time, expiration time, usage statistics, and status. + * Instances of this class can be created using the provided builder pattern. + * + * @author Artem Poliakov + * @version 1.0 + */ @Data @AllArgsConstructor @NoArgsConstructor diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponseMapper.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponseMapper.java new file mode 100644 index 0000000..7fbc493 --- /dev/null +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkInfoResponseMapper.java @@ -0,0 +1,29 @@ +package com.linkurlshorter.urlshortener.link; + +import org.springframework.stereotype.Component; + +/** + * Mapper class responsible for mapping a Link entity to a LinkInfoResponse object. + *

+ * This mapper class provides a method to map a Link entity along with an optional error message + *

+ * to a LinkInfoResponse object. + * + * @author Artem Poliakov + * @version 1.0 + */ +@Component +public class LinkInfoResponseMapper { + public LinkInfoResponse mapLinkToResponse(Link link, String error) { + return LinkInfoResponse.builder() + .id(link.getId()) + .longLink(link.getLongLink()) + .shortLink(link.getShortLink()) + .createdTime(link.getCreatedTime()) + .expirationTime(link.getExpirationTime()) + .usageStatistics(link.getStatistics()) + .status(link.getStatus()) + .error(error) + .build(); + } +} From 69b0fca4816ff7f2d675cac7954362b8441abf70 Mon Sep 17 00:00:00 2001 From: ArtemPoliakov Date: Wed, 17 Apr 2024 12:19:46 +0300 Subject: [PATCH 07/11] added link controller modifying methods, also reformatted some files to match checkstyle --- .../urlshortener/link/LinkController.java | 49 +------------------ 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java index 90c12f2..08e6770 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkController.java @@ -13,10 +13,7 @@ import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; -import java.util.Comparator; -import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; /** @@ -35,7 +32,7 @@ public class LinkController { private final LinkService linkService; private final UserService userService; private final EntityManager entityManager; - private final LinkInfoDtoMapper linkDtoMapper; + private final LinkInfoResponseMapper infoResponseMapper; private final LinkInfoResponseMapper infoResponseMapper; @@ -140,50 +137,6 @@ public ResponseEntity refreshLink(@RequestParam UUID id) throw new ForbiddenException(OPERATION_FORBIDDEN_MSG); } } - //TODO: for info and all-links-info exclude all results of status DELETED! - @GetMapping("/info") - public ResponseEntity getInfoByShortLink(@RequestParam String shortLink) { - Link link = linkService.findByShortLink(shortLink); - if (doesUserHaveRightsForLinkById(link.getId())) { - LinkInfoDto dto = linkDtoMapper.mapLinkToDto(link); - LinkInfoResponse response = new LinkInfoResponse(List.of(dto), "ok"); - return ResponseEntity.ok(response); - } else { - throw new ForbiddenException(OPERATION_FORBIDDEN_MSG); - } - } - - @GetMapping("/all-links-info") - public ResponseEntity getAllLinksForUser() { - try { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - User requesterUser = userService.findByEmail(authentication.getName()); - List linksDto = linkService - .findAllByUser(requesterUser) - .stream() - .map(linkDtoMapper::mapLinkToDto) - .sorted(Comparator.comparing(LinkInfoDto::getUsageStatistics).reversed()) - .collect(Collectors.toList()); - return ResponseEntity.ok(new LinkInfoResponse(linksDto, "ok")); - } catch (Exception e) { - throw new InternalServerLinkException(); - } - - } - - //TODO: exclude all results of non-Active (or only of DELETED?) - @GetMapping("/url-usage-top-for-user") - public ResponseEntity getLinksStatsForUser() { - try { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - User requesterUser = userService.findByEmail(authentication.getName()); - List stats = linkService.getLinkUsageStatsByUserId(requesterUser.getId()); - stats.sort(Comparator.comparing(LinkStatisticsDto::getUsageStatistics).reversed()); - return ResponseEntity.ok(new LinkStatisticsResponse(stats, "ok")); - } catch (Exception e) { - throw new InternalServerLinkException(); - } - } /** * Generates a new short link. From ce57b64985fb6b15c22474c05c2cace0040d0dd8 Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:49:54 +0300 Subject: [PATCH 08/11] Swapped the service link --- .../urlshortener/link/LinkService.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java index 69921f1..9c98a94 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java @@ -125,6 +125,23 @@ public void deleteById(UUID id) { //TODO: needs test } linkRepository.deleteById(id); } + /** + * Searches for a unique existing link by a short link. + * If an active link is found for the specified short link, returns that link. + * + * @param shortLink A string representing the short link to be searched. + * @return The active link found for the specified short link. + * @throws NoLinkFoundByShortLinkException If no link was found by the short link. + * @throws NullLinkPropertyException If the found link does not have the ACTIVE status. + */ + public Link findByExistUniqueLink(String shortLink) { + Link existingLink = linkRepository.findByShortLink(shortLink).orElseThrow(NoLinkFoundByShortLinkException::new); + if (existingLink.getStatus() == LinkStatus.ACTIVE) { + return existingLink; + } else { + throw new NullLinkPropertyException(); + } + } /** * Searches for a unique existing link by a short link. From 5c7965eb362e64994397dcb370a734f19e71097b Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Wed, 17 Apr 2024 20:56:21 +0300 Subject: [PATCH 09/11] Small refactor code --- .../urlshortener/link/LinkService.java | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java index 9c98a94..04c6355 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java @@ -39,6 +39,7 @@ public Link save(Link link) { * @throws NullLinkPropertyException If the 'link' parameter is null. * @throws DeletedLinkException If the link has been marked as deleted. */ + public Link update(Link link) { if (Objects.isNull(link)) { throw new NullLinkPropertyException(); @@ -89,19 +90,20 @@ public Link findByShortLink(String shortLink) { return link; } - public List findAllByUser(User user){ - if(Objects.isNull(user)){ + public List findAllByUser(User user) { + if (Objects.isNull(user)) { throw new NullLinkPropertyException(); } return linkRepository.findAllByUser(user); } - public List getLinkUsageStatsByUserId(UUID userId){ - if(Objects.isNull(userId)){ + public List getLinkUsageStatsByUserId(UUID userId) { + if (Objects.isNull(userId)) { throw new NullLinkPropertyException(); } return linkRepository.getLinkUsageStatsForUser(userId); } + /** * Marks a link entity as deleted by its short link. * @@ -125,23 +127,6 @@ public void deleteById(UUID id) { //TODO: needs test } linkRepository.deleteById(id); } - /** - * Searches for a unique existing link by a short link. - * If an active link is found for the specified short link, returns that link. - * - * @param shortLink A string representing the short link to be searched. - * @return The active link found for the specified short link. - * @throws NoLinkFoundByShortLinkException If no link was found by the short link. - * @throws NullLinkPropertyException If the found link does not have the ACTIVE status. - */ - public Link findByExistUniqueLink(String shortLink) { - Link existingLink = linkRepository.findByShortLink(shortLink).orElseThrow(NoLinkFoundByShortLinkException::new); - if (existingLink.getStatus() == LinkStatus.ACTIVE) { - return existingLink; - } else { - throw new NullLinkPropertyException(); - } - } /** * Searches for a unique existing link by a short link. @@ -173,3 +158,4 @@ private void throwIfDeleted(Link link) { } } } + From eef8a3b5202ed2974133d917c9797f5bf66c56af Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Wed, 17 Apr 2024 21:37:50 +0300 Subject: [PATCH 10/11] Small refactor code --- .../java/com/linkurlshorter/urlshortener/link/LinkService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java index 04c6355..a2d20f2 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java @@ -5,7 +5,6 @@ import org.springframework.stereotype.Service; import java.util.List; - import java.util.Objects; import java.util.UUID; From 26f3c1824416628c2793ed014dd71c66f2d4491c Mon Sep 17 00:00:00 2001 From: Vlasosik <128188585+Vlasosik@users.noreply.github.com> Date: Wed, 17 Apr 2024 22:10:46 +0300 Subject: [PATCH 11/11] test --- .../java/com/linkurlshorter/urlshortener/link/LinkService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java index a2d20f2..20c4900 100644 --- a/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java +++ b/src/main/java/com/linkurlshorter/urlshortener/link/LinkService.java @@ -145,6 +145,7 @@ public Link findByExistUniqueLink(String shortLink) { } } + /** * Throws a DeletedLinkException if the link has been marked as deleted. *