DEV Community

Cover image for Full Stack Reddit Clone - Spring Boot, React, Electron App - Part 8
MaxiCB
MaxiCB

Posted on

Full Stack Reddit Clone - Spring Boot, React, Electron App - Part 8

Full Stack Reddit Clone - Spring Boot, React, Electron App - Part 8

Introduction

Welcome to Part 8 of creating a Reddit clone using Spring Boot, and React.

What are we building in this part?

  • Vote DTO
  • Vote Exception
  • Vote Service
  • Vote Controller

In Part 7 we added the CREATE && READ endpoints for creating and reading comments!!

Important Links

Part 1: Vote DTO πŸ“¨

Let's cover our the DTO's we will need for receiving and sending Vote information. Inside com.your-name.backend.dto we will create the following class.

  • VoteDTO: Handles creation of the data that will be sent from the client to the API.

import com.maxicb.backend.model.VoteType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class VoteDTO {
    private VoteType voteType;
    private Long id;
}
Enter fullscreen mode Exit fullscreen mode

Part 2: Vote Exception 🚫

Let's cover our custom exceptions we will need. Inside com.your-name.backend.exception we will create the following class.

  • VoteException: Handles exceptions related to looking for a invalid user.
package com.maxicb.backend.exception;

public class VoteException extends RuntimeException {
        public VoteException(String message) {
            super(message);
        }
}

Enter fullscreen mode Exit fullscreen mode

Part 3: Vote Service 🌎

Let's cover the vote service our application will have. Inside com.your-name.backend.services add the following class.

  • VoteService: Hold the logic for mapping data to and from DTO's, and adding votes to a post.
package com.maxicb.backend.service;

import com.maxicb.backend.dto.VoteDTO;
import com.maxicb.backend.exception.PostNotFoundException;
import com.maxicb.backend.model.Post;
import com.maxicb.backend.model.Vote;
import com.maxicb.backend.repository.PostRepository;
import com.maxicb.backend.repository.VoteRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

import static com.maxicb.backend.model.VoteType.UPVOTE;

@Service
@AllArgsConstructor
public class VoteService {
    private final VoteRepository voteRepository;
    private final PostRepository postRepository;
    private final AuthService authService;

    private Vote maptoVote(VoteDTO voteDTO, Post post) {
        return Vote.builder()
                .voteType(voteDTO.getVoteType())
                .post(post)
                .user(authService.getCurrentUser())
                .build();
    }

    @Transactional
    public void vote(VoteDTO voteDTO) {
        Post post = postRepository.findById(voteDTO.getId())
                .orElseThrow(() -> new PostNotFoundException("Post not found with id:" + voteDTO.getId()));
        Optional<Vote> votePostAndUser = voteRepository.findTopByPostAndUserOrderByVoteIdDesc(post, authService.getCurrentUser());
        if(votePostAndUser.isPresent() && votePostAndUser.get().getVoteType().equals(voteDTO.getVoteType())) {
            throw new PostNotFoundException("You've already " + voteDTO.getVoteType() + "'d this post");
        }
        if(UPVOTE.equals(voteDTO.getVoteType())) {
            post.setVoteCount(post.getVoteCount() + 1);
        } else {
            post.setVoteCount(post.getVoteCount() - 1);
        }
        voteRepository.save(maptoVote(voteDTO, post));
        postRepository.save(post);
    }
}
Enter fullscreen mode Exit fullscreen mode

Part 4: Vote Controller 🌐

Let's cover the vote controller our application will have. Inside com.your-name.backend.controller add the following class.

  • VoteController: Hold the endpoints adding votes to a specific post.
package com.maxicb.backend.controller;

import com.maxicb.backend.dto.VoteDTO;
import com.maxicb.backend.service.VoteService;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/vote")
@AllArgsConstructor
public class VoteController {

    private final VoteService voteService;

    @PostMapping
    public ResponseEntity<Void> vote(@RequestBody VoteDTO voteDTO) {
        voteService.vote(voteDTO);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion πŸ”

  • To ensure everything is configured correctly you can run the application, and ensure there are no error in the console. Towards the bottom of the console you should see output similar to below

Alt Text

  • If there are no error's in the console you can test the voting logic by sending a post request to http://localhost:8080/api/vote with the following data. You will still have to follow the same steps covered in the previous parts to login to an account to make post's, as well as create a subreddit, and valid post to add a comment to.
{
    "voteType": "UPVOTE",
    "id": <post-id>
}
Enter fullscreen mode Exit fullscreen mode
{
    "postId": 9,
    "postTitle": "Testing Post",
    "url": "URL",
    "description": "DESCRIPTION",
    "userName": "USERNAME",
    "subredditName": "/r/NAME",
    "voteCount": 1,
    "commentCount": 1,
    "duration": "4 hours ago",
    "upVote": true,
    "downVote": false
}
Enter fullscreen mode Exit fullscreen mode
  • In this article we implemented the logic for voting on post's!.

Next

Follow to get informed when part nine is released, where we will cover the post voting functionality! If you have any questions be sure to leave a comment!

Top comments (0)