Skip to content

Commit

Permalink
Merge pull request #57 from nastiausenko/profiling
Browse files Browse the repository at this point in the history
Profiling
  • Loading branch information
IvanShalaev1990 authored Apr 21, 2024
2 parents 0798ab3 + 6e49651 commit 354fa66
Show file tree
Hide file tree
Showing 23 changed files with 105 additions and 58 deletions.
24 changes: 18 additions & 6 deletions docker/compose.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
services:
postgres:
image: 'postgres:latest'
environment:
- 'POSTGRES_DB=${PROD_DATABASE_USERNAME}'
- 'POSTGRES_PASSWORD=${PROD_DATABASE_PASSWORD}'
- 'POSTGRES_USER=${PROD_DATABASE_NAME}'
image: 'postgres:14'
container_name: prod_postgres
restart: always
ports:
- '${PROD_DATABASE_PORT}:5432'
# - '${PROD_DATABASE_PORT}:5432'
- "5432:5432"
environment:
# - 'POSTGRES_DB=${PROD_DATABASE_USERNAME}'
# - 'POSTGRES_PASSWORD=${PROD_DATABASE_PASSWORD}'
# - 'POSTGRES_USER=${PROD_DATABASE_NAME}'
POSTGRES_USER: user
POSTGRES_PASSWORD: StrongPass01
POSTGRES_DB: prodDb

volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
postgres_data:
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
* Main class for the URL Shortener application.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
public class CreateLinkRequest {
@UrlLongFormatValidator
private String longLink;
private String shortLinkName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
@AllArgsConstructor
@NoArgsConstructor
public class EditLinkContentRequest {
private UUID id;
private String oldShortLink;
private String newShortLink;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
Expand All @@ -20,6 +19,7 @@
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
Expand All @@ -38,6 +38,7 @@ public class LinkController {
private final LinkService linkService;
private final UserService userService;
private final LinkInfoDtoMapper linkDtoMapper;
private final ShortLinkGenerator linkGenerator;

/**
* Controller method for creating a new link.
Expand All @@ -63,7 +64,12 @@ public class LinkController {
public ResponseEntity<CreateLinkResponse> createLink(@RequestBody @Valid CreateLinkRequest createRequest) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findByEmail(authentication.getName());
String newShortUrl = generateShortLink();
String newShortUrl;
if (Objects.nonNull(createRequest.getShortLinkName())) {
newShortUrl = createRequest.getShortLinkName();
} else {
newShortUrl = linkGenerator.generate();
}
try {
linkService.save(
Link.builder()
Expand All @@ -82,16 +88,16 @@ public ResponseEntity<CreateLinkResponse> createLink(@RequestBody @Valid CreateL
/**
* Handles a request to delete a link.
*
* @param id the UUID of the link to delete
* @param shortLink the String short link of the link to delete
* @return a ResponseEntity containing the response object indicating the success of the deletion operation
* @throws ForbiddenException if the authenticated user does not have rights to delete the link
*/
@PostMapping("/delete")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Delete link by ID")
public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam UUID id) {
if (doesUserHaveRightsForLinkById(id)) {
linkService.deleteById(id);//TODO: add validations (id not null etc)
@Operation(summary = "Delete link by short link")
public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam String shortLink) {
if (doesUserHaveRightsForLinkByShortLink(shortLink)) {
linkService.deleteByShortLink(shortLink);//TODO: add validations (id not null etc)
return ResponseEntity.ok(new LinkModifyingResponse("ok"));
} else {
throw new ForbiddenException(OPERATION_FORBIDDEN_MSG);
Expand All @@ -101,7 +107,7 @@ public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam UUID id) {
/**
* Handles a request to edit the content of a link.
*
* @param request the request object containing the ID of the link and the new short link
* @param request the request object containing the old short link of the link and the new short link
* @return a ResponseEntity containing the response object indicating the success of the edit operation
* @throws ForbiddenException if the authenticated user does not have rights to edit the link
* @throws LinkStatusException if the status of the link is not ACTIVE
Expand All @@ -110,8 +116,8 @@ public ResponseEntity<LinkModifyingResponse> deleteLink(@RequestParam UUID id) {
@SecurityRequirement(name = "JWT")
@Operation(summary = "Edit link content")
public ResponseEntity<LinkModifyingResponse> editLinkContent(@RequestBody EditLinkContentRequest request) {
if (doesUserHaveRightsForLinkById(request.getId())) {
Link oldLink = linkService.findById(request.getId());
if (doesUserHaveRightsForLinkByShortLink(request.getOldShortLink())) {
Link oldLink = linkService.findByShortLink(request.getOldShortLink());
if (oldLink.getStatus() != LinkStatus.ACTIVE) {
throw new LinkStatusException();
}
Expand All @@ -126,17 +132,17 @@ public ResponseEntity<LinkModifyingResponse> editLinkContent(@RequestBody EditLi
/**
* Handles a request to refresh the expiration time of a link.
*
* @param id the UUID of the link to refresh
* @param shortLink the String shortLink of the link to refresh
* @return a ResponseEntity containing the response object indicating the success of the refresh operation
* @throws ForbiddenException if the authenticated user does not have rights to refresh the link
* @throws DeletedLinkException if the link is already deleted
*/
@PostMapping("/edit/refresh")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Refresh link expiration time")
public ResponseEntity<LinkModifyingResponse> refreshLink(@RequestParam UUID id) {
if (doesUserHaveRightsForLinkById(id)) {
Link oldLink = linkService.findById(id);
public ResponseEntity<LinkModifyingResponse> refreshLink(@RequestParam String shortLink) {
if (doesUserHaveRightsForLinkByShortLink(shortLink)) {
Link oldLink = linkService.findByShortLink(shortLink);
if (oldLink.getStatus() == LinkStatus.DELETED) {
throw new DeletedLinkException();
}
Expand All @@ -160,8 +166,8 @@ public ResponseEntity<LinkModifyingResponse> refreshLink(@RequestParam UUID id)
@SecurityRequirement(name = "JWT")
@Operation(summary = "Get link info")
public ResponseEntity<LinkInfoResponse> getInfoByShortLink(@RequestParam String shortLink) {
Link link = linkService.findByShortLink(shortLink);
if (doesUserHaveRightsForLinkById(link.getId())) {
if (doesUserHaveRightsForLinkByShortLink(shortLink)) {
Link link = linkService.findByShortLink(shortLink);
LinkInfoDto dto = linkDtoMapper.mapLinkToDto(link);
LinkInfoResponse response = new LinkInfoResponse(List.of(dto), "ok");
return ResponseEntity.ok(response);
Expand Down Expand Up @@ -207,24 +213,15 @@ public ResponseEntity<LinkStatisticsResponse> getLinksStatsForUser() {
return ResponseEntity.ok(new LinkStatisticsResponse(stats, "ok"));
}

/**
* Generates a new short link.
*
* @return a randomly generated short link of [A-Za-z0-9]
*/
private String generateShortLink() {
return RandomStringUtils.randomAlphanumeric(8);
} //TODO: extracted into a servicethis method into service.

/**
* Checks if the authenticated user has rights to perform operations on a given link.
*
* @param linkId the UUID of the link to check
* @param shortLink the String short link of the link to check
* @return true if the user has rights, false otherwise
*/
private boolean doesUserHaveRightsForLinkById(UUID linkId) { //TODO: may be transformed into @? and extracted into a servicethis method into service.
private boolean doesUserHaveRightsForLinkByShortLink(String shortLink) { //TODO: may be transformed into @? and extracted into a servicethis method into service.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UUID linkUserId = linkService.findById(linkId).getUser().getId();
UUID linkUserId = linkService.findByShortLink(shortLink).getUser().getId();
UUID currentUserId = userService.findByEmail(authentication.getName()).getId();
return linkUserId.equals(currentUserId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.linkurlshorter.urlshortener.link;

import com.linkurlshorter.urlshortener.user.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.linkurlshorter.urlshortener.link;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Service;

@Service
public class ShortLinkGenerator {

public String generate() {
return RandomStringUtils.randomAlphanumeric(8);
}
}
7 changes: 7 additions & 0 deletions src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#Datasource
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.flyway.locations=classpath:db/migration/prod,classpath:db/migration/dev
15 changes: 15 additions & 0 deletions src/main/resources/application-prod.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
##Datasource
#spring.datasource.url=jdbc:postgresql://localhost:5432/prodDb
#spring.datasource.driverClassName=org.postgresql.Driver
#spring.datasource.username=user
#spring.datasource.password=StrongPass01

#TODO: check if real postgres settings work on your pc

#DatasourceMock
spring.datasource.url=jdbc:h2:mem:testProddb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=AP
spring.datasource.password=pass

spring.flyway.locations=classpath:db/migration/prod
8 changes: 3 additions & 5 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ server.servlet.context-path=/url-shortener
##Plugins for displaying colour logs
spring.output.ansi.enabled=ALWAYS

# DataSource settings
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
#Need for testcontainer
#spring.sql.init.mode=always

Expand All @@ -22,3 +17,6 @@ spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

spring.docker.compose.enabled=false

# Profiling
spring.profiles.default=dev
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;

import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.Profile;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;


import static org.assertj.core.api.Assertions.assertThat;


class JwtUtilTest {
private JwtUtil jwtUtil;
private Authentication authentication;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void setUp() throws Exception {
"https://www.google.com",
"https://www.facebook.com"})
void createShortLinkWorksCorrectly(String url) throws Exception {
CreateLinkRequest createLinkRequest = new CreateLinkRequest(url);
CreateLinkRequest createLinkRequest = new CreateLinkRequest(url, null); //TODO: Artem has added null here to match the new method signature
mockMvc.perform(post(baseUrl + "create")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", token)
Expand All @@ -89,7 +89,7 @@ void createShortLinkWorksCorrectly(String url) throws Exception {
"https://www.google.com@",
"https://www.facebook.com%"})
void createShortLinkFailsWhenUrlIsInvalid(String url) throws Exception {
CreateLinkRequest createLinkRequest = new CreateLinkRequest(url);
CreateLinkRequest createLinkRequest = new CreateLinkRequest(url, null);
mockMvc.perform(post(baseUrl + "create")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void createLinkTest() throws Exception {
when(userService.findByEmail(any())).thenReturn(user);
when(linkService.save(any())).thenReturn(link);

CreateLinkRequest request = new CreateLinkRequest("https://www.example.com");
CreateLinkRequest request = new CreateLinkRequest("https://www.example.com", null); //TODO: Artem has added null here to match the new method signature

ResultActions resultActions = mockMvc.perform(post("/api/V1/link/create")
.contentType(MediaType.APPLICATION_JSON)
Expand All @@ -104,7 +104,7 @@ void createLinkTest() throws Exception {
@Test
@WithMockUser
void createLinkFailedTest() throws Exception {
CreateLinkRequest request = new CreateLinkRequest("https://www.example.com");
CreateLinkRequest request = new CreateLinkRequest("https://www.example.com", null);

when(userService.findByEmail(any())).thenReturn(user);
when(linkService.save(any())).thenThrow(new RuntimeException("Short link already exists"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Profile;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.Profile;

import java.time.LocalDateTime;
import java.util.*;
Expand Down Expand Up @@ -184,7 +185,7 @@ void findByNullShortLinkTest() {
*/
@Test
void findByShortLinkNotFoundTest() {
assertThatThrownBy(() -> linkService.deleteByShortLink("https://link/short"))
assertThatThrownBy(() -> linkService.deleteByShortLink("http://link/short"))
.isInstanceOf(NoLinkFoundByShortLinkException.class);
}

Expand Down
Loading

0 comments on commit 354fa66

Please sign in to comment.