Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom annotations and exception handling #34

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c39659e
Added findByExistUniqueLink method to find a unique link.
Vlasosik Apr 16, 2024
37bc786
Added custom annotation UrlShortValidator with used to validate short…
Vlasosik Apr 16, 2024
90ff83d
Added custom annotation UrlShortValidatorImpl with used to validate s…
Vlasosik Apr 16, 2024
6e124e1
The EndTimeLinkValidator annotation has been added to check the link …
Vlasosik Apr 16, 2024
b8ac5fb
Added the EndTimeLinkValidatorImpl class to implement custom annotation.
Vlasosik Apr 16, 2024
f370151
added link controller modifying methods, also reformatted some files …
ArtemPoliakov Apr 17, 2024
69b0fca
added link controller modifying methods, also reformatted some files …
ArtemPoliakov Apr 17, 2024
ce57b64
Swapped the service link
Vlasosik Apr 17, 2024
5c7965e
Small refactor code
Vlasosik Apr 17, 2024
eef8a3b
Small refactor code
Vlasosik Apr 17, 2024
26f3c18
test
Vlasosik Apr 17, 2024
3947d0e
Updated link repository and service, controller, added javadoc
ArtemPoliakov Apr 18, 2024
cf4b108
Merge pull request #44 from nastiausenko/link-controller-update
IvanShalaev1990 Apr 18, 2024
e0368c1
Updated a base URL path for link controller endpoints
egorsivenko Apr 18, 2024
9c016b4
Improved the security configuration for all existing endpoints
egorsivenko Apr 18, 2024
a96859f
Merge pull request #45 from nastiausenko/refactor/security-configuration
IvanShalaev1990 Apr 18, 2024
bb5256c
Added validation of the existing email address when changing email
egorsivenko Apr 18, 2024
3c17924
Returning jwtToken in response body after email change
egorsivenko Apr 18, 2024
e38795a
Merge pull request #46 from nastiausenko/refactor/user-controller
IvanShalaev1990 Apr 18, 2024
d7c6134
Added findByExistUniqueLink method to find a unique link.
Vlasosik Apr 16, 2024
6cb5f4f
Added custom annotation UrlShortValidator with used to validate short…
Vlasosik Apr 16, 2024
c6d0e75
Added custom annotation UrlShortValidatorImpl with used to validate s…
Vlasosik Apr 16, 2024
7eef236
The EndTimeLinkValidator annotation has been added to check the link …
Vlasosik Apr 16, 2024
9eecf97
Added the EndTimeLinkValidatorImpl class to implement custom annotation.
Vlasosik Apr 16, 2024
0a278c6
test
Vlasosik Apr 18, 2024
ee5c414
added link controller modifying methods, also reformatted some files …
ArtemPoliakov Apr 17, 2024
684f774
Swapped the service link
Vlasosik Apr 17, 2024
063d146
Small refactor code
Vlasosik Apr 17, 2024
b722f9a
test
Vlasosik Apr 17, 2024
88731af
Merge remote-tracking branch 'origin/custom-annotations-and-exception…
Vlasosik Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -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<EndTimeLinkValidator, LocalDateTime> {
/**
* 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);
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/linkurlshorter/urlshortener/link/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class Link {
@Column(name = "statistics")
private int statistics;
@Column(name = "status")
@Builder.Default
@Enumerated(EnumType.STRING)
private LinkStatus status;
private LinkStatus status = LinkStatus.ACTIVE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
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;


/**
* Controller for Link-related operations such as create, delete, update and get info + statistics
Expand All @@ -34,7 +32,9 @@ 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;

/**
* Controller method for creating a new link.
Expand Down Expand Up @@ -137,50 +137,6 @@ public ResponseEntity<LinkModifyingResponse> 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<LinkInfoResponse> 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<LinkInfoResponse> getAllLinksForUser() {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User requesterUser = userService.findByEmail(authentication.getName());
List<LinkInfoDto> 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<LinkStatisticsResponse> getLinksStatsForUser() {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User requesterUser = userService.findByEmail(authentication.getName());
List<LinkStatisticsDto> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* This mapper class provides a method to map a Link entity along with an optional error message
* <p>
* 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
import com.linkurlshorter.urlshortener.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
Expand All @@ -34,9 +30,10 @@ public interface LinkRepository extends JpaRepository<Link, UUID> {
*/
Optional<Link> findByShortLink(String shortLink);

List<Link> findAllByUser(User user);
@Query("SELECT l from Link l WHERE l.user.id = :userId AND l.status <> 'DELETED'")
List<Link> findAllByUserId(@Param(value = "userId") UUID userId);

@Query("SELECT new com.linkurlshorter.urlshortener.link.LinkStatisticsDto(l.id, l.shortLink, l.statistics)" +
" FROM Link l WHERE l.user.id = :userId")
" FROM Link l WHERE l.user.id = :userId AND l.status <> 'DELETED'")
List<LinkStatisticsDto> getLinkUsageStatsForUser(@Param(value = "userId") UUID userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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();
Expand Down Expand Up @@ -88,19 +89,20 @@ public Link findByShortLink(String shortLink) {
return link;
}

public List<Link> findAllByUser(User user){
if(Objects.isNull(user)){
public List<Link> findAllByUser(User user) {
if (Objects.isNull(user)) {
throw new NullLinkPropertyException();
}
return linkRepository.findAllByUser(user);
return linkRepository.findAllByUserId(userId);
}

public List<LinkStatisticsDto> getLinkUsageStatsByUserId(UUID userId){
if(Objects.isNull(userId)){
public List<LinkStatisticsDto> getLinkUsageStatsByUserId(UUID userId) {
if (Objects.isNull(userId)) {
throw new NullLinkPropertyException();
}
return linkRepository.getLinkUsageStatsForUser(userId);
}

/**
* Marks a link entity as deleted by its short link.
*
Expand All @@ -125,6 +127,25 @@ 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.
*
Expand All @@ -137,3 +158,4 @@ private void throwIfDeleted(Link link) {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
import lombok.NoArgsConstructor;

import java.util.UUID;

/**
* Data transfer object (DTO) for representing link statistics.
* This class encapsulates information about a link's ID, short link, and usage statistics.
*
* @author Artem Poliakov
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
import lombok.NoArgsConstructor;

import java.util.List;

/**
* Data transfer object (DTO) for representing a response containing statistics of links.
* This class encapsulates a list of {@link LinkStatisticsDto} objects representing link statistics,
* along with an optional error message.
*
* @author Artem Poliakov
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -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<? extends Payload>[] payload() default {};
}
Loading
Loading