diff --git a/pom.xml b/pom.xml index ed84c9b..fab15cc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ UTF-8 - io.zipcoder.tc_spring_poll_application.QuickPollApplication + io.zipcoder.springdemo.QuickPollApplication 1.7 diff --git a/src/main/java/dtos/OptionCount.java b/src/main/java/dtos/OptionCount.java new file mode 100644 index 0000000..cdf9522 --- /dev/null +++ b/src/main/java/dtos/OptionCount.java @@ -0,0 +1,22 @@ +package dtos; + +public class OptionCount { + private Long optionId; + private int count; + + public Long getOptionId() { + return optionId; + } + + public void setOptionId(Long optionId) { + this.optionId = optionId; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/src/main/java/dtos/VoteResult.java b/src/main/java/dtos/VoteResult.java new file mode 100644 index 0000000..b3d873d --- /dev/null +++ b/src/main/java/dtos/VoteResult.java @@ -0,0 +1,24 @@ +package dtos; + +import java.util.Collection; + +public class VoteResult { + private int totalVotes; + private Collection results; + + public int getTotalVotes() { + return totalVotes; + } + + public void setTotalVotes(int totalVotes) { + this.totalVotes = totalVotes; + } + + public Collection getResults() { + return results; + } + + public void setResults(Collection results) { + this.results = results; + } +} diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/controller/ComputeResultController.java b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/ComputeResultController.java new file mode 100644 index 0000000..385ebc5 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/ComputeResultController.java @@ -0,0 +1,51 @@ +package io.zipcoder.tc_spring_poll_application.controller; + +import dtos.OptionCount; +import dtos.VoteResult; +import io.zipcoder.tc_spring_poll_application.domain.Vote; +import io.zipcoder.tc_spring_poll_application.repositories.VoteRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class ComputeResultController { + + private VoteRepository voteRepository; + + @Autowired + public ComputeResultController(VoteRepository voteRepository){ + this.voteRepository = voteRepository; + } + + @RequestMapping(value = "/computeresult", method = RequestMethod.GET) + public ResponseEntity computeResult(@RequestParam Long pollId){ //requestparam annotation instructs SPring to retrieve the pollId value from a HTTP query + VoteResult voteResult = new VoteResult(); + Iterable allVotes = voteRepository.findVotesByPoll(pollId); + + int totalVotes = 0; + Map tempMap = new HashMap<>(); + for(Vote v : allVotes) { + totalVotes ++; + // Get the OptionCount corresponding to this Option + OptionCount optionCount = tempMap.get(v.getOption().getId()); + if(optionCount == null) { + optionCount = new OptionCount(); + optionCount.setOptionId(v.getOption().getId()); + tempMap.put(v.getOption().getId(), optionCount); + } + optionCount.setCount(optionCount.getCount()+1); + } + voteResult.setTotalVotes(totalVotes); + voteResult.setResults(tempMap.values()); + + return new ResponseEntity<>(voteResult, HttpStatus.OK); //computes results sent to client using new response entity + } +} diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/controller/PollController.java b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/PollController.java new file mode 100644 index 0000000..0b0ad2d --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/PollController.java @@ -0,0 +1,79 @@ +package io.zipcoder.tc_spring_poll_application.controller; + +import io.zipcoder.tc_spring_poll_application.domain.Poll; +import io.zipcoder.tc_spring_poll_application.exception.ResourceNotFoundException; +import io.zipcoder.tc_spring_poll_application.repositories.PollRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.validation.Valid; +import java.net.URI; + +@RestController// marks entity as a controller following REST specs +public class PollController { + + private PollRepository pollRepository; + + @Autowired //The @Autowired annotation allows you to skip configurations elsewhere of what to inject and just does it for you + public PollController(PollRepository pollRepository){ + this.pollRepository = pollRepository; + } + + @RequestMapping(value="/polls", method= RequestMethod.GET)//maps web requests to entities with the @RequestMapping + public ResponseEntity> getAllPolls(Pageable pageable) { + Page allPolls = pollRepository.findAll(pageable); //essentially pages the data + return new ResponseEntity<>(allPolls, HttpStatus.OK);// returns paged data + } + + @RequestMapping(value="/polls", method=RequestMethod.POST) + public ResponseEntity createPoll(@RequestBody Poll poll) { //@RequestBody tells Spring that the entire request body needs to be converted to an instance of Poll + poll = pollRepository.save(poll); + HttpHeaders newHeaders = new HttpHeaders(); + + URI newPollUri = ServletUriComponentsBuilder //allows the client to have a way to know the uri of the created poll + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(poll.getId()) + .toUri(); + newHeaders.setLocation(newPollUri); + return new ResponseEntity<>(newHeaders, HttpStatus.CREATED); + } + + @Valid + @RequestMapping(value="/polls/{pollId}", method=RequestMethod.GET) + public ResponseEntity getPoll(@PathVariable Long pollId) { + verifyPoll(pollId); + Poll p = pollRepository.findOne(pollId); + return new ResponseEntity<> (p, HttpStatus.OK); + } + + @Valid + @RequestMapping(value="/polls/{pollId}", method=RequestMethod.PUT) + public ResponseEntity updatePoll(@RequestBody Poll poll, @PathVariable Long pollId) { + // Save the entity + verifyPoll(pollId); + Poll p = pollRepository.save(poll); + return new ResponseEntity<>(HttpStatus.OK); + } + + @RequestMapping(value="/polls/{pollId}", method=RequestMethod.DELETE) + public ResponseEntity deletePoll(@PathVariable Long pollId) { + verifyPoll(pollId); + pollRepository.delete(pollId); + return new ResponseEntity<>(HttpStatus.OK); + } + + public void verifyPoll(Long pollId){ + Poll poll = pollRepository.findOne(pollId); + if(poll == null){ + throw new ResourceNotFoundException("The given poll id " + pollId + " does not exist!"); + } + } + +} diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/controller/VoteController.java b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/VoteController.java new file mode 100644 index 0000000..7a9bb10 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/VoteController.java @@ -0,0 +1,43 @@ +package io.zipcoder.tc_spring_poll_application.controller; + +import io.zipcoder.tc_spring_poll_application.domain.Vote; +import io.zipcoder.tc_spring_poll_application.repositories.VoteRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + + +@RestController +public class VoteController { + + private VoteRepository voteRepository; + + @Autowired + public VoteController(VoteRepository voteRepository) { + this.voteRepository = voteRepository; + } + + @RequestMapping(value = "/polls/{pollId}/votes", method = RequestMethod.POST) + public ResponseEntity createVote(@PathVariable Long pollId, @RequestBody Vote + vote) { + vote = voteRepository.save(vote); + // Set the headers for the newly created resource + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setLocation(ServletUriComponentsBuilder. + fromCurrentRequest().path("/{id}").buildAndExpand(vote.getId()).toUri()); + return new ResponseEntity<>(null, responseHeaders, HttpStatus.CREATED); + } + + @RequestMapping(value="/polls/votes", method=RequestMethod.GET) + public Iterable getAllVotes() { + return voteRepository.findAll(); + } + + @RequestMapping(value="/polls/{pollId}/votes", method=RequestMethod.GET) + public Iterable getVote(@PathVariable Long pollId) { + return voteRepository.findVotesByPoll(pollId); + } +} diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Option.java b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Option.java new file mode 100644 index 0000000..82ca99d --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Option.java @@ -0,0 +1,35 @@ +package io.zipcoder.tc_spring_poll_application.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity // this means that a class can be mapped to a table. Just is a marker like Serializable +public class Option { + + @Id // specifies primary key of entity (primary key is a special column) + @GeneratedValue // configures the way of increment of the specified column (field) + @Column(name = "OPTION_ID")// specifies mapped column for a persistence property. Without this annotation the framework assumes the field's variable-name is the persistent property + private Long id; + + @Column(name = "OPTION_VALUE")// specifies mapped column for persistence value + private String value; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Poll.java b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Poll.java new file mode 100644 index 0000000..c3e8b3f --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Poll.java @@ -0,0 +1,50 @@ +package io.zipcoder.tc_spring_poll_application.domain; + +import org.hibernate.validator.constraints.NotEmpty; + +import javax.persistence.*; +import javax.validation.constraints.Size; +import java.util.Set; + +@Entity +public class Poll { + + @Id + @GeneratedValue + @Column(name = "POLL_ID") + private Long id; + + @NotEmpty + @Column(name = "QUESTION") + private String question; + + @Size(min=2, max = 6) + @OneToMany(cascade = CascadeType.ALL)// indicates that a Poll instance can contain zero or more Option instances. The CascadeType.All indicates that any database operations such as persist, remove, or merge on a Poll instance needs to be propagated to all related Option instances + @JoinColumn(name = "POLL_ID")// indicates this entity is the owner of the relationship. It has a key to the column with options + @OrderBy// orders by ASC default + private Set