Skip to content

Commit

Permalink
Merge recent changes in develop
Browse files Browse the repository at this point in the history
  • Loading branch information
ozdentarikcan committed Dec 11, 2024
2 parents e8c2a17 + 3870c66 commit 33f6690
Show file tree
Hide file tree
Showing 50 changed files with 1,618 additions and 431 deletions.
19 changes: 11 additions & 8 deletions .github/workflows/prod_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,37 @@ jobs:

# Build a Docker image of the application and tag the image with the $GITHUB_SHA.
- name: Build backend image
run: docker build -t registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7) ./backend
run: docker build -t registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7) ./backend

- name: Build web image
run: docker build -t registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7) ./frontend
run: docker build -t registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7) ./frontend

- name: Log in to DigitalOcean Container Registry with short-lived credentials
run: doctl registry login --expiry-seconds 1200

# Push the Docker image to registry

- name: Push backend image
run: docker push registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7)
run: docker push registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7)

- name: Push web image
run: docker push registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7)
run: docker push registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7)

# tag as latest

- name: Tag backend image
run: docker tag registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages/backend:latest
run: docker tag registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages-2/backend:latest

- name: Tag web image
run: docker tag registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages/web:latest
run: docker tag registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages-2/web:latest

# Push the Docker image to registry

- name: Push backend image
run: docker push registry.digitalocean.com/programming-languages/backend:latest
run: docker push registry.digitalocean.com/programming-languages-2/backend:latest

- name: Push web image
run: docker push registry.digitalocean.com/programming-languages/web:latest
run: docker push registry.digitalocean.com/programming-languages-2/web:latest

- name: Create deployment again to ensure both images are up to date
run: doctl apps create-deployment 58cc1048-2af2-42f8-a71f-5d1085113742
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,24 +114,24 @@ docker compose build

```bash
# for prod
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages/web:latest
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages/backend:latest
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages-2/web:latest
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages-2/backend:latest

# for staging
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages/web:staging
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages/backend:staging
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages-2/web:staging
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages-2/backend:staging
```

3. Push images to the registry.

```bash
# for prod
docker push registry.digitalocean.com/programming-languages/web:latest
docker push registry.digitalocean.com/programming-languages/backend:latest
docker push registry.digitalocean.com/programming-languages-2/web:latest
docker push registry.digitalocean.com/programming-languages-2/backend:latest

# for staging
docker push registry.digitalocean.com/programming-languages/web:staging
docker push registry.digitalocean.com/programming-languages/backend:staging
docker push registry.digitalocean.com/programming-languages-2/web:staging
docker push registry.digitalocean.com/programming-languages-2/backend:staging
```

This will trigger a deployment on the DigitalOcean backend.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ protected void configure() {
skip(destination.getPassword());
skip(destination.getFollowers());
skip(destination.getFollowing());

}
});
modelMapper.addMappings(new PropertyMap<User, SelfProfileResponseDto>() {
@Override
protected void configure() {
skip(destination.getQuestions());
skip(destination.getAnswers());
map(source.getReputationPoints(), destination.getReputationPoints());
}
});
modelMapper.addMappings(new PropertyMap <UserProfileResponseDto,User>() {
Expand All @@ -50,6 +52,7 @@ protected void configure() {
@Override
protected void configure() {
skip(destination.getFollowedTags());
map(source.getReputationPoints(), destination.getReputationPoints());

}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import com.group1.programminglanguagesforum.Constants.EndpointConstants;
import com.group1.programminglanguagesforum.DTOs.Responses.BookmarkQuestionResponseDto;
import com.group1.programminglanguagesforum.DTOs.Responses.QuestionSummaryDto;
import com.group1.programminglanguagesforum.DTOs.Responses.ErrorResponse;
import com.group1.programminglanguagesforum.DTOs.Responses.GenericApiResponse;
import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException;
import com.group1.programminglanguagesforum.Exceptions.ExceptionResponseHandler;
import com.group1.programminglanguagesforum.Services.BookmarkService;
import com.group1.programminglanguagesforum.Util.ApiResponseBuilder;

import jakarta.persistence.EntityExistsException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -19,7 +20,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;

Expand All @@ -40,22 +40,12 @@ public ResponseEntity<GenericApiResponse<BookmarkQuestionResponseDto>> bookmarkQ
bookmarkQuestionResponseDto
);
return buildResponse(genericApiResponse, HttpStatus.OK);


} catch (NoSuchElementException e) {
ErrorResponse errorResponse = ErrorResponse.builder()
.errorMessage(e.getMessage())
.stackTrace(Arrays.toString(e.getStackTrace()))
.build();
GenericApiResponse<BookmarkQuestionResponseDto> genericApiResponse = ApiResponseBuilder.buildErrorResponse(
BookmarkQuestionResponseDto.class,
"An error occurred while bookmarking the question",
HttpStatus.NOT_FOUND.value(),
errorResponse
);
return buildResponse(genericApiResponse, HttpStatus.NOT_FOUND);
return ExceptionResponseHandler.NoSuchElementException(e);
} catch (UnauthorizedAccessException e) {
return ExceptionResponseHandler.UnauthorizedAccessException(e);
} catch (EntityExistsException e) {
return ExceptionResponseHandler.EntityExistsException(e);
}
}

Expand All @@ -70,19 +60,8 @@ public ResponseEntity<GenericApiResponse<BookmarkQuestionResponseDto>> removeBoo
bookmarkQuestionResponseDto
);
return buildResponse(genericApiResponse, HttpStatus.OK);
}
catch (NoSuchElementException e) {
ErrorResponse errorResponse = ErrorResponse.builder()
.errorMessage(e.getMessage())
.stackTrace(Arrays.toString(e.getStackTrace()))
.build();
GenericApiResponse<BookmarkQuestionResponseDto> genericApiResponse = ApiResponseBuilder.buildErrorResponse(
BookmarkQuestionResponseDto.class,
"An error occurred while deleting the bookmark",
HttpStatus.NOT_FOUND.value(),
errorResponse
);
return buildResponse(genericApiResponse, HttpStatus.NOT_FOUND);
} catch (NoSuchElementException e) {
return ExceptionResponseHandler.NoSuchElementException(e);
} catch (UnauthorizedAccessException e) {
return ExceptionResponseHandler.UnauthorizedAccessException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException;
import com.group1.programminglanguagesforum.Services.TagService;
import com.group1.programminglanguagesforum.Services.UserContextService;
import com.group1.programminglanguagesforum.Services.UserService;
import com.group1.programminglanguagesforum.Util.ApiResponseBuilder;

import jakarta.persistence.EntityExistsException;
Expand All @@ -26,6 +27,7 @@

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Optional;


@RestController
Expand All @@ -35,6 +37,7 @@ public class TagController extends BaseController {

private final TagService tagService;
private final UserContextService userContextService;
private final UserService userService;

@GetMapping(value = EndpointConstants.TagEndpoints.SEARCH)
public ResponseEntity<GenericApiResponse<TagSearchResponseDto>> tagSearch(
Expand Down Expand Up @@ -94,6 +97,13 @@ public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> getTagDetail
@PostMapping(value = EndpointConstants.TagEndpoints.BASE_PATH)
public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> createTag(@RequestBody CreateTagRequestDto dto){
try{
User user = userContextService.getCurrentUser();
if(userService.calculateReputation(user) < 50){
return ExceptionResponseHandler.IllegalArgumentException(
new IllegalArgumentException("User does not have enough reputation to create a tag which should be at least 50")
);
}

GetTagDetailsResponseDto tagDetails = tagService.createTag(dto);
GenericApiResponse<GetTagDetailsResponseDto> response = GenericApiResponse.<GetTagDetailsResponseDto>builder()
.status(201)
Expand All @@ -115,6 +125,8 @@ public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> createTag(@R
.build();
ApiResponseBuilder.buildErrorResponse(GetTagDetailsResponseDto.class, "Invalid tag type", 400, errorResponse);
return buildResponse(response, HttpStatus.BAD_REQUEST);
} catch (UnauthorizedAccessException e) {
return ExceptionResponseHandler.UnauthorizedAccessException(e);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public ResponseEntity<GenericApiResponse<SelfProfileResponseDto>> getUser() {
selfProfileResponseDto.setFollowedTags(
tagService.getFollowedTags(user.getId())
);
selfProfileResponseDto.setReputationPoints(userService.calculateReputation(user));
selfProfileResponseDto.setQuestionCount((long) questions.size());
selfProfileResponseDto.setQuestions(
questions);
Expand Down Expand Up @@ -81,6 +82,7 @@ public ResponseEntity<GenericApiResponse<UserProfileResponseDto>> getUserById(
if (user.isPresent()) {
UserProfileResponseDto userProfileResponseDto = modelMapper.map(user.get(),
UserProfileResponseDto.class);
userProfileResponseDto.setReputationPoints(userService.calculateReputation(user.get()));
userProfileResponseDto.setSelfFollowing(userService.selfFollowing(user.get()));
userProfileResponseDto.setFollowedTags(
tagService.getFollowedTags(user.get().getId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class SelfProfileResponseDto {
private Long answerCount;
private int followersCount;
private int followingCount;
private int reputationPoints;
private Long reputationPoints;
private Long questionCount;
private ExperienceLevel experienceLevel;
private List<QuestionSummaryDto> questions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class UserProfileResponseDto {
private int followersCount;
private int followingCount;
private boolean selfFollowing;
private int reputationPoints;
private Long reputationPoints;
private ExperienceLevel experienceLevel;
@Builder.Default
private List<SelfProfileResponseDto.FollowedTags> followedTags = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public Long getDownvoteCount() {
return votes.stream().filter(vote -> !vote.isUpvote()).count();
}

public Long getVoteDifference() {
return Math.max(this.getUpvoteCount() - this.getDownvoteCount(), 0);
}

public Integer getRating() {
return (int)(this.getUpvoteCount() - this.getDownvoteCount());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,8 @@ public Long getUpvoteCount() {
public Long getDownvoteCount() {
return votes.stream().filter(vote -> !vote.isUpvote()).count();
}
public Long getVoteDifference() {
return Math.max(getUpvoteCount() - getDownvoteCount(), 0);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public static <T> ResponseEntity<GenericApiResponse<T>> EntityExistsException(En
GenericApiResponse<T> response = ApiResponseBuilder.buildErrorResponse(
UserProfileResponseDto.class,
e.getMessage(),
HttpStatus.CONFLICT.value(),
HttpStatus.BAD_REQUEST.value(),
errorResponse
);

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

import com.group1.programminglanguagesforum.Entities.DifficultyLevel;
import com.group1.programminglanguagesforum.Entities.Question;
import com.group1.programminglanguagesforum.Entities.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -10,6 +11,7 @@
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface QuestionRepository extends JpaRepository<Question, Long> {
Expand All @@ -28,6 +30,11 @@ Page<Question> searchQuestions(
@Param("tagIds") List<Long> tagIds,
@Param("difficulty") DifficultyLevel difficulty,
Pageable pageable);

@Query("SELECT q FROM Question q WHERE q.askedBy.id = :author")
List<Question> findByAuthorId(@Param("author") Long authorId);

@Query("SELECT q.askedBy FROM Question q WHERE q.id = :id")
Optional<User> findQuestionOwner(@Param("id") Long questionId);
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
@Repository
public interface TagRepository extends JpaRepository<Tag,Long> {
List<Tag> findAllByIdIn(List<Long> ids);
@Query("SELECT t FROM Tag t " +
"JOIN Question q ON t MEMBER OF q.tags " +
"GROUP BY t.id " +
"ORDER BY COUNT(q.id) DESC")

Page<Tag> findTagsByTagNameContainingIgnoreCase(String tagName, Pageable pageable);
@Query("SELECT t FROM Tag t JOIN t.followers u WHERE u.id = :userId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException;
import com.group1.programminglanguagesforum.Repositories.BookmarkRepository;

import jakarta.persistence.EntityExistsException;
import lombok.RequiredArgsConstructor;

import org.modelmapper.ModelMapper;
Expand All @@ -30,7 +31,11 @@ public BookmarkQuestionResponseDto bookmarkQuestion(Long questionId) throws Unau
User user = userContextService.getCurrentUser();

Optional<Question> question = questionService.findById(questionId);
Question questionEntity = question.orElseThrow();
Question questionEntity = question.orElseThrow(() -> new NoSuchElementException("Question not found"));

bookmarkRepository.findByUserAndQuestion(user, questionEntity).ifPresent(bookmark -> {
throw new EntityExistsException("Question already bookmarked");
});

Bookmark bookmark = Bookmark.builder()
.user(user)
Expand All @@ -45,8 +50,8 @@ public BookmarkQuestionResponseDto bookmarkQuestion(Long questionId) throws Unau

public BookmarkQuestionResponseDto removeBookmark(Long questionId) throws UnauthorizedAccessException {
User user = userContextService.getCurrentUser();
Question question = questionService.findById(questionId).orElseThrow();
Bookmark bookmark = bookmarkRepository.findByUserAndQuestion(user, question).orElseThrow(NoSuchElementException::new);
Question question = questionService.findById(questionId).orElseThrow(() -> new NoSuchElementException("Question not found"));
Bookmark bookmark = bookmarkRepository.findByUserAndQuestion(user, question).orElseThrow(() -> new NoSuchElementException("Bookmark not found"));

bookmarkRepository.delete(bookmark);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.group1.programminglanguagesforum.Services;

import com.group1.programminglanguagesforum.DTOs.Requests.UserProfileUpdateRequestDto;
import com.group1.programminglanguagesforum.Entities.Answer;
import com.group1.programminglanguagesforum.Entities.Question;
import com.group1.programminglanguagesforum.Entities.User;
import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException;
import com.group1.programminglanguagesforum.Exceptions.UserNotFoundException;
import com.group1.programminglanguagesforum.Repositories.AnswerRepository;
import com.group1.programminglanguagesforum.Repositories.QuestionRepository;
import com.group1.programminglanguagesforum.Repositories.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand All @@ -20,11 +24,24 @@
public class UserService {
private final UserRepository userRepository;
private final UserContextService userContextService;
private final AnswerRepository answerRepository;
private final QuestionRepository questionRepository;

public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}

public Long calculateReputation(User user) {
List<Question> questions = questionRepository.findByAuthorId(user.getId());
long questionCount = questions.size();
Long questionVoteDifference = questions.stream().map(Question::getVoteDifference).reduce(0L, Long::sum);
List< Answer> answers= answerRepository.findByAnsweredBy(user.getId());
long answerCount = answers.size();
Long answerVoteDifference = answers.stream().map(Answer::getVoteDifference).reduce(0L, Long::sum);
return (questionCount * 10 + answerCount * 15 + questionVoteDifference * 25 + answerVoteDifference * 30);

}

public User updateUser(User user, UserProfileUpdateRequestDto userProfileUpdateRequestDto)
throws UserNotFoundException {
Optional<User> userOptional = userRepository.findById(user.getId());
Expand Down
Loading

0 comments on commit 33f6690

Please sign in to comment.