diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index ed84c9b..1fa25be 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 io.zipcoder @@ -11,12 +11,14 @@ org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE - + UTF-8 io.zipcoder.tc_spring_poll_application.QuickPollApplication - 1.7 + 11 + 11 + 11 @@ -43,7 +45,44 @@ javax.inject 1 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.1 + + + + + io.springfox + springfox-swagger2 + 2.7.0 + + + io.springfox + springfox-swagger-ui + 2.7.0 + + + com.sun.activation + javax.activation + 1.2.0 + + + + + org.springframework.boot + spring-boot-maven-plugin + + + - + \ No newline at end of file diff --git a/src/main/java/io/zipcoder/tc_spring_poll_application/config/SwaggerConfig.java b/src/main/java/io/zipcoder/tc_spring_poll_application/config/SwaggerConfig.java new file mode 100644 index 0000000..8ba7543 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/config/SwaggerConfig.java @@ -0,0 +1,39 @@ +package io.zipcoder.tc_spring_poll_application.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.Collections; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .host("localhost:8080") + .select() + .apis(RequestHandlerSelectors.basePackage("io.zipcoder.tc_spring_poll_application.controller")) + .paths(PathSelectors.any()) + .build() + .apiInfo(apiInfo()); + } + + private ApiInfo apiInfo() { + return new ApiInfo( + "Quick Poll API", + "API for managing polls and votes", + "1.0", + "Terms of service", + new Contact("ZipCode Wilmington", "www.zipcodewilmington.com", "info@zipcodewilmington.com"), + "License of API", "API license URL", Collections.emptyList()); + } +} \ No newline at end of file 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..9a6b984 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/PollController.java @@ -0,0 +1,85 @@ +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.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 java.net.URI; + +import javax.validation.Valid; + +@RestController +public class PollController { + + private final PollRepository pollRepository; + + @Autowired + public PollController(PollRepository pollRepository) { + this.pollRepository = pollRepository; + } + + // GET all polls + @RequestMapping(value="/polls", method=RequestMethod.GET) + public ResponseEntity> getAllPolls() { + Iterable allPolls = pollRepository.findAll(); + return new ResponseEntity<>(allPolls, HttpStatus.OK); + } + + // POST a new poll + @RequestMapping(value="/polls", method=RequestMethod.POST) + public ResponseEntity createPoll(@Valid @RequestBody Poll poll) { + + poll = pollRepository.save(poll); + + // Build URI for the newly created poll + URI newPollUri = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(poll.getId()) + .toUri(); + + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(newPollUri); + + return new ResponseEntity<>(headers, HttpStatus.CREATED); + } + + // GET a single poll by ID + @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); + } + + // UPDATE a poll + @RequestMapping(value="/polls/{pollId}", method=RequestMethod.PUT) + public ResponseEntity updatePoll(@Valid @RequestBody Poll poll, @PathVariable Long pollId) { + // Optionally, you could validate pollId matches poll.getId() + verifyPoll(pollId); + pollRepository.save(poll); + return new ResponseEntity<>(HttpStatus.OK); + } + + // DELETE a poll + @RequestMapping(value="/polls/{pollId}", method=RequestMethod.DELETE) + public ResponseEntity deletePoll(@PathVariable Long pollId) { + verifyPoll(pollId); + pollRepository.delete(pollId); + return new ResponseEntity<>(HttpStatus.OK); + } + + private void verifyPoll(Long pollId) { + Poll poll = pollRepository.findOne(pollId); + if (poll == null) { + throw new ResourceNotFoundException("Poll with id " + pollId + " not found"); + } +} + +} 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..4ab9ef6 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/controller/VoteController.java @@ -0,0 +1,52 @@ +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 javax.validation.Valid; + +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 final VoteRepository voteRepository; + + @Autowired + public VoteController(VoteRepository voteRepository) { + this.voteRepository = voteRepository; + } + + // POST a new vote for a specific poll + @RequestMapping(value = "/polls/{pollId}/votes", method = RequestMethod.POST) + public ResponseEntity createVote(@PathVariable Long pollId, @Valid @RequestBody Vote vote) { + vote = voteRepository.save(vote); + + // Build the location URI for the newly created vote + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setLocation(ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(vote.getId()) + .toUri()); + + return new ResponseEntity<>(null, responseHeaders, HttpStatus.CREATED); + } + + // GET all votes + @RequestMapping(value="/polls/votes", method=RequestMethod.GET) + public Iterable getAllVotes() { + return voteRepository.findAll(); + } + + // GET all votes for a specific poll + @RequestMapping(value="/polls/{pollId}/votes", method=RequestMethod.GET) + public Iterable getVotesByPoll(@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..77cd6f4 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Option.java @@ -0,0 +1,37 @@ +package io.zipcoder.tc_spring_poll_application.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Option { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "OPTION_ID") + private Long id; + + @Column(name = "OPTION_VALUE") + private String value; + + // Getter and Setter for id + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + // Getter and Setter for value + 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..5bb4bb6 --- /dev/null +++ b/src/main/java/io/zipcoder/tc_spring_poll_application/domain/Poll.java @@ -0,0 +1,57 @@ +package io.zipcoder.tc_spring_poll_application.domain; + + +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +@Entity +public class Poll { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "POLL_ID") + private Long id; + + @Column(name = "QUESTION") + private String question; + + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn(name = "POLL_ID") + @OrderBy + private Set