├── src ├── test │ └── java │ │ └── br │ │ └── com │ │ └── devdojo │ │ └── examgenerator │ │ ├── DemoApplicationTests.java │ │ └── endpoint │ │ └── v1 │ │ ├── ProfessorEndpointTest.java │ │ ├── course │ │ └── CourseEndpointTest.java │ │ ├── questionassignment │ │ └── QuestionAssignmentEndpointTest.java │ │ ├── assignment │ │ └── AssignmentEndpointTest.java │ │ ├── question │ │ └── QuestionEndpointTest.java │ │ └── choice │ │ └── ChoiceEndpointTest.java └── main │ ├── java │ └── br │ │ └── com │ │ └── devdojo │ │ └── examgenerator │ │ ├── exception │ │ ├── ConflictException.java │ │ └── ResourceNotFoundException.java │ │ ├── persistence │ │ ├── respository │ │ │ ├── ProfessorRepository.java │ │ │ ├── ApplicationUserRepository.java │ │ │ ├── ExamAnswerRepository.java │ │ │ ├── CourseRepository.java │ │ │ ├── AssignmentRepository.java │ │ │ ├── QuestionRepository.java │ │ │ ├── ChoiceRepository.java │ │ │ ├── QuestionAssignmentRepository.java │ │ │ └── CustomPagingAndSortRepository.java │ │ └── model │ │ │ ├── AbstractEntity.java │ │ │ ├── Professor.java │ │ │ ├── Course.java │ │ │ ├── ApplicationUser.java │ │ │ ├── Student.java │ │ │ ├── Question.java │ │ │ ├── QuestionAssignment.java │ │ │ ├── Choice.java │ │ │ ├── Assignment.java │ │ │ └── ExamAnswer.java │ │ ├── ExamGeneratorApplication.java │ │ ├── security │ │ ├── filter │ │ │ ├── Constants.java │ │ │ ├── JWTAuthorizationFilter.java │ │ │ └── JWTAuthenticationFilter.java │ │ ├── config │ │ │ └── SecurityConfig.java │ │ └── service │ │ │ └── CustomUserDetailsService.java │ │ ├── endpoint │ │ └── v1 │ │ │ ├── genericservice │ │ │ └── GenericService.java │ │ │ ├── ProfessorEndpoint.java │ │ │ ├── deleteservice │ │ │ └── CascadeDeleteService.java │ │ │ ├── course │ │ │ └── CourseEndpoint.java │ │ │ ├── question │ │ │ └── QuestionEndpoint.java │ │ │ ├── assignment │ │ │ └── AssignmentEndpoint.java │ │ │ ├── choice │ │ │ └── ChoiceEndpoint.java │ │ │ ├── exam │ │ │ └── ExamEndpoint.java │ │ │ └── questionassignment │ │ │ └── QuestionAssignmentEndpoint.java │ │ ├── docs │ │ └── SwaggerConfig.java │ │ └── util │ │ └── EndpointUtil.java │ └── resources │ └── application.properties ├── .idea └── dataSources.xml └── pom.xml /src/test/java/br/com/devdojo/examgenerator/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/exception/ConflictException.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | /** 7 | * @author William Suane on 20/03/2018 8 | */ 9 | @ResponseStatus(HttpStatus.CONFLICT) 10 | public class ConflictException extends RuntimeException { 11 | public ConflictException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/ProfessorRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Professor; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | 6 | /** 7 | * @author William Suane for DevDojo on 10/24/17. 8 | */ 9 | public interface ProfessorRepository extends PagingAndSortingRepository { 10 | Professor findByEmail(String email); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | /** 7 | * @author William Suane for DevDojo on 10/27/17. 8 | */ 9 | @ResponseStatus(HttpStatus.NOT_FOUND) 10 | public class ResourceNotFoundException extends RuntimeException { 11 | public ResourceNotFoundException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/ApplicationUserRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.ApplicationUser; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | 6 | /** 7 | * @author William Suane for DevDojo on 10/10/17. 8 | */ 9 | public interface ApplicationUserRepository extends PagingAndSortingRepository { 10 | ApplicationUser findByUsername(String username); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/ExamAnswerRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.ExamAnswer; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | 6 | /** 7 | * @author William Suane on 29/08/2018 8 | */ 9 | public interface ExamAnswerRepository extends PagingAndSortingRepository { 10 | boolean existsExamAnswerByAssignmentIdAndStudentId(long assignmentId, long studentId); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/ProfessorEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Professor; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * @author William Suane for DevDojo on 11/3/17. 9 | */ 10 | public class ProfessorEndpointTest { 11 | public static Professor mockProfessor(){ 12 | return Professor.Builder.newProfessor() 13 | .id(1L) 14 | .name("Will") 15 | .email("will@something.com") 16 | .build(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mysql://localhost:3306/exam?useSSL=false 2 | spring.datasource.username=root 3 | spring.datasource.password=root 4 | spring.datasource.tomcat.test-while-idle=true 5 | spring.datasource.tomcat.validation-query=SELECT 1 6 | 7 | 8 | spring.jpa.hibernate.ddl-auto=update 9 | spring.jpa.show-sql=true 10 | 11 | spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect 12 | spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 13 | 14 | server.port= 8085 15 | spring.jackson.serialization.write-dates-as-timestamps=false 16 | spring.jackson.serialization.write-dates-with-zone-id=true 17 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/CourseRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Course; 4 | import org.springframework.data.jpa.repository.Query; 5 | import org.springframework.data.repository.PagingAndSortingRepository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author William Suane for DevDojo on 10/27/17. 11 | */ 12 | @SuppressWarnings("ALL") 13 | public interface CourseRepository extends CustomPagingAndSortRepository { 14 | @Query("select c from Course c where c.name like %?1% and c.professor = ?#{principal.professor} and c.enabled = true") 15 | List listCoursesByName(String name); 16 | } -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/ExamGeneratorApplication.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension; 7 | 8 | @SpringBootApplication 9 | public class ExamGeneratorApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ExamGeneratorApplication.class, args); 12 | } 13 | 14 | @Bean 15 | public SecurityEvaluationContextExtension securityEvaluationContextExtension() { 16 | return new SecurityEvaluationContextExtension(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/security/filter/Constants.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.security.filter; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * @author William Suane for DevDojo on 10/10/17. 9 | */ 10 | public class Constants { 11 | public static final String SECRET = "secre"; 12 | public static final String TOKEN_PREFIX = "Bearer "; 13 | public static final String HEADER_STRING = "Authorization"; 14 | public static final long EXPIRATION_TIME = 86400000L; // 1 day 15 | 16 | public static void main(String[] args) { 17 | System.out.println(TimeUnit.MILLISECONDS.convert(1,TimeUnit.DAYS)); 18 | System.out.println(new BCryptPasswordEncoder().encode("devdojo")); 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/genericservice/GenericService.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.genericservice; 2 | 3 | import br.com.devdojo.examgenerator.exception.ResourceNotFoundException; 4 | import br.com.devdojo.examgenerator.persistence.model.AbstractEntity; 5 | import br.com.devdojo.examgenerator.persistence.respository.CustomPagingAndSortRepository; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * @author William Suane for DevDojo on 11/10/17. 10 | */ 11 | @Service 12 | public class GenericService { 13 | 14 | public void throwResourceNotFoundIfDoesNotExist(T t, CustomPagingAndSortRepository repository, String msg) { 15 | if (t == null || t.getId() == null || repository.findOne(t.getId()) == null) 16 | throw new ResourceNotFoundException(msg); 17 | } 18 | 19 | public void throwResourceNotFoundIfDoesNotExist(long id, CustomPagingAndSortRepository repository, String msg) { 20 | if (id == 0 || repository.findOne(id) == null) 21 | throw new ResourceNotFoundException(msg); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/AbstractEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import javax.persistence.*; 4 | import java.io.Serializable; 5 | 6 | /** 7 | * @author William Suane for DevDojo on 10/10/17. 8 | */ 9 | @MappedSuperclass 10 | public class AbstractEntity implements Serializable { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | protected Long id; 14 | @Column(columnDefinition = "boolean default true", nullable = false) 15 | private boolean enabled = true; 16 | @Override 17 | public boolean equals(Object o) { 18 | if (this == o) return true; 19 | if (o == null || getClass() != o.getClass()) return false; 20 | AbstractEntity that = (AbstractEntity) o; 21 | return id != null ? id.equals(that.id) : that.id == null; 22 | } 23 | 24 | @Override 25 | public int hashCode() { 26 | return id != null ? id.hashCode() : 0; 27 | } 28 | 29 | public boolean isEnabled() { 30 | return enabled; 31 | } 32 | 33 | public void setEnabled(boolean enabled) { 34 | this.enabled = enabled; 35 | } 36 | 37 | public Long getId() { 38 | return id; 39 | } 40 | 41 | public void setId(Long id) { 42 | this.id = id; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/AssignmentRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Assignment; 4 | import org.springframework.data.jpa.repository.Modifying; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author William Suane for DevDojo on 12/07/17. 11 | */ 12 | @SuppressWarnings("ALL") 13 | public interface AssignmentRepository extends CustomPagingAndSortRepository { 14 | @Query("select a from Assignment a where a.course.id = ?1 and a.title like %?2% and a.professor = ?#{principal.professor} and a.enabled = true") 15 | List listAssignemntsByCourseAndTitle(long courseId, String title); 16 | 17 | @Query("update Assignment a set a.enabled = false where a.course.id = ?1 and a.professor = ?#{principal.professor} and a.enabled = true") 18 | @Modifying 19 | void deleteAllAssignmentsRelatedToCourse(long courseId); 20 | 21 | @Query("select a from Assignment a where a.course.id = ?1 and a.accessCode =?2 and a.professor = ?#{principal.professor} and a.enabled = true") 22 | Assignment accessCodeExistsForCourse(String accessCode, long courseId); 23 | 24 | @Query("select a from Assignment a where a.accessCode =?1 and a.enabled = true") 25 | Assignment findAssignmentByAccessCode(String accessCode); 26 | } -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/ProfessorEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Professor; 4 | import br.com.devdojo.examgenerator.persistence.respository.ProfessorRepository; 5 | import io.swagger.annotations.ApiOperation; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | /** 15 | * @author William Suane for DevDojo on 10/10/17. 16 | */ 17 | @RestController 18 | @RequestMapping("v1/professor") 19 | public class ProfessorEndpoint { 20 | private final ProfessorRepository professorRepository; 21 | 22 | @Autowired 23 | public ProfessorEndpoint(ProfessorRepository professorRepository) { 24 | this.professorRepository = professorRepository; 25 | } 26 | 27 | @GetMapping(path = "{id}") 28 | @ApiOperation(value = "Find professor by his ID", notes = "We have to make this method better", response = Professor.class) 29 | public ResponseEntity getProfessorById(@PathVariable long id) { 30 | Professor professor = professorRepository.findOne(id); 31 | return new ResponseEntity<>(professor, HttpStatus.OK); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/QuestionRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Question; 4 | import org.springframework.data.jpa.repository.Modifying; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.transaction.annotation.Transactional; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author William Suane for DevDojo on 12/15/17. 12 | */ 13 | @SuppressWarnings("ALL") 14 | public interface QuestionRepository extends CustomPagingAndSortRepository { 15 | @Query("select q from Question q where q.course.id = ?1 and q.title like %?2% and q.professor = ?#{principal.professor} and q.enabled = true") 16 | List listQuestionsByCourseAndTitle(long courseId, String title); 17 | 18 | @Query("update Question q set q.enabled = false where q.course.id = ?1 and q.professor = ?#{principal.professor} and q.enabled = true") 19 | @Modifying 20 | void deleteAllQuestionsRelatedToCourse(long courseId); 21 | 22 | @Query("select q from Question q where q.course.id = ?1 and q.id not in " + 23 | "(select qa.question.id from QuestionAssignment qa where qa.assignment.id = ?2 and qa.professor = ?#{principal.professor} and qa.enabled = true) " + 24 | "and q.professor = ?#{principal.professor} and q.enabled = true") 25 | @Transactional 26 | List listQuestionsByCourseNotAssociatedWithAnAssignment(long courseId, long assigmentId); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/docs/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.docs; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.RequestHandlerSelectors; 7 | import springfox.documentation.service.ApiInfo; 8 | import springfox.documentation.service.Contact; 9 | import springfox.documentation.spi.DocumentationType; 10 | import springfox.documentation.spring.web.plugins.Docket; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | /** 14 | * @author William Suane for DevDojo on 10/24/17. 15 | */ 16 | @Configuration 17 | @EnableSwagger2 18 | public class SwaggerConfig { 19 | 20 | @Bean 21 | public Docket api() { 22 | return new Docket(DocumentationType.SWAGGER_2) 23 | .select() 24 | .apis(RequestHandlerSelectors.basePackage("br.com.devdojo.examgenerator.endpoint.v1")) 25 | .build() 26 | .apiInfo(metaData()); 27 | } 28 | 29 | private ApiInfo metaData() { 30 | return new ApiInfoBuilder() 31 | .title("Exam generator by DevDojo") 32 | .description("Software to generate exams based on questions") 33 | .version("1.0") 34 | .contact(new Contact("William Suane", "http://devdojo.com.br", "william.suane@devdojo.com.br")) 35 | .license("Apache License Version 2.0") 36 | .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0") 37 | .build(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mysql 6 | true 7 | com.mysql.jdbc.Driver 8 | jdbc:mysql://devdojo.ddns.net:2062/exam 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | mysql 20 | true 21 | com.mysql.jdbc.Driver 22 | jdbc:mysql://localhost:3306/exam 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/util/EndpointUtil.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.util; 2 | 3 | import br.com.devdojo.examgenerator.exception.ResourceNotFoundException; 4 | import br.com.devdojo.examgenerator.persistence.model.ApplicationUser; 5 | import br.com.devdojo.examgenerator.persistence.model.Professor; 6 | import br.com.devdojo.examgenerator.persistence.model.Student; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.io.Serializable; 14 | import java.util.List; 15 | 16 | /** 17 | * @author William Suane for DevDojo on 10/27/17. 18 | */ 19 | @Service 20 | public class EndpointUtil implements Serializable { 21 | public ResponseEntity returnObjectOrNotFound(Object object) { 22 | if (object == null) throw new ResourceNotFoundException("Not found"); 23 | return new ResponseEntity<>(object, HttpStatus.OK); 24 | } 25 | 26 | public ResponseEntity returnObjectOrNotFound(List list) { 27 | if (list == null || list.isEmpty()) throw new ResourceNotFoundException("Not found"); 28 | return new ResponseEntity<>(list, HttpStatus.OK); 29 | } 30 | 31 | public Professor extractProfessorFromToken() { 32 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 33 | return ((ApplicationUser) authentication.getPrincipal()).getProfessor(); 34 | } 35 | 36 | public Student extractStudentFromToken() { 37 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 38 | return ((ApplicationUser) authentication.getPrincipal()).getStudent(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Professor.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import org.hibernate.validator.constraints.Email; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | 9 | /** 10 | * @author William Suane for DevDojo on 10/10/17. 11 | */ 12 | @Entity 13 | public class Professor extends AbstractEntity { 14 | @NotEmpty(message = "The field name cannot be empty") 15 | private String name; 16 | @Email(message = "This email is not valid") 17 | @NotEmpty(message = "The field email cannot be empty") 18 | @Column(unique = true) 19 | private String email; 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public String getEmail() { 30 | return email; 31 | } 32 | 33 | public void setEmail(String email) { 34 | this.email = email; 35 | } 36 | 37 | public static final class Builder { 38 | private Professor professor; 39 | 40 | private Builder() { 41 | professor = new Professor(); 42 | } 43 | 44 | public static Builder newProfessor() { 45 | return new Builder(); 46 | } 47 | 48 | public Builder name(String name) { 49 | professor.setName(name); 50 | return this; 51 | } 52 | 53 | public Builder id(Long id) { 54 | professor.setId(id); 55 | return this; 56 | } 57 | 58 | public Builder email(String email) { 59 | professor.setEmail(email); 60 | return this; 61 | } 62 | 63 | public Professor build() { 64 | return professor; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Course.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.ManyToOne; 9 | 10 | /** 11 | * @author William Suane for DevDojo on 10/27/17. 12 | */ 13 | @Entity 14 | public class Course extends AbstractEntity { 15 | @NotEmpty(message = "The field name cannot be empty") 16 | @ApiModelProperty(notes = "The name of the course") 17 | private String name; 18 | @ManyToOne(optional = false) 19 | private Professor professor; 20 | 21 | 22 | public static final class Builder { 23 | private Course course; 24 | 25 | private Builder() { 26 | course = new Course(); 27 | } 28 | 29 | public static Builder newCourse() { 30 | return new Builder(); 31 | } 32 | 33 | public Builder id(Long id) { 34 | course.setId(id); 35 | return this; 36 | } 37 | 38 | public Builder name(String name) { 39 | course.setName(name); 40 | return this; 41 | } 42 | 43 | public Builder professor(Professor professor) { 44 | course.setProfessor(professor); 45 | return this; 46 | } 47 | 48 | public Course build() { 49 | return course; 50 | } 51 | } 52 | 53 | public String getName() { 54 | return name; 55 | } 56 | 57 | public void setName(String name) { 58 | this.name = name; 59 | } 60 | 61 | public Professor getProfessor() { 62 | return professor; 63 | } 64 | 65 | public void setProfessor(Professor professor) { 66 | this.professor = professor; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/ApplicationUser.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import org.hibernate.validator.constraints.NotEmpty; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.OneToOne; 8 | 9 | /** 10 | * @author William Suane for DevDojo on 10/10/17. 11 | */ 12 | @Entity 13 | public class ApplicationUser extends AbstractEntity { 14 | @NotEmpty(message = "The field username cannot be empty") 15 | @Column(unique = true) 16 | private String username; 17 | @NotEmpty(message = "The field password cannot be empty") 18 | private String password; 19 | @OneToOne 20 | private Professor professor; 21 | @OneToOne 22 | private Student student; 23 | 24 | public ApplicationUser() { 25 | } 26 | 27 | public ApplicationUser(ApplicationUser applicationUser) { 28 | this.username = applicationUser.username; 29 | this.password = applicationUser.password; 30 | this.professor = applicationUser.professor; 31 | this.student = applicationUser.student; 32 | } 33 | 34 | public String getUsername() { 35 | return username; 36 | } 37 | 38 | public void setUsername(String username) { 39 | this.username = username; 40 | } 41 | 42 | public String getPassword() { 43 | return password; 44 | } 45 | 46 | public void setPassword(String password) { 47 | this.password = password; 48 | } 49 | 50 | public Professor getProfessor() { 51 | return professor; 52 | } 53 | 54 | public void setProfessor(Professor professor) { 55 | this.professor = professor; 56 | } 57 | 58 | public Student getStudent() { 59 | return student; 60 | } 61 | 62 | public void setStudent(Student student) { 63 | this.student = student; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Student.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import org.hibernate.validator.constraints.Email; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | 9 | /** 10 | * @author William Suane on 07/04/2018 11 | */ 12 | @Entity 13 | public class Student extends AbstractEntity { 14 | @NotEmpty(message = "The field name cannot be empty") 15 | private String name; 16 | @Email(message = "This email is not valid") 17 | @NotEmpty(message = "The field email cannot be empty") 18 | @Column(unique = true) 19 | private String email; 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public String getEmail() { 30 | return email; 31 | } 32 | 33 | public void setEmail(String email) { 34 | this.email = email; 35 | } 36 | 37 | public static final class Builder { 38 | private Student student; 39 | 40 | private Builder() { 41 | student = new Student(); 42 | } 43 | 44 | public static Builder newStudent() { 45 | return new Builder(); 46 | } 47 | 48 | public Builder id(Long id) { 49 | student.setId(id); 50 | return this; 51 | } 52 | 53 | public Builder name(String name) { 54 | student.setName(name); 55 | return this; 56 | } 57 | 58 | public Builder enabled(boolean enabled) { 59 | student.setEnabled(enabled); 60 | return this; 61 | } 62 | 63 | public Builder email(String email) { 64 | student.setEmail(email); 65 | return this; 66 | } 67 | 68 | public Student build() { 69 | return student; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/ChoiceRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Choice; 4 | import br.com.devdojo.examgenerator.persistence.model.Question; 5 | import org.springframework.data.jpa.repository.Modifying; 6 | import org.springframework.data.jpa.repository.Query; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author William Suane for DevDojo on 11/20/17. 12 | */ 13 | public interface ChoiceRepository extends CustomPagingAndSortRepository { 14 | @Query("select c from Choice c where c.question.id = ?1 and c.professor = ?#{principal.professor} and c.enabled = true") 15 | List listChoicesByQuestionId(long questionId); 16 | 17 | @Query("select c from Choice c where c.question.id in ?1 and c.enabled = true") 18 | List listChoicesByQuestionsIdForStudent(List questionsId); 19 | 20 | @Query("update Choice c set c.correctAnswer = false where c <> ?1 and c.question = ?2 and c.professor = ?#{principal.professor} and c.enabled = true") 21 | @Modifying 22 | void updateAllOtherChoicesCorrectAnswerToFalse(Choice choice, Question question); 23 | 24 | @Query("update Choice c set c.enabled = false where c.question.id = ?1 and c.professor = ?#{principal.professor} and c.enabled = true") 25 | @Modifying 26 | void deleteAllChoicesRelatedToQuestion(long questionId); 27 | 28 | @Query("update Choice c set c.enabled = false where c.question.id in (select q.id from Question q where q.course.id = ?1) and c.professor = ?#{principal.professor} and c.enabled = true") 29 | @Modifying 30 | void deleteAllChoicesRelatedToCourse(long courseId); 31 | 32 | @Query("select c from Choice c where c.id =?1 and c.enabled = true") 33 | Choice findOne(Long id); 34 | 35 | @Query("select c from Choice c where c.question.id = ?1 and c.correctAnswer = true and c.enabled = true") 36 | Choice findCorrectChoiceForQuestion(long questionId); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/security/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.security.config; 2 | 3 | import br.com.devdojo.examgenerator.security.filter.JWTAuthenticationFilter; 4 | import br.com.devdojo.examgenerator.security.filter.JWTAuthorizationFilter; 5 | import br.com.devdojo.examgenerator.security.service.CustomUserDetailsService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | 13 | /** 14 | * @author William Suane for DevDojo on 10/12/17. 15 | */ 16 | @EnableWebSecurity 17 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 18 | private final CustomUserDetailsService customUserDetailsService; 19 | 20 | @Autowired 21 | public SecurityConfig(CustomUserDetailsService customUserDetailsService) { 22 | this.customUserDetailsService = customUserDetailsService; 23 | } 24 | 25 | @Override 26 | protected void configure(HttpSecurity http) throws Exception { 27 | // http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()) 28 | http.cors().disable() 29 | .csrf().disable() 30 | .authorizeRequests() 31 | .antMatchers("/*/professor/**").hasRole("PROFESSOR") 32 | .antMatchers("/*/student/**").hasRole("STUDENT") 33 | .and() 34 | .addFilter(new JWTAuthenticationFilter(authenticationManager())) 35 | .addFilter(new JWTAuthorizationFilter(authenticationManager(),customUserDetailsService)); 36 | } 37 | 38 | @Override 39 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 40 | auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/deleteservice/CascadeDeleteService.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.deleteservice; 2 | 3 | import br.com.devdojo.examgenerator.persistence.respository.*; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | 7 | /** 8 | * @author William Suane for DevDojo on 11/22/17. 9 | */ 10 | @Service 11 | public class CascadeDeleteService { 12 | private final QuestionRepository questionRepository; 13 | private final ChoiceRepository choiceRepository; 14 | private final CourseRepository courseRepository; 15 | private final AssignmentRepository assignmentRepository; 16 | private final QuestionAssignmentRepository questionAssignmentRepository; 17 | 18 | @Autowired 19 | public CascadeDeleteService(QuestionRepository questionRepository, ChoiceRepository choiceRepository, CourseRepository courseRepository, AssignmentRepository assignmentRepository, QuestionAssignmentRepository questionAssignmentRepository) { 20 | this.questionRepository = questionRepository; 21 | this.choiceRepository = choiceRepository; 22 | this.courseRepository = courseRepository; 23 | this.assignmentRepository = assignmentRepository; 24 | this.questionAssignmentRepository = questionAssignmentRepository; 25 | } 26 | 27 | public void deleteCourseAndAllRelatedEntities(long courseId){ 28 | courseRepository.delete(courseId); 29 | questionRepository.deleteAllQuestionsRelatedToCourse(courseId); 30 | choiceRepository.deleteAllChoicesRelatedToCourse(courseId); 31 | assignmentRepository.deleteAllAssignmentsRelatedToCourse(courseId); 32 | questionAssignmentRepository.deleteAllQuestionAssignmentsRelatedToCourse(courseId); 33 | } 34 | 35 | public void deleteAssignmentAndAllRelatedEntities(long assignmentId) { 36 | assignmentRepository.delete(assignmentId); 37 | questionAssignmentRepository.deleteAllQuestionAssignmentsRelatedToAssignment(assignmentId); 38 | } 39 | 40 | public void deleteQuestionAndAllRelatedEntities(long questionId){ 41 | questionRepository.delete(questionId); 42 | choiceRepository.deleteAllChoicesRelatedToQuestion(questionId); 43 | questionAssignmentRepository.deleteAllQuestionAssignmentsRelatedToQuestion(questionId); 44 | } 45 | 46 | public void deleteQuestionAssignmentAndAllRelatedEntities(long questionAssignmentId) { 47 | questionAssignmentRepository.delete(questionAssignmentId); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/QuestionAssignmentRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.Question; 4 | import br.com.devdojo.examgenerator.persistence.model.QuestionAssignment; 5 | import org.springframework.data.jpa.repository.Modifying; 6 | import org.springframework.data.jpa.repository.Query; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author William Suane for DevDojo on 12/15/17. 12 | */ 13 | @SuppressWarnings("ALL") 14 | public interface QuestionAssignmentRepository extends CustomPagingAndSortRepository { 15 | @Query("update QuestionAssignment qa set qa.enabled = false where qa.assignment.id in (select a.id from Assignment a where a.course.id = ?1) and qa.professor = ?#{principal.professor} and qa.enabled = true") 16 | @Modifying 17 | void deleteAllQuestionAssignmentsRelatedToCourse(long courseId); 18 | 19 | @Query("update QuestionAssignment qa set qa.enabled = false where qa.assignment.id = ?1 and qa.professor = ?#{principal.professor} and qa.enabled = true") 20 | @Modifying 21 | void deleteAllQuestionAssignmentsRelatedToAssignment(long assignmentId); 22 | 23 | @Query("update QuestionAssignment qa set qa.enabled = false where qa.question.id = ?1 and qa.professor = ?#{principal.professor} and qa.enabled = true") 24 | @Modifying 25 | void deleteAllQuestionAssignmentsRelatedToQuestion(long questionId); 26 | 27 | @Query("select qa from QuestionAssignment qa where qa.question.id = ?1 and qa.assignment.id = ?2 and qa.professor = ?#{principal.professor} and qa.enabled = true") 28 | List listQuestionAssignmentByQuestionAndAssignment(long questionId, long assignmentId); 29 | 30 | @Query("select qa from QuestionAssignment qa where qa.assignment.id = ?1 and qa.professor = ?#{principal.professor} and qa.enabled = true") 31 | List listQuestionAssignmentByAssignmentId(long assignmentId); 32 | 33 | @Query("select qa from QuestionAssignment qa where qa.question.id = ?1 and qa.professor = ?#{principal.professor} and qa.enabled = true") 34 | List listQuestionAssignmentByQuestionId(long questionId); 35 | 36 | @Query("select qa.question from QuestionAssignment qa where qa.assignment.accessCode = ?1 and qa.enabled = true") 37 | List listQuestionsFromQuestionAssignmentByAssignmentAccessCode(String accessCode); 38 | 39 | @Query("select qa from QuestionAssignment qa where qa.assignment.id = ?1 and qa.question.id = ?2 and qa.enabled = true") 40 | QuestionAssignment findQuestionAssignmentByAssignmentIdAndQuestionId(long assignmentId, long questionId); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/respository/CustomPagingAndSortRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.respository; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.AbstractEntity; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.domain.Sort; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | import org.springframework.data.repository.NoRepositoryBean; 10 | import org.springframework.data.repository.PagingAndSortingRepository; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | /** 14 | * @author William Suane for DevDojo on 11/10/17. 15 | */ 16 | @NoRepositoryBean 17 | public interface CustomPagingAndSortRepository 18 | extends PagingAndSortingRepository { 19 | @Override 20 | @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true") 21 | Iterable findAll(Sort sort); 22 | 23 | @Override 24 | @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true") 25 | Page findAll(Pageable pageable); 26 | 27 | @Override 28 | @Query("select e from #{#entityName} e where e.id =?1 and e.professor = ?#{principal.professor} and e.enabled = true") 29 | T findOne(Long id); 30 | 31 | @Override 32 | default boolean exists(Long id){ 33 | return findOne(id) != null; 34 | } 35 | 36 | @Override 37 | @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true") 38 | Iterable findAll(); 39 | 40 | @Override 41 | @Query("select e from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true") 42 | Iterable findAll(Iterable iterable); 43 | 44 | @Override 45 | @Query("select count(e) from #{#entityName} e where e.professor = ?#{principal.professor} and e.enabled = true") 46 | long count(); 47 | 48 | @Override 49 | @Transactional 50 | @Modifying 51 | @Query("update #{#entityName} e set e.enabled=false where e.id=?1 and e.professor = ?#{principal.professor}") 52 | void delete(Long id); 53 | 54 | @Override 55 | @Transactional 56 | @Modifying 57 | default void delete(T t){ 58 | delete(t.getId()); 59 | } 60 | 61 | @Override 62 | @Transactional 63 | @Modifying 64 | default void delete(Iterable iterable){ 65 | iterable.forEach(entity -> delete(entity.getId())); 66 | } 67 | 68 | @Override 69 | @Transactional 70 | @Modifying 71 | @Query("update #{#entityName} e set e.enabled=false where e.professor = ?#{principal.professor}") 72 | void deleteAll(); 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Question.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import org.hibernate.validator.constraints.NotEmpty; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.FetchType; 9 | import javax.persistence.ManyToOne; 10 | import javax.persistence.OneToMany; 11 | import java.util.List; 12 | 13 | /** 14 | * @author William Suane for DevDojo on 11/10/17. 15 | */ 16 | @Entity 17 | public class Question extends AbstractEntity { 18 | @NotEmpty(message = "The field title cannot be empty") 19 | @ApiModelProperty(notes = "The title of the question") 20 | private String title; 21 | @ManyToOne(optional = false) 22 | private Course course; 23 | @ManyToOne(optional = false) 24 | private Professor professor; 25 | @OneToMany(mappedBy = "question", fetch = FetchType.LAZY) 26 | @JsonIgnore 27 | private List choices; 28 | 29 | 30 | public static final class Builder { 31 | private Question question; 32 | 33 | private Builder() { 34 | question = new Question(); 35 | } 36 | 37 | public static Builder newQuestion() { 38 | return new Builder(); 39 | } 40 | 41 | public Builder id(Long id) { 42 | question.setId(id); 43 | return this; 44 | } 45 | 46 | public Builder enabled(boolean enabled) { 47 | question.setEnabled(enabled); 48 | return this; 49 | } 50 | 51 | public Builder title(String title) { 52 | question.setTitle(title); 53 | return this; 54 | } 55 | 56 | public Builder course(Course course) { 57 | question.setCourse(course); 58 | return this; 59 | } 60 | 61 | public Builder professor(Professor professor) { 62 | question.setProfessor(professor); 63 | return this; 64 | } 65 | 66 | public Question build() { 67 | return question; 68 | } 69 | } 70 | 71 | public List getChoices() { 72 | return choices; 73 | } 74 | 75 | public void setChoices(List choices) { 76 | this.choices = choices; 77 | } 78 | 79 | public String getTitle() { 80 | return title; 81 | } 82 | 83 | public void setTitle(String title) { 84 | this.title = title; 85 | } 86 | 87 | public Course getCourse() { 88 | return course; 89 | } 90 | 91 | public void setCourse(Course course) { 92 | this.course = course; 93 | } 94 | 95 | public Professor getProfessor() { 96 | return professor; 97 | } 98 | 99 | public void setProfessor(Professor professor) { 100 | this.professor = professor; 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/security/filter/JWTAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.security.filter; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.ApplicationUser; 4 | import br.com.devdojo.examgenerator.security.service.CustomUserDetailsService; 5 | import io.jsonwebtoken.Jwts; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 11 | 12 | import javax.servlet.FilterChain; 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | 18 | import static br.com.devdojo.examgenerator.security.filter.Constants.*; 19 | 20 | /** 21 | * @author William Suane for DevDojo on 10/10/17. 22 | */ 23 | public class JWTAuthorizationFilter extends BasicAuthenticationFilter { 24 | private final CustomUserDetailsService customUserDetailsService; 25 | 26 | public JWTAuthorizationFilter(AuthenticationManager authenticationManager, 27 | CustomUserDetailsService customUserDetailsService) { 28 | super(authenticationManager); 29 | this.customUserDetailsService = customUserDetailsService; 30 | } 31 | 32 | @Override 33 | protected void doFilterInternal(HttpServletRequest request, 34 | HttpServletResponse response, 35 | FilterChain chain) throws IOException, ServletException { 36 | String header = request.getHeader(HEADER_STRING); 37 | if (header == null || !header.startsWith(TOKEN_PREFIX)) { 38 | chain.doFilter(request, response); 39 | return; 40 | } 41 | UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request); 42 | SecurityContextHolder.getContext().setAuthentication(authenticationToken); 43 | chain.doFilter(request, response); 44 | } 45 | 46 | private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) { 47 | String token = request.getHeader(HEADER_STRING); 48 | if (token == null) return null; 49 | String username = Jwts.parser().setSigningKey(SECRET) 50 | .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) 51 | .getBody() 52 | .getSubject(); 53 | UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); 54 | ApplicationUser applicationUser = customUserDetailsService.loadApplicationUserByUsername(username); 55 | return username != null ? new UsernamePasswordAuthenticationToken(applicationUser, null, userDetails.getAuthorities()) : null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/security/service/CustomUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.security.service; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.ApplicationUser; 4 | import br.com.devdojo.examgenerator.persistence.respository.ApplicationUserRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.AuthorityUtils; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.Collection; 14 | import java.util.List; 15 | import java.util.Optional; 16 | 17 | /** 18 | * @author William Suane for DevDojo on 10/10/17. 19 | */ 20 | @Service 21 | public class CustomUserDetailsService implements UserDetailsService { 22 | private final ApplicationUserRepository applicationUserRepository; 23 | 24 | @Autowired 25 | public CustomUserDetailsService(ApplicationUserRepository applicationUserRepository) { 26 | this.applicationUserRepository = applicationUserRepository; 27 | } 28 | 29 | @Override 30 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 31 | ApplicationUser applicationUser = loadApplicationUserByUsername(username); 32 | return new CustomUserDetails(applicationUser); 33 | } 34 | 35 | public ApplicationUser loadApplicationUserByUsername(String username) { 36 | return Optional.ofNullable(applicationUserRepository.findByUsername(username)) 37 | .orElseThrow(() -> new UsernameNotFoundException("ApplicationUser not found")); 38 | } 39 | 40 | private final static class CustomUserDetails extends ApplicationUser implements UserDetails { 41 | private CustomUserDetails(ApplicationUser applicationUser) { 42 | super(applicationUser); 43 | } 44 | 45 | @Override 46 | public Collection getAuthorities() { 47 | List authorityListProfessor = AuthorityUtils.createAuthorityList("ROLE_PROFESSOR"); 48 | List authorityListStudent = AuthorityUtils.createAuthorityList("ROLE_STUDENT"); 49 | return this.getProfessor() != null ? authorityListProfessor : authorityListStudent; 50 | } 51 | 52 | @Override 53 | public boolean isAccountNonExpired() { 54 | return true; 55 | } 56 | 57 | @Override 58 | public boolean isAccountNonLocked() { 59 | return true; 60 | } 61 | 62 | @Override 63 | public boolean isCredentialsNonExpired() { 64 | return true; 65 | } 66 | 67 | @Override 68 | public boolean isEnabled() { 69 | return true; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/QuestionAssignment.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.ManyToOne; 8 | @Entity 9 | @ApiModel(description = "This will be the class responsible for generating the exam for the students," + 10 | "The sum of all questions must be equal to 100") 11 | public class QuestionAssignment extends AbstractEntity { 12 | @ManyToOne(optional = false) 13 | private Professor professor; 14 | @ManyToOne 15 | @ApiModelProperty(notes = "The question with at least two choices and one of them is true") 16 | private Question question; 17 | @ManyToOne 18 | @ApiModelProperty(notes = "The assignment this question belongs to") 19 | private Assignment assignment; 20 | @ApiModelProperty(notes = "The grade value for the question") 21 | private double grade; 22 | 23 | 24 | public Professor getProfessor() { 25 | return professor; 26 | } 27 | 28 | public void setProfessor(Professor professor) { 29 | this.professor = professor; 30 | } 31 | 32 | public Question getQuestion() { 33 | return question; 34 | } 35 | 36 | public void setQuestion(Question question) { 37 | this.question = question; 38 | } 39 | 40 | public Assignment getAssignment() { 41 | return assignment; 42 | } 43 | 44 | public void setAssignment(Assignment assignment) { 45 | this.assignment = assignment; 46 | } 47 | 48 | public double getGrade() { 49 | return grade; 50 | } 51 | 52 | public void setGrade(double grade) { 53 | this.grade = grade; 54 | } 55 | 56 | public static final class Builder { 57 | private QuestionAssignment questionAssignment; 58 | 59 | private Builder() { 60 | questionAssignment = new QuestionAssignment(); 61 | } 62 | 63 | public static Builder newQuestionAssignment() { 64 | return new Builder(); 65 | } 66 | 67 | public Builder id(Long id) { 68 | questionAssignment.setId(id); 69 | return this; 70 | } 71 | 72 | public Builder enabled(boolean enabled) { 73 | questionAssignment.setEnabled(enabled); 74 | return this; 75 | } 76 | 77 | public Builder professor(Professor professor) { 78 | questionAssignment.setProfessor(professor); 79 | return this; 80 | } 81 | 82 | public Builder question(Question question) { 83 | questionAssignment.setQuestion(question); 84 | return this; 85 | } 86 | 87 | public Builder assignment(Assignment assignment) { 88 | questionAssignment.setAssignment(assignment); 89 | return this; 90 | } 91 | 92 | public Builder grade(double grade) { 93 | questionAssignment.setGrade(grade); 94 | return this; 95 | } 96 | 97 | public QuestionAssignment build() { 98 | return questionAssignment; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Choice.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonManagedReference; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import org.hibernate.validator.constraints.NotEmpty; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.ManyToOne; 10 | import javax.validation.constraints.NotNull; 11 | 12 | /** 13 | * @author William Suane for DevDojo on 11/20/17. 14 | */ 15 | @Entity 16 | public class Choice extends AbstractEntity{ 17 | @NotEmpty(message = "The field title cannot be empty") 18 | @ApiModelProperty(notes = "The title of the choice") 19 | private String title; 20 | @NotNull(message = "The field correctAnswer must be true or false") 21 | @ApiModelProperty(notes = "Correct answer for the associated question, you can have only one correct answer per question") 22 | @Column(columnDefinition = "boolean default false", nullable = false) 23 | private boolean correctAnswer = false; 24 | @ManyToOne(optional = false) 25 | private Question question; 26 | @ManyToOne(optional = false) 27 | private Professor professor; 28 | 29 | 30 | public static final class Builder { 31 | private Choice choice; 32 | 33 | private Builder() { 34 | choice = new Choice(); 35 | } 36 | 37 | public static Builder newChoice() { 38 | return new Builder(); 39 | } 40 | 41 | public Builder id(Long id) { 42 | choice.setId(id); 43 | return this; 44 | } 45 | 46 | public Builder enabled(boolean enabled) { 47 | choice.setEnabled(enabled); 48 | return this; 49 | } 50 | 51 | public Builder title(String title) { 52 | choice.setTitle(title); 53 | return this; 54 | } 55 | 56 | public Builder correctAnswer(boolean correctAnswer) { 57 | choice.setCorrectAnswer(correctAnswer); 58 | return this; 59 | } 60 | 61 | public Builder question(Question question) { 62 | choice.setQuestion(question); 63 | return this; 64 | } 65 | 66 | public Builder professor(Professor professor) { 67 | choice.setProfessor(professor); 68 | return this; 69 | } 70 | 71 | public Choice build() { 72 | return choice; 73 | } 74 | } 75 | 76 | public String getTitle() { 77 | return title; 78 | } 79 | 80 | public void setTitle(String title) { 81 | this.title = title; 82 | } 83 | 84 | public boolean isCorrectAnswer() { 85 | return correctAnswer; 86 | } 87 | 88 | public void setCorrectAnswer(boolean correctAnswer) { 89 | this.correctAnswer = correctAnswer; 90 | } 91 | 92 | public Question getQuestion() { 93 | return question; 94 | } 95 | 96 | public void setQuestion(Question question) { 97 | this.question = question; 98 | } 99 | 100 | public Professor getProfessor() { 101 | return professor; 102 | } 103 | 104 | public void setProfessor(Professor professor) { 105 | this.professor = professor; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/Assignment.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.ManyToOne; 8 | import java.time.LocalDateTime; 9 | /** 10 | * @author William Suane for DevDojo on 12/07/17. 11 | */ 12 | 13 | @Entity 14 | public class Assignment extends AbstractEntity { 15 | @NotEmpty(message = "The field title cannot be empty") 16 | @ApiModelProperty(notes = "The title of the assignment") 17 | private String title; 18 | private LocalDateTime createdAt = LocalDateTime.now(); 19 | @ManyToOne(optional = false) 20 | private Course course; 21 | @ManyToOne(optional = false) 22 | private Professor professor; 23 | private String accessCode; 24 | 25 | public String getAccessCode() { 26 | return accessCode; 27 | } 28 | 29 | public void setAccessCode(String accessCode) { 30 | this.accessCode = accessCode; 31 | } 32 | 33 | public String getTitle() { 34 | return title; 35 | } 36 | 37 | public void setTitle(String title) { 38 | this.title = title; 39 | } 40 | 41 | public LocalDateTime getCreatedAt() { 42 | return createdAt; 43 | } 44 | 45 | public void setCreatedAt(LocalDateTime createdAt) { 46 | this.createdAt = createdAt; 47 | } 48 | 49 | public Course getCourse() { 50 | return course; 51 | } 52 | 53 | public void setCourse(Course course) { 54 | this.course = course; 55 | } 56 | 57 | public Professor getProfessor() { 58 | return professor; 59 | } 60 | 61 | public void setProfessor(Professor professor) { 62 | this.professor = professor; 63 | } 64 | 65 | 66 | public static final class Builder { 67 | private Assignment assignment; 68 | 69 | private Builder() { 70 | assignment = new Assignment(); 71 | } 72 | 73 | public static Builder newAssignment() { 74 | return new Builder(); 75 | } 76 | 77 | public Builder id(Long id) { 78 | assignment.setId(id); 79 | return this; 80 | } 81 | 82 | public Builder enabled(boolean enabled) { 83 | assignment.setEnabled(enabled); 84 | return this; 85 | } 86 | 87 | public Builder title(String title) { 88 | assignment.setTitle(title); 89 | return this; 90 | } 91 | 92 | public Builder createdAt(LocalDateTime createdAt) { 93 | assignment.setCreatedAt(createdAt); 94 | return this; 95 | } 96 | 97 | public Builder course(Course course) { 98 | assignment.setCourse(course); 99 | return this; 100 | } 101 | 102 | public Builder professor(Professor professor) { 103 | assignment.setProfessor(professor); 104 | return this; 105 | } 106 | 107 | public Builder accessCode(String accessCode) { 108 | assignment.setAccessCode(accessCode); 109 | return this; 110 | } 111 | 112 | public Assignment build() { 113 | return assignment; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/security/filter/JWTAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.security.filter; 2 | 3 | import br.com.devdojo.examgenerator.persistence.model.ApplicationUser; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.AuthenticationException; 11 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 12 | 13 | import javax.servlet.FilterChain; 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | import java.time.ZoneOffset; 19 | import java.time.ZonedDateTime; 20 | import java.time.temporal.ChronoUnit; 21 | import java.util.Date; 22 | 23 | import static br.com.devdojo.examgenerator.security.filter.Constants.*; 24 | 25 | /** 26 | * @author William Suane for DevDojo on 10/10/17. 27 | */ 28 | public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 29 | private AuthenticationManager authenticationManager; 30 | 31 | public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { 32 | this.authenticationManager = authenticationManager; 33 | } 34 | 35 | @Override 36 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 37 | try { 38 | ApplicationUser user = new ObjectMapper().readValue(request.getInputStream(), ApplicationUser.class); 39 | return authenticationManager 40 | .authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())); 41 | } catch (IOException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | @Override 47 | protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { 48 | ZonedDateTime expTimeUTC = ZonedDateTime.now(ZoneOffset.UTC).plus(EXPIRATION_TIME, ChronoUnit.MILLIS); 49 | String accessType = ((ApplicationUser) authResult.getPrincipal()).getProfessor() != null ? "professor" : "student"; 50 | String token = Jwts.builder() 51 | .setSubject(((ApplicationUser) authResult.getPrincipal()).getUsername()) 52 | .setExpiration(Date.from(expTimeUTC.toInstant())) 53 | .claim("accessType", accessType) 54 | .signWith(SignatureAlgorithm.HS256, SECRET) 55 | .compact(); 56 | token = TOKEN_PREFIX + token; 57 | String tokenJson = String.format("{\"token\": %s, \"exp\": %s, \"accessType\": %s}", addQuotes(token), addQuotes(expTimeUTC.toString()), addQuotes(accessType)); 58 | response.getWriter().write(tokenJson); 59 | response.addHeader("Content-Type", "application/json;charset=UTF-8"); 60 | response.addHeader(HEADER_STRING, token); 61 | } 62 | 63 | private String addQuotes(String value) { 64 | return new StringBuilder(300).append("\"").append(value).append("\"").toString(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/course/CourseEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.course; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.deleteservice.CascadeDeleteService; 4 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 5 | import br.com.devdojo.examgenerator.persistence.model.Course; 6 | import br.com.devdojo.examgenerator.persistence.respository.CourseRepository; 7 | import br.com.devdojo.examgenerator.util.EndpointUtil; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import io.swagger.annotations.ApiParam; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.transaction.annotation.Transactional; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.validation.Valid; 17 | 18 | import static org.springframework.http.HttpStatus.OK; 19 | 20 | /** 21 | * @author William Suane for DevDojo on 10/27/17. 22 | */ 23 | @RestController 24 | @RequestMapping("v1/professor/course") 25 | @Api(description = "Operations related to professors' course") 26 | public class CourseEndpoint { 27 | private final CourseRepository courseRepository; 28 | private final GenericService service; 29 | private final CascadeDeleteService deleteService; 30 | private final EndpointUtil endpointUtil; 31 | 32 | @Autowired 33 | public CourseEndpoint(CourseRepository courseRepository, GenericService service, 34 | CascadeDeleteService deleteService, EndpointUtil endpointUtil) { 35 | this.courseRepository = courseRepository; 36 | this.service = service; 37 | this.deleteService = deleteService; 38 | this.endpointUtil = endpointUtil; 39 | } 40 | 41 | @ApiOperation(value = "Return a course based on it's id", response = Course.class) 42 | @GetMapping(path = "{id}") 43 | public ResponseEntity getCourseById(@PathVariable long id) { 44 | return endpointUtil.returnObjectOrNotFound(courseRepository.findOne(id)); 45 | } 46 | 47 | @ApiOperation(value = "Return a list of courses related to professor", response = Course.class) 48 | @GetMapping(path = "list") 49 | public ResponseEntity listCourses(@ApiParam("Course name") @RequestParam(value = "name", defaultValue = "") String name) { 50 | return new ResponseEntity<>(courseRepository.listCoursesByName(name), OK); 51 | } 52 | 53 | @ApiOperation(value = "Delete a specific course and all related questions and choices and return 200 Ok with no body") 54 | @DeleteMapping(path = "{id}") 55 | @Transactional 56 | public ResponseEntity delete(@PathVariable long id) { 57 | validateCourseExistenceOnDB(id); 58 | deleteService.deleteCourseAndAllRelatedEntities(id); 59 | return new ResponseEntity<>(OK); 60 | } 61 | 62 | @ApiOperation(value = "Update course and return 200 Ok with no body") 63 | @PutMapping 64 | public ResponseEntity update(@Valid @RequestBody Course course) { 65 | validateCourseExistenceOnDB(course.getId()); 66 | courseRepository.save(course); 67 | return new ResponseEntity<>(OK); 68 | } 69 | 70 | private void validateCourseExistenceOnDB(Long id) { 71 | service.throwResourceNotFoundIfDoesNotExist(id, courseRepository, "Course not found"); 72 | } 73 | 74 | @ApiOperation(value = "Create course and return the course created") 75 | @PostMapping 76 | public ResponseEntity create(@Valid @RequestBody Course course) { 77 | course.setProfessor(endpointUtil.extractProfessorFromToken()); 78 | return new ResponseEntity<>(courseRepository.save(course), OK); 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | br.com.devdojo 7 | demo 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | exam-generator 12 | Project from DevDojo 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.hibernate 30 | hibernate-java8 31 | 5.0.12.Final 32 | 33 | 34 | 35 | com.fasterxml.jackson.datatype 36 | jackson-datatype-jsr310 37 | 2.9.1 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-data-jpa 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-security 47 | 48 | 49 | org.springframework.security 50 | spring-security-data 51 | 4.2.3.RELEASE 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-web 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-test 61 | test 62 | 63 | 64 | org.springframework.security 65 | spring-security-test 66 | test 67 | 68 | 69 | mysql 70 | mysql-connector-java 71 | 72 | 73 | io.jsonwebtoken 74 | jjwt 75 | 0.8.0 76 | 77 | 78 | io.springfox 79 | springfox-swagger2 80 | 2.7.0 81 | compile 82 | 83 | 84 | io.springfox 85 | springfox-swagger-ui 86 | 2.7.0 87 | compile 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-maven-plugin 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/question/QuestionEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.question; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.deleteservice.CascadeDeleteService; 4 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 5 | import br.com.devdojo.examgenerator.persistence.model.Question; 6 | import br.com.devdojo.examgenerator.persistence.respository.CourseRepository; 7 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 8 | import br.com.devdojo.examgenerator.util.EndpointUtil; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import io.swagger.annotations.ApiParam; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.transaction.annotation.Transactional; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.validation.Valid; 18 | 19 | import static org.springframework.http.HttpStatus.OK; 20 | 21 | /** 22 | * @author William Suane for DevDojo on 10/27/17. 23 | */ 24 | @RestController 25 | @RequestMapping("v1/professor/course/question") 26 | @Api(description = "Operations related to courses' question") 27 | public class QuestionEndpoint { 28 | private final QuestionRepository questionRepository; 29 | private final CourseRepository courseRepository; 30 | private final GenericService service; 31 | private final CascadeDeleteService deleteService; 32 | private final EndpointUtil endpointUtil; 33 | 34 | @Autowired 35 | public QuestionEndpoint(QuestionRepository questionRepository, 36 | CourseRepository courseRepository, GenericService service, 37 | CascadeDeleteService deleteService, EndpointUtil endpointUtil) { 38 | this.questionRepository = questionRepository; 39 | this.courseRepository = courseRepository; 40 | this.service = service; 41 | this.deleteService = deleteService; 42 | this.endpointUtil = endpointUtil; 43 | } 44 | 45 | @ApiOperation(value = "Return a question based on it's id", response = Question.class) 46 | @GetMapping(path = "{id}") 47 | public ResponseEntity getQuestionById(@PathVariable long id) { 48 | return endpointUtil.returnObjectOrNotFound(questionRepository.findOne(id)); 49 | } 50 | 51 | @ApiOperation(value = "Return a list of question related to course", response = Question[].class) 52 | @GetMapping(path = "list/{courseId}/") 53 | public ResponseEntity listQuestions(@PathVariable long courseId, 54 | @ApiParam("Question title") @RequestParam(value = "title", defaultValue = "") String title) { 55 | return new ResponseEntity<>(questionRepository.listQuestionsByCourseAndTitle(courseId, title), OK); 56 | } 57 | 58 | @ApiOperation(value = "Delete a specific question and all related choices and return 200 Ok with no body") 59 | @DeleteMapping(path = "{id}") 60 | @Transactional 61 | public ResponseEntity delete(@PathVariable long id) { 62 | validateQuestionExistenceOnDB(id); 63 | deleteService.deleteQuestionAndAllRelatedEntities(id); 64 | return new ResponseEntity<>(OK); 65 | } 66 | 67 | @ApiOperation(value = "Update question and return 200 Ok with no body") 68 | @PutMapping 69 | public ResponseEntity update(@Valid @RequestBody Question question) { 70 | validateQuestionExistenceOnDB(question.getId()); 71 | questionRepository.save(question); 72 | return new ResponseEntity<>(OK); 73 | } 74 | 75 | private void validateQuestionExistenceOnDB(Long id) { 76 | service.throwResourceNotFoundIfDoesNotExist(id, questionRepository, "Question not found"); 77 | } 78 | 79 | @ApiOperation(value = "Create question and return the question created", response = Question.class) 80 | @PostMapping 81 | public ResponseEntity create(@Valid @RequestBody Question question) { 82 | service.throwResourceNotFoundIfDoesNotExist(question.getCourse(), courseRepository, "Course not found"); 83 | question.setProfessor(endpointUtil.extractProfessorFromToken()); 84 | return new ResponseEntity<>(questionRepository.save(question), OK); 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/assignment/AssignmentEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.assignment; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.deleteservice.CascadeDeleteService; 4 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 5 | import br.com.devdojo.examgenerator.persistence.model.Assignment; 6 | import br.com.devdojo.examgenerator.persistence.respository.AssignmentRepository; 7 | import br.com.devdojo.examgenerator.persistence.respository.CourseRepository; 8 | import br.com.devdojo.examgenerator.util.EndpointUtil; 9 | import io.swagger.annotations.Api; 10 | import io.swagger.annotations.ApiOperation; 11 | import io.swagger.annotations.ApiParam; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.transaction.annotation.Transactional; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.validation.Valid; 18 | import java.util.concurrent.ThreadLocalRandom; 19 | 20 | import static org.springframework.http.HttpStatus.OK; 21 | 22 | /** 23 | * @author William Suane for DevDojo on 10/27/17. 24 | */ 25 | @RestController 26 | @RequestMapping("v1/professor/course/assignment") 27 | @Api(description = "Operations related to courses' assignment") 28 | public class AssignmentEndpoint { 29 | private final AssignmentRepository assignmentRepository; 30 | private final CourseRepository courseRepository; 31 | private final GenericService service; 32 | private final EndpointUtil endpointUtil; 33 | private final CascadeDeleteService deleteService; 34 | 35 | @Autowired 36 | public AssignmentEndpoint(AssignmentRepository assignmentRepository, 37 | CourseRepository courseRepository, GenericService service, 38 | EndpointUtil endpointUtil, CascadeDeleteService deleteService) { 39 | this.assignmentRepository = assignmentRepository; 40 | this.courseRepository = courseRepository; 41 | this.service = service; 42 | this.endpointUtil = endpointUtil; 43 | this.deleteService = deleteService; 44 | } 45 | 46 | @ApiOperation(value = "Return an assignment based on it's id", response = Assignment.class) 47 | @GetMapping(path = "{id}") 48 | public ResponseEntity getAssignmentById(@PathVariable long id) { 49 | return endpointUtil.returnObjectOrNotFound(assignmentRepository.findOne(id)); 50 | } 51 | 52 | @ApiOperation(value = "Return a list of assignments related to course", response = Assignment[].class) 53 | @GetMapping(path = "list/{courseId}/") 54 | public ResponseEntity listAssignments(@PathVariable long courseId, 55 | @ApiParam("Assignment title") @RequestParam(value = "title", defaultValue = "") String title) { 56 | return new ResponseEntity<>(assignmentRepository.listAssignemntsByCourseAndTitle(courseId, title), OK); 57 | } 58 | 59 | @ApiOperation(value = "Delete a specific assignment return 200 Ok with no body") 60 | @DeleteMapping(path = "{id}") 61 | @Transactional 62 | public ResponseEntity delete(@PathVariable long id) { 63 | validateAssignmentExistenceOnDB(id); 64 | assignmentRepository.delete(id); 65 | deleteService.deleteAssignmentAndAllRelatedEntities(id); 66 | return new ResponseEntity<>(OK); 67 | } 68 | 69 | @ApiOperation(value = "Update assignment and return 200 Ok with no body") 70 | @PutMapping 71 | public ResponseEntity update(@Valid @RequestBody Assignment assignment) { 72 | validateAssignmentExistenceOnDB(assignment.getId()); 73 | assignmentRepository.save(assignment); 74 | return new ResponseEntity<>(OK); 75 | } 76 | 77 | private void validateAssignmentExistenceOnDB(Long id) { 78 | service.throwResourceNotFoundIfDoesNotExist(id, assignmentRepository, "Assignment not found"); 79 | } 80 | 81 | @ApiOperation(value = "Create assignment and return the assignment created") 82 | @PostMapping 83 | public ResponseEntity create(@Valid @RequestBody Assignment assignment) { 84 | service.throwResourceNotFoundIfDoesNotExist(assignment.getCourse(), courseRepository, "Course not found"); 85 | assignment.setProfessor(endpointUtil.extractProfessorFromToken()); 86 | assignment.setAccessCode(generateAccessCode(assignment.getCourse().getId())); 87 | return new ResponseEntity<>(assignmentRepository.save(assignment), OK); 88 | } 89 | 90 | private String generateAccessCode(long courseId) { 91 | long accessCode = ThreadLocalRandom.current().nextLong(1000, 10000); 92 | while (assignmentRepository.accessCodeExistsForCourse(String.valueOf(accessCode), courseId) != null) { 93 | generateAccessCode(courseId); 94 | } 95 | return String.valueOf(accessCode); 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/choice/ChoiceEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.choice; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 4 | import br.com.devdojo.examgenerator.exception.ConflictException; 5 | import br.com.devdojo.examgenerator.persistence.model.Choice; 6 | import br.com.devdojo.examgenerator.persistence.model.QuestionAssignment; 7 | import br.com.devdojo.examgenerator.persistence.respository.ChoiceRepository; 8 | import br.com.devdojo.examgenerator.persistence.respository.QuestionAssignmentRepository; 9 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 10 | import br.com.devdojo.examgenerator.util.EndpointUtil; 11 | import io.swagger.annotations.Api; 12 | import io.swagger.annotations.ApiOperation; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.transaction.annotation.Transactional; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import javax.validation.Valid; 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | import static org.springframework.http.HttpStatus.OK; 23 | 24 | /** 25 | * @author William Suane for DevDojo on 11/20/17. 26 | */ 27 | @RestController 28 | @RequestMapping("v1/professor/course/question/choice") 29 | @Api(description = "Operations related to questions' choice") 30 | public class ChoiceEndpoint { 31 | private final QuestionRepository questionRepository; 32 | private final ChoiceRepository choiceRepository; 33 | private final QuestionAssignmentRepository questionAssignmentRepository; 34 | private final GenericService service; 35 | private final EndpointUtil endpointUtil; 36 | 37 | @Autowired 38 | public ChoiceEndpoint(QuestionRepository questionRepository, 39 | ChoiceRepository choiceRepository, QuestionAssignmentRepository questionAssignmentRepository, GenericService service, 40 | EndpointUtil endpointUtil) { 41 | this.questionRepository = questionRepository; 42 | this.choiceRepository = choiceRepository; 43 | this.questionAssignmentRepository = questionAssignmentRepository; 44 | this.service = service; 45 | this.endpointUtil = endpointUtil; 46 | } 47 | @ApiOperation(value = "Return a choice based on it's id", response = Choice.class) 48 | @GetMapping(path = "{id}") 49 | public ResponseEntity getChoiceById(@PathVariable long id) { 50 | return endpointUtil.returnObjectOrNotFound(choiceRepository.findOne(id)); 51 | } 52 | 53 | @ApiOperation(value = "Return a list of choices related to the questionId", response = Choice[].class) 54 | @GetMapping(path = "list/{questionId}/") 55 | public ResponseEntity listChoicesByQuestionId(@PathVariable long questionId) { 56 | return new ResponseEntity<>(choiceRepository.listChoicesByQuestionId(questionId), OK); 57 | } 58 | 59 | @ApiOperation(value = "Create choice and return the choice created", 60 | notes = "If this choice's correctAnswer is true all other choices' correctAnswer related to this question will be updated to false") 61 | @PostMapping 62 | @Transactional 63 | public ResponseEntity create(@Valid @RequestBody Choice choice) { 64 | throwResourceNotFoundExceptionIfQuestionDoesNotExist(choice); 65 | choice.setProfessor(endpointUtil.extractProfessorFromToken()); 66 | Choice savedChoice = choiceRepository.save(choice); 67 | updateChangingOtherChoicesCorrectAnswerToFalse(choice); 68 | return new ResponseEntity<>(savedChoice, OK); 69 | } 70 | 71 | @ApiOperation(value = "Update choice and return 200 Ok with no body", 72 | notes = "If this choice's correctAnswer is true all other choices' correctAnswer related to this question will be updated to false") 73 | @PutMapping 74 | @Transactional 75 | public ResponseEntity update(@Valid @RequestBody Choice choice) { 76 | throwResourceNotFoundExceptionIfQuestionDoesNotExist(choice); 77 | updateChangingOtherChoicesCorrectAnswerToFalse(choice); 78 | choiceRepository.save(choice); 79 | return new ResponseEntity<>(OK); 80 | } 81 | 82 | @ApiOperation(value = "Delete a specific choice and return 200 Ok with no body") 83 | @DeleteMapping(path = "{id}") 84 | public ResponseEntity delete(@PathVariable long id) { 85 | service.throwResourceNotFoundIfDoesNotExist(id, choiceRepository, "Choice was not found"); 86 | Choice choice = choiceRepository.findOne(id); 87 | throwConflictExceptionIfQuestionIsBeingUsedInAnyAssignment(choice.getQuestion().getId()); 88 | choiceRepository.delete(id); 89 | return new ResponseEntity<>(OK); 90 | } 91 | 92 | private void throwConflictExceptionIfQuestionIsBeingUsedInAnyAssignment(long questionId) { 93 | List questionAssignments = questionAssignmentRepository.listQuestionAssignmentByQuestionId(questionId); 94 | if (questionAssignments.isEmpty()) return; 95 | String assignments = questionAssignments 96 | .stream() 97 | .map(qa -> qa.getAssignment().getTitle()) 98 | .collect(Collectors.joining(", ")); 99 | throw new ConflictException("This choice cannot be deleted because this question is being used in the following assignments: " + assignments); 100 | } 101 | 102 | private void throwResourceNotFoundExceptionIfQuestionDoesNotExist(@Valid @RequestBody Choice choice) { 103 | service.throwResourceNotFoundIfDoesNotExist(choice.getQuestion(), questionRepository, "The question related to this choice was not found"); 104 | } 105 | 106 | private void updateChangingOtherChoicesCorrectAnswerToFalse(Choice choice) { 107 | if (choice.isCorrectAnswer()) 108 | choiceRepository.updateAllOtherChoicesCorrectAnswerToFalse(choice, choice.getQuestion()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/exam/ExamEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.exam; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.deleteservice.CascadeDeleteService; 4 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 5 | import br.com.devdojo.examgenerator.exception.ConflictException; 6 | import br.com.devdojo.examgenerator.exception.ResourceNotFoundException; 7 | import br.com.devdojo.examgenerator.persistence.model.*; 8 | import br.com.devdojo.examgenerator.persistence.respository.*; 9 | import br.com.devdojo.examgenerator.util.EndpointUtil; 10 | import io.swagger.annotations.Api; 11 | import io.swagger.annotations.ApiOperation; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.transaction.annotation.Transactional; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.stream.Collectors; 20 | 21 | import static org.springframework.http.HttpStatus.OK; 22 | 23 | /** 24 | * @author William Suane for DevDojo on 04/13/18. 25 | */ 26 | @RestController 27 | @RequestMapping("v1/student/exam") 28 | @Api(description = "Operations to associate questions to an assignment") 29 | public class ExamEndpoint { 30 | private final QuestionRepository questionRepository; 31 | private final QuestionAssignmentRepository questionAssignmentRepository; 32 | private final ChoiceRepository choiceRepository; 33 | private final AssignmentRepository assignmentRepository; 34 | private final ExamAnswerRepository examAnswerRepository; 35 | private final GenericService service; 36 | private final CascadeDeleteService deleteService; 37 | private final EndpointUtil endpointUtil; 38 | 39 | @Autowired 40 | public ExamEndpoint(QuestionRepository questionRepository, 41 | QuestionAssignmentRepository questionAssignmentRepository, 42 | ChoiceRepository choiceRepository, AssignmentRepository assignmentRepository, 43 | ExamAnswerRepository examAnswerRepository, GenericService service, 44 | CascadeDeleteService deleteService, 45 | EndpointUtil endpointUtil) { 46 | this.questionRepository = questionRepository; 47 | this.questionAssignmentRepository = questionAssignmentRepository; 48 | this.choiceRepository = choiceRepository; 49 | this.assignmentRepository = assignmentRepository; 50 | this.examAnswerRepository = examAnswerRepository; 51 | this.service = service; 52 | this.deleteService = deleteService; 53 | this.endpointUtil = endpointUtil; 54 | } 55 | 56 | @ApiOperation(value = "Validate if the student already answered the exam, returns 409 if yes, 200 with no body if not") 57 | @GetMapping(path = "validate/{accessCode}") 58 | public ResponseEntity validateAccessCode(@PathVariable String accessCode) { 59 | throwConflictExceptionIfStudentAlreadyAnswered(accessCode); 60 | return new ResponseEntity<>(OK); 61 | } 62 | 63 | private void throwConflictExceptionIfStudentAlreadyAnswered(String accessCode) { 64 | throwResourceNotFoundExceptionIfAccessCodeDoesNotExists(accessCode); 65 | Assignment assignmentByAccessCode = assignmentRepository.findAssignmentByAccessCode(accessCode); 66 | boolean studentAlreadyAnswered = examAnswerRepository.existsExamAnswerByAssignmentIdAndStudentId(assignmentByAccessCode.getId(), endpointUtil.extractStudentFromToken().getId()); 67 | if (studentAlreadyAnswered) throw new ConflictException("This Exam was already answered"); 68 | } 69 | 70 | @ApiOperation(value = "List all Choices based on the Questions by the assignment access code", response = Choice[].class) 71 | @GetMapping(path = "choice/{accessCode}") 72 | public ResponseEntity listQuestionsFromQuestionAssignmentByAssignmentAccessCode(@PathVariable String accessCode) { 73 | throwResourceNotFoundExceptionIfAccessCodeDoesNotExists(accessCode); 74 | throwConflictExceptionIfStudentAlreadyAnswered(accessCode); 75 | List questions = questionAssignmentRepository.listQuestionsFromQuestionAssignmentByAssignmentAccessCode(accessCode); 76 | List questionsId = questions.stream().map(Question::getId).collect(Collectors.toList()); 77 | List choices = choiceRepository.listChoicesByQuestionsIdForStudent(questionsId); 78 | return new ResponseEntity<>(choices, OK); 79 | } 80 | 81 | private void throwResourceNotFoundExceptionIfAccessCodeDoesNotExists(String accessCode) { 82 | Assignment assignment = assignmentRepository.findAssignmentByAccessCode(accessCode); 83 | if (assignment == null) 84 | throw new ResourceNotFoundException("Invalid access code"); 85 | } 86 | 87 | @ApiOperation(value = "Save Student's answers") 88 | @PostMapping(path = "{accessCode}") 89 | @Transactional 90 | public ResponseEntity save(@PathVariable String accessCode, @RequestBody Map questionChoiceIdsMap) { 91 | Assignment assignment = assignmentRepository.findAssignmentByAccessCode(accessCode); 92 | if (assignment == null) throw new ResourceNotFoundException("Invalid access code"); 93 | internallySaveExamAnswer(questionChoiceIdsMap, assignment); 94 | return new ResponseEntity<>(OK); 95 | } 96 | 97 | private void internallySaveExamAnswer(Map questionChoiceIdsMap, Assignment assignment) { 98 | questionChoiceIdsMap.forEach((questionId, choiceId) -> { 99 | QuestionAssignment questionAssignment = questionAssignmentRepository.findQuestionAssignmentByAssignmentIdAndQuestionId(assignment.getId(), questionId); 100 | Choice selectedChoiceByStudent = choiceRepository.findOne(choiceId); 101 | Choice correctChoice = choiceRepository.findCorrectChoiceForQuestion(questionId); 102 | ExamAnswer examAnswer = ExamAnswer.ExamAnswerBuilder.newExamAnswer() 103 | .questionId(questionId) 104 | .assignmentId(assignment.getId()) 105 | .questionAssignmentId(questionAssignment.getId()) 106 | .professorId(assignment.getProfessor().getId()) 107 | .studentId(endpointUtil.extractStudentFromToken().getId()) 108 | .assignmentTitle(assignment.getTitle()) 109 | .questionTitle(questionAssignment.getQuestion().getTitle()) 110 | .choiceGrade(questionAssignment.getGrade()) 111 | .answerGrade(selectedChoiceByStudent.isCorrectAnswer() ? questionAssignment.getGrade() : 0) 112 | .selectedChoiceId(selectedChoiceByStudent.getId()) 113 | .selectedChoiceTitle(selectedChoiceByStudent.getTitle()) 114 | .correctChoiceId(correctChoice.getId()) 115 | .correctChoiceTitle(correctChoice.getTitle()) 116 | .build(); 117 | examAnswerRepository.save(examAnswer); 118 | }); 119 | } 120 | 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/course/CourseEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.course; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.ProfessorEndpointTest; 4 | import br.com.devdojo.examgenerator.persistence.model.Course; 5 | import br.com.devdojo.examgenerator.persistence.respository.CourseRepository; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.BDDMockito; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.boot.test.web.client.TestRestTemplate; 15 | import org.springframework.core.ParameterizedTypeReference; 16 | import org.springframework.http.HttpEntity; 17 | import org.springframework.http.HttpHeaders; 18 | import org.springframework.http.ResponseEntity; 19 | import org.springframework.test.context.junit4.SpringRunner; 20 | 21 | import java.util.Collections; 22 | import java.util.List; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | import static org.springframework.http.HttpMethod.DELETE; 26 | import static org.springframework.http.HttpMethod.GET; 27 | import static org.springframework.http.HttpMethod.POST; 28 | 29 | /** 30 | * @author William Suane for DevDojo on 11/3/17. 31 | */ 32 | @RunWith(SpringRunner.class) 33 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 34 | @AutoConfigureMockMvc 35 | public class CourseEndpointTest { 36 | @MockBean 37 | private CourseRepository courseRepository; 38 | @Autowired 39 | private TestRestTemplate testRestTemplate; 40 | private HttpEntity professorHeader; 41 | private HttpEntity wrongHeader; 42 | private Course course = mockCourse(); 43 | 44 | public static Course mockCourse() { 45 | return Course.Builder.newCourse() 46 | .id(1L) 47 | .name("Java") 48 | .professor(ProfessorEndpointTest.mockProfessor()) 49 | .build(); 50 | } 51 | 52 | @Before 53 | public void configProfessorHeader() { 54 | String body = "{\"username\":\"william\",\"password\":\"devdojo\"}"; 55 | HttpHeaders headers = testRestTemplate.postForEntity("/login", body, String.class).getHeaders(); 56 | this.professorHeader = new HttpEntity<>(headers); 57 | } 58 | 59 | @Before 60 | public void configWrongHeader() { 61 | HttpHeaders headers = new HttpHeaders(); 62 | headers.add("Authorization", "1111"); 63 | this.wrongHeader = new HttpEntity<>(headers); 64 | } 65 | 66 | @Before 67 | public void setup() { 68 | BDDMockito.when(courseRepository.findOne(course.getId())).thenReturn(course); 69 | BDDMockito.when(courseRepository.listCoursesByName("")).thenReturn(Collections.singletonList(course)); 70 | BDDMockito.when(courseRepository.listCoursesByName("java")).thenReturn(Collections.singletonList(course)); 71 | } 72 | 73 | @Test 74 | public void getCourseByIdWhenTokenIsWrongShouldReturn403() throws Exception { 75 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/1", GET, wrongHeader, String.class); 76 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 77 | } 78 | 79 | @Test 80 | public void listCoursesWhenTokenIsWrongShouldReturn403() throws Exception { 81 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/list?name=", GET, wrongHeader, String.class); 82 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 83 | } 84 | 85 | @Test 86 | public void listAllCoursesWhenNameDoesNotExistsShouldReturnEmptyList() throws Exception { 87 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/list?name=xaxa", GET, 88 | professorHeader, new ParameterizedTypeReference>() { 89 | }); 90 | assertThat(exchange.getBody()).isEmpty(); 91 | } 92 | 93 | @Test 94 | public void listAllCoursesWhenNameExistsShouldReturn200() throws Exception { 95 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/list?name=java", GET, professorHeader, String.class); 96 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 97 | } 98 | 99 | @Test 100 | public void getCourseByIdWithoutIdShouldReturn400() throws Exception { 101 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/", GET, professorHeader, String.class); 102 | assertThat(exchange.getStatusCodeValue()).isEqualTo(400); 103 | } 104 | 105 | @Test 106 | public void getCourseByIdWhenCourseIdDoesNotExistsShouldReturn404() throws Exception { 107 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/-1", GET, professorHeader, String.class); 108 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 109 | } 110 | 111 | @Test 112 | public void getCourseByIdWhenCourseExistsShouldReturn200() throws Exception { 113 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/1", GET, professorHeader, String.class); 114 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 115 | } 116 | 117 | @Test 118 | public void deleteCourseWhenIdExistsShouldReturn200() throws Exception { 119 | long id = 1L; 120 | BDDMockito.doNothing().when(courseRepository).delete(id); 121 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/{id}", DELETE, professorHeader, String.class, id); 122 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 123 | } 124 | 125 | @Test 126 | public void deleteCourseWhenIdDoesNotExistsShouldReturn404() throws Exception { 127 | long id = -1L; 128 | BDDMockito.doNothing().when(courseRepository).delete(id); 129 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/{id}", DELETE, professorHeader, String.class, id); 130 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 131 | } 132 | 133 | @Test 134 | public void createCourseWhenNameIsNullShouldReturn400() throws Exception { 135 | Course course = courseRepository.findOne(1L); 136 | course.setName(null); 137 | assertThat(createCourse(course).getStatusCodeValue()).isEqualTo(400); 138 | } 139 | @Test 140 | public void createCourseWhenEverythingIsRightShouldReturn200() throws Exception { 141 | Course course = courseRepository.findOne(1L); 142 | course.setId(null); 143 | assertThat(createCourse(course).getStatusCodeValue()).isEqualTo(200); 144 | } 145 | 146 | private ResponseEntity createCourse(Course course) { 147 | BDDMockito.when(courseRepository.save(course)).thenReturn(course); 148 | return testRestTemplate.exchange("/v1/professor/course/", POST, 149 | new HttpEntity<>(course, professorHeader.getHeaders()), String.class); 150 | } 151 | 152 | } -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/endpoint/v1/questionassignment/QuestionAssignmentEndpoint.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.questionassignment; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.deleteservice.CascadeDeleteService; 4 | import br.com.devdojo.examgenerator.endpoint.v1.genericservice.GenericService; 5 | import br.com.devdojo.examgenerator.persistence.model.Choice; 6 | import br.com.devdojo.examgenerator.persistence.model.Question; 7 | import br.com.devdojo.examgenerator.persistence.model.QuestionAssignment; 8 | import br.com.devdojo.examgenerator.persistence.respository.AssignmentRepository; 9 | import br.com.devdojo.examgenerator.persistence.respository.QuestionAssignmentRepository; 10 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 11 | import br.com.devdojo.examgenerator.util.EndpointUtil; 12 | import io.swagger.annotations.Api; 13 | import io.swagger.annotations.ApiOperation; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.transaction.annotation.Transactional; 17 | import org.springframework.web.bind.annotation.*; 18 | 19 | import javax.validation.Valid; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | import static org.springframework.http.HttpStatus.NOT_MODIFIED; 24 | import static org.springframework.http.HttpStatus.OK; 25 | 26 | /** 27 | * @author William Suane for DevDojo on 12/15/17. 28 | */ 29 | @RestController 30 | @RequestMapping("v1/professor/course/assignment/questionassignment") 31 | @Api(description = "Operations to associate questions to an assignment") 32 | public class QuestionAssignmentEndpoint { 33 | private final QuestionRepository questionRepository; 34 | private final QuestionAssignmentRepository questionAssignmentRepository; 35 | private final AssignmentRepository assignmentRepository; 36 | private final GenericService service; 37 | private final CascadeDeleteService deleteService; 38 | private final EndpointUtil endpointUtil; 39 | 40 | @Autowired 41 | public QuestionAssignmentEndpoint(QuestionRepository questionRepository, 42 | QuestionAssignmentRepository questionAssignmentRepository, 43 | AssignmentRepository assignmentRepository, 44 | GenericService service, 45 | CascadeDeleteService deleteService, 46 | EndpointUtil endpointUtil) { 47 | this.questionRepository = questionRepository; 48 | this.questionAssignmentRepository = questionAssignmentRepository; 49 | this.assignmentRepository = assignmentRepository; 50 | this.service = service; 51 | this.deleteService = deleteService; 52 | this.endpointUtil = endpointUtil; 53 | } 54 | 55 | @ApiOperation(value = "Return valid questions for that course (valid questions are questions with at least two choices" + 56 | " and one of the choices is correct and it is not already associated with that assignment)", response = Question[].class) 57 | @GetMapping(path = "{courseId}/{assignmentId}") 58 | public ResponseEntity listValidQuestionsForAnAssignment(@PathVariable long courseId, @PathVariable long assignmentId) { 59 | List questions = questionRepository.listQuestionsByCourseNotAssociatedWithAnAssignment(courseId, assignmentId); 60 | List validQuestions = questions 61 | .stream() 62 | .filter(question -> hasMoreThanOneChoice(question) && hasOnlyOneCorrectAnswer(question)) 63 | .collect(Collectors.toList()); 64 | return new ResponseEntity<>(validQuestions, OK); 65 | } 66 | 67 | private boolean hasOnlyOneCorrectAnswer(Question question) { 68 | return question.getChoices().stream().filter(Choice::isCorrectAnswer).count() == 1; 69 | } 70 | 71 | private boolean hasMoreThanOneChoice(Question question) { 72 | return question.getChoices() != null && question.getChoices().size() > 1; 73 | } 74 | 75 | @ApiOperation(value = "Associate a question to an assignment and return the QuestionAssignment created", response = QuestionAssignment[].class) 76 | @PostMapping 77 | public ResponseEntity create(@Valid @RequestBody QuestionAssignment questionAssignment) { 78 | validateQuestionAndAssignmentExistence(questionAssignment); 79 | if (isQuestionAlreadyAssociatedWithAssignment(questionAssignment)) { 80 | return new ResponseEntity<>(NOT_MODIFIED); 81 | } 82 | questionAssignment.setProfessor(endpointUtil.extractProfessorFromToken()); 83 | return new ResponseEntity<>(questionAssignmentRepository.save(questionAssignment), OK); 84 | } 85 | 86 | private void validateQuestionAndAssignmentExistence(@Valid @RequestBody QuestionAssignment questionAssignment) { 87 | service.throwResourceNotFoundIfDoesNotExist(questionAssignment.getQuestion(), questionRepository, "Question not found"); 88 | service.throwResourceNotFoundIfDoesNotExist(questionAssignment.getAssignment(), assignmentRepository, "Assignment not found"); 89 | } 90 | 91 | private boolean isQuestionAlreadyAssociatedWithAssignment(QuestionAssignment questionAssignment) { 92 | long questionId = questionAssignment.getQuestion().getId(); 93 | long assignmentId = questionAssignment.getAssignment().getId(); 94 | List questionAssignments = questionAssignmentRepository.listQuestionAssignmentByQuestionAndAssignment(questionId, assignmentId); 95 | return !questionAssignments.isEmpty(); 96 | } 97 | 98 | @ApiOperation(value = "Delete a specific question assigned to an assignment and return 200 Ok with no body") 99 | @DeleteMapping(path = "{questionAssignmentId}") 100 | @Transactional 101 | public ResponseEntity delete(@PathVariable long questionAssignmentId) { 102 | validateQuestionAssignmentOnDB(questionAssignmentId); 103 | deleteService.deleteQuestionAssignmentAndAllRelatedEntities(questionAssignmentId); 104 | return new ResponseEntity<>(OK); 105 | } 106 | 107 | @ApiOperation(value = "Update QuestionAssignment and return 200 Ok with no body") 108 | @PutMapping 109 | public ResponseEntity update(@Valid @RequestBody QuestionAssignment questionAssignment) { 110 | validateQuestionAssignmentOnDB(questionAssignment.getId()); 111 | questionAssignmentRepository.save(questionAssignment); 112 | return new ResponseEntity<>(OK); 113 | } 114 | 115 | private void validateQuestionAssignmentOnDB(Long questionAssignmentId) { 116 | service.throwResourceNotFoundIfDoesNotExist(questionAssignmentId, questionAssignmentRepository, "QuestionAssignment not found"); 117 | 118 | } 119 | 120 | @ApiOperation(value = "List all QuestionAssignment associated with assignmentId", response = QuestionAssignment[].class) 121 | @GetMapping(path = "{assignmentId}") 122 | public ResponseEntity list(@PathVariable long assignmentId) { 123 | return new ResponseEntity<>(questionAssignmentRepository.listQuestionAssignmentByAssignmentId(assignmentId), OK); 124 | } 125 | 126 | 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/questionassignment/QuestionAssignmentEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.questionassignment; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.ProfessorEndpointTest; 4 | import br.com.devdojo.examgenerator.endpoint.v1.assignment.AssignmentEndpointTest; 5 | import br.com.devdojo.examgenerator.endpoint.v1.choice.ChoiceEndpointTest; 6 | import br.com.devdojo.examgenerator.endpoint.v1.course.CourseEndpointTest; 7 | import br.com.devdojo.examgenerator.endpoint.v1.question.QuestionEndpointTest; 8 | import br.com.devdojo.examgenerator.persistence.model.*; 9 | import br.com.devdojo.examgenerator.persistence.respository.AssignmentRepository; 10 | import br.com.devdojo.examgenerator.persistence.respository.QuestionAssignmentRepository; 11 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.mockito.BDDMockito; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.boot.test.mock.mockito.MockBean; 20 | import org.springframework.boot.test.web.client.TestRestTemplate; 21 | import org.springframework.core.ParameterizedTypeReference; 22 | import org.springframework.http.HttpEntity; 23 | import org.springframework.http.HttpHeaders; 24 | import org.springframework.http.ResponseEntity; 25 | import org.springframework.test.context.junit4.SpringRunner; 26 | 27 | import java.util.Collections; 28 | import java.util.List; 29 | 30 | import static java.util.Arrays.asList; 31 | import static org.assertj.core.api.Assertions.assertThat; 32 | import static org.springframework.http.HttpMethod.*; 33 | 34 | /** 35 | * @author William Suane on 11/02/2018 36 | */ 37 | @RunWith(SpringRunner.class) 38 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 39 | @AutoConfigureMockMvc 40 | public class QuestionAssignmentEndpointTest { 41 | @MockBean 42 | private QuestionRepository questionRepository; 43 | @MockBean 44 | private AssignmentRepository assignmentRepository; 45 | @MockBean 46 | private QuestionAssignmentRepository questionAssignmentRepository; 47 | @Autowired 48 | private TestRestTemplate testRestTemplate; 49 | private HttpEntity professorHeader; 50 | private HttpEntity wrongHeader; 51 | private Question question = QuestionEndpointTest.mockQuestion(); 52 | private Course course = CourseEndpointTest.mockCourse(); 53 | private Assignment assignment = AssignmentEndpointTest.mockAssignment(); 54 | private Choice choice1 = ChoiceEndpointTest.mockChoiceCorrectAnswerTrue(); 55 | private Choice choice2 = ChoiceEndpointTest.mockChoiceCorrectAnswerFalse(); 56 | private QuestionAssignment questionAssignment = mockQuestionAssignment(); 57 | 58 | public static QuestionAssignment mockQuestionAssignment() { 59 | return QuestionAssignment.Builder.newQuestionAssignment() 60 | .assignment(AssignmentEndpointTest.mockAssignment()) 61 | .professor(ProfessorEndpointTest.mockProfessor()) 62 | .question(QuestionEndpointTest.mockQuestion()) 63 | .grade(50) 64 | .id(1L) 65 | .build(); 66 | } 67 | 68 | @Before 69 | public void configProfessorHeader() { 70 | String body = "{\"username\":\"william\",\"password\":\"devdojo\"}"; 71 | HttpHeaders headers = testRestTemplate.postForEntity("/login", body, String.class).getHeaders(); 72 | this.professorHeader = new HttpEntity<>(headers); 73 | } 74 | 75 | @Before 76 | public void configWrongHeader() { 77 | HttpHeaders headers = new HttpHeaders(); 78 | headers.add("Authorization", "1111"); 79 | this.wrongHeader = new HttpEntity<>(headers); 80 | } 81 | 82 | @Before 83 | public void setup() { 84 | question.setChoices(asList(choice1, choice2)); 85 | BDDMockito.when(questionRepository.listQuestionsByCourseNotAssociatedWithAnAssignment(course.getId(), assignment.getId())).thenReturn(Collections.singletonList(question)); 86 | BDDMockito.when(questionRepository.findOne(question.getId())).thenReturn(question); 87 | BDDMockito.when(assignmentRepository.findOne(assignment.getId())).thenReturn(assignment); 88 | BDDMockito.when(questionAssignmentRepository.findOne(questionAssignment.getId())).thenReturn(questionAssignment); 89 | } 90 | 91 | @Test 92 | public void listValidQuestionsForAnAssignmentWhenTokenIsWrongShouldReturn403() throws Exception { 93 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/{courseId}/{assignmentId}", 94 | GET, wrongHeader, String.class, course.getId(), assignment.getId()); 95 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 96 | } 97 | 98 | @Test 99 | public void listValidQuestionsForAnAssignmentWhenCourseDoesNotExistShouldReturnEmptyList() throws Exception { 100 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/-1/1", GET, 101 | professorHeader, new ParameterizedTypeReference>() { 102 | }); 103 | assertThat(exchange.getBody()).isEmpty(); 104 | } 105 | 106 | @Test 107 | public void listValidQuestionsForAnAssignmentWhenAssignmentDoesNotExistShouldReturnEmptyList() throws Exception { 108 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/1/-1", GET, 109 | professorHeader, new ParameterizedTypeReference>() { 110 | }); 111 | assertThat(exchange.getBody()).isEmpty(); 112 | } 113 | 114 | @Test 115 | public void listValidQuestionsForAnAssignmentWhenAssignmentAndCourseExistAndHasValidQuestionsShouldReturnListWithOneElement() throws Exception { 116 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/1/1", GET, 117 | professorHeader, new ParameterizedTypeReference>() { 118 | }); 119 | assertThat(exchange.getBody().size()).isEqualTo(1); 120 | } 121 | 122 | @Test 123 | public void deleteQuestionAssignmentWhenIdExistsShouldReturn200() throws Exception { 124 | long id = questionAssignment.getId(); 125 | BDDMockito.doNothing().when(questionAssignmentRepository).delete(id); 126 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/{id}", DELETE, professorHeader, String.class, id); 127 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 128 | } 129 | 130 | @Test 131 | public void createQuestionAssignmentWhenEverythingIsRightShouldReturn200() throws Exception { 132 | QuestionAssignment questionAssignment = mockQuestionAssignment(); 133 | questionAssignment.setId(null); 134 | assertThat(createQuestionAssignment(questionAssignment).getStatusCodeValue()).isEqualTo(200); 135 | } 136 | 137 | private ResponseEntity createQuestionAssignment(QuestionAssignment questionAssignment) { 138 | BDDMockito.when(questionAssignmentRepository.save(questionAssignment)).thenReturn(questionAssignment); 139 | return testRestTemplate.exchange("/v1/professor/course/assignment/questionassignment/", POST, 140 | new HttpEntity<>(questionAssignment, professorHeader.getHeaders()), String.class); 141 | } 142 | 143 | } -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/assignment/AssignmentEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.assignment; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.ProfessorEndpointTest; 4 | import br.com.devdojo.examgenerator.endpoint.v1.course.CourseEndpointTest; 5 | import br.com.devdojo.examgenerator.persistence.model.Assignment; 6 | import br.com.devdojo.examgenerator.persistence.model.Course; 7 | import br.com.devdojo.examgenerator.persistence.respository.AssignmentRepository; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.BDDMockito; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.boot.test.mock.mockito.MockBean; 16 | import org.springframework.boot.test.web.client.TestRestTemplate; 17 | import org.springframework.core.ParameterizedTypeReference; 18 | import org.springframework.http.HttpEntity; 19 | import org.springframework.http.HttpHeaders; 20 | import org.springframework.http.ResponseEntity; 21 | import org.springframework.test.context.junit4.SpringRunner; 22 | 23 | import java.util.Collections; 24 | import java.util.List; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.springframework.http.HttpMethod.*; 28 | 29 | @RunWith(SpringRunner.class) 30 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 31 | @AutoConfigureMockMvc 32 | public class AssignmentEndpointTest { 33 | @MockBean 34 | private AssignmentRepository assignmentRepository; 35 | @Autowired 36 | private TestRestTemplate testRestTemplate; 37 | private HttpEntity professorHeader; 38 | private HttpEntity wrongHeader; 39 | private Assignment assignment = mockAssignment(); 40 | 41 | public static Assignment mockAssignment() { 42 | return Assignment.Builder.newAssignment() 43 | .id(1L) 44 | .title("The Java Exam from Hell?") 45 | .course(CourseEndpointTest.mockCourse()) 46 | .professor(ProfessorEndpointTest.mockProfessor()) 47 | .build(); 48 | } 49 | 50 | @Before 51 | public void configProfessorHeader() { 52 | String body = "{\"username\":\"william\",\"password\":\"devdojo\"}"; 53 | HttpHeaders headers = testRestTemplate.postForEntity("/login", body, String.class).getHeaders(); 54 | this.professorHeader = new HttpEntity<>(headers); 55 | } 56 | 57 | @Before 58 | public void configWrongHeader() { 59 | HttpHeaders headers = new HttpHeaders(); 60 | headers.add("Authorization", "1111"); 61 | this.wrongHeader = new HttpEntity<>(headers); 62 | } 63 | 64 | @Before 65 | public void setup() { 66 | BDDMockito.when(assignmentRepository.findOne(assignment.getId())).thenReturn(assignment); 67 | BDDMockito.when(assignmentRepository.listAssignemntsByCourseAndTitle(assignment.getCourse().getId(), "")).thenReturn(Collections.singletonList(assignment)); 68 | BDDMockito.when(assignmentRepository.listAssignemntsByCourseAndTitle(assignment.getCourse().getId(), "The Java")).thenReturn(Collections.singletonList(assignment)); 69 | } 70 | 71 | @Test 72 | public void getAssignmentByIdWhenTokenIsWrongShouldReturn403() throws Exception { 73 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/1", GET, wrongHeader, String.class); 74 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 75 | } 76 | 77 | @Test 78 | public void listAssignmentsByCourseAndTitleWhenTokenIsWrongShouldReturn403() throws Exception { 79 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/list/1/?title=", GET, wrongHeader, String.class); 80 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 81 | } 82 | 83 | @Test 84 | public void listAllAssignmentsByCourseAndTitleWhenTitleDoesNotExistsShouldReturnEmptyList() throws Exception { 85 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/assignment/list/1/?title=xaxa", GET, 86 | professorHeader, new ParameterizedTypeReference>() { 87 | }); 88 | assertThat(exchange.getBody()).isEmpty(); 89 | } 90 | 91 | @Test 92 | public void listAllAssignmentsByCourseWhenTitleExistsShouldReturn200() throws Exception { 93 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/list/1/?title=The Java", GET, professorHeader, String.class); 94 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 95 | } 96 | 97 | @Test 98 | public void getAssignmentByIdWithoutIdShouldReturn400() throws Exception { 99 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/", GET, professorHeader, String.class); 100 | assertThat(exchange.getStatusCodeValue()).isEqualTo(400); 101 | } 102 | 103 | @Test 104 | public void getAssignmentByIdWhenAssignmentIdDoesNotExistsShouldReturn404() throws Exception { 105 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/-1", GET, professorHeader, String.class); 106 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 107 | } 108 | 109 | @Test 110 | public void getAssignmentByIdWhenAssignmentExistsShouldReturn200() throws Exception { 111 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/1", GET, professorHeader, String.class); 112 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 113 | } 114 | 115 | @Test 116 | public void deleteAssignmentWhenIdExistsShouldReturn200() throws Exception { 117 | long id = 1L; 118 | BDDMockito.doNothing().when(assignmentRepository).delete(id); 119 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/{id}", DELETE, professorHeader, String.class, id); 120 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 121 | } 122 | 123 | @Test 124 | public void deleteAssignmentWhenIdDoesNotExistsShouldReturn404() throws Exception { 125 | long id = -1L; 126 | BDDMockito.doNothing().when(assignmentRepository).delete(id); 127 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/assignment/{id}", DELETE, professorHeader, String.class, id); 128 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 129 | } 130 | 131 | @Test 132 | public void createAssignmentWhenTitleIsNullShouldReturn400() throws Exception { 133 | Assignment assignment = assignmentRepository.findOne(1L); 134 | assignment.setTitle(null); 135 | assertThat(createAssignment(assignment).getStatusCodeValue()).isEqualTo(400); 136 | } 137 | 138 | @Test 139 | public void createAssignmentWhenCourseDoesNotExistsShouldReturn404() throws Exception { 140 | Assignment assignment = assignmentRepository.findOne(1L); 141 | assignment.setCourse(new Course()); 142 | assertThat(createAssignment(assignment).getStatusCodeValue()).isEqualTo(404); 143 | } 144 | 145 | @Test 146 | public void createAssignmentWhenEverythingIsRightShouldReturn200() throws Exception { 147 | Assignment assignment = assignmentRepository.findOne(1L); 148 | assignment.setId(null); 149 | assertThat(createAssignment(assignment).getStatusCodeValue()).isEqualTo(200); 150 | } 151 | 152 | private ResponseEntity createAssignment(Assignment assignment) { 153 | BDDMockito.when(assignmentRepository.save(assignment)).thenReturn(assignment); 154 | return testRestTemplate.exchange("/v1/professor/course/assignment/", POST, 155 | new HttpEntity<>(assignment, professorHeader.getHeaders()), String.class); 156 | } 157 | } -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/question/QuestionEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.question; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.ProfessorEndpointTest; 4 | import br.com.devdojo.examgenerator.endpoint.v1.course.CourseEndpointTest; 5 | import br.com.devdojo.examgenerator.persistence.model.Course; 6 | import br.com.devdojo.examgenerator.persistence.model.Question; 7 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.BDDMockito; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.boot.test.mock.mockito.MockBean; 16 | import org.springframework.boot.test.web.client.TestRestTemplate; 17 | import org.springframework.core.ParameterizedTypeReference; 18 | import org.springframework.http.HttpEntity; 19 | import org.springframework.http.HttpHeaders; 20 | import org.springframework.http.ResponseEntity; 21 | import org.springframework.test.context.junit4.SpringRunner; 22 | 23 | import java.util.Collections; 24 | import java.util.List; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.springframework.http.HttpMethod.DELETE; 28 | import static org.springframework.http.HttpMethod.GET; 29 | import static org.springframework.http.HttpMethod.POST; 30 | 31 | /** 32 | * @author William Suane for DevDojo on 11/10/17. 33 | */ 34 | @RunWith(SpringRunner.class) 35 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 36 | @AutoConfigureMockMvc 37 | public class QuestionEndpointTest { 38 | @MockBean 39 | private QuestionRepository questionRepository; 40 | @Autowired 41 | private TestRestTemplate testRestTemplate; 42 | private HttpEntity professorHeader; 43 | private HttpEntity wrongHeader; 44 | private Question question = mockQuestion(); 45 | 46 | public static Question mockQuestion() { 47 | return Question.Builder.newQuestion() 48 | .id(1L) 49 | .title("What is class?") 50 | .course(CourseEndpointTest.mockCourse()) 51 | .professor(ProfessorEndpointTest.mockProfessor()) 52 | .build(); 53 | } 54 | 55 | @Before 56 | public void configProfessorHeader() { 57 | String body = "{\"username\":\"william\",\"password\":\"devdojo\"}"; 58 | HttpHeaders headers = testRestTemplate.postForEntity("/login", body, String.class).getHeaders(); 59 | this.professorHeader = new HttpEntity<>(headers); 60 | } 61 | 62 | @Before 63 | public void configWrongHeader() { 64 | HttpHeaders headers = new HttpHeaders(); 65 | headers.add("Authorization", "1111"); 66 | this.wrongHeader = new HttpEntity<>(headers); 67 | } 68 | 69 | @Before 70 | public void setup() { 71 | BDDMockito.when(questionRepository.findOne(question.getId())).thenReturn(question); 72 | BDDMockito.when(questionRepository.listQuestionsByCourseAndTitle(question.getCourse().getId(), "")).thenReturn(Collections.singletonList(question)); 73 | BDDMockito.when(questionRepository.listQuestionsByCourseAndTitle(question.getCourse().getId(), "What is class")).thenReturn(Collections.singletonList(question)); 74 | } 75 | 76 | @Test 77 | public void getQuestionByIdWhenTokenIsWrongShouldReturn403() throws Exception { 78 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/1", GET, wrongHeader, String.class); 79 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 80 | } 81 | 82 | @Test 83 | public void listQuestionsByCourseAndTitleWhenTokenIsWrongShouldReturn403() throws Exception { 84 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/list/1/?title=", GET, wrongHeader, String.class); 85 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 86 | } 87 | 88 | @Test 89 | public void listAllQuestionsByCourseAndTitleWhenTitleDoesNotExistsShouldReturnEmptyList() throws Exception { 90 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/question/list/1/?title=xaxa", GET, 91 | professorHeader, new ParameterizedTypeReference>() { 92 | }); 93 | assertThat(exchange.getBody()).isEmpty(); 94 | } 95 | 96 | @Test 97 | public void listAllQuestionsByCourseWhenTitleExistsShouldReturn200() throws Exception { 98 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/list/1/?title=what", GET, professorHeader, String.class); 99 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 100 | } 101 | 102 | @Test 103 | public void getQuestionByIdWithoutIdShouldReturn400() throws Exception { 104 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/", GET, professorHeader, String.class); 105 | assertThat(exchange.getStatusCodeValue()).isEqualTo(400); 106 | } 107 | 108 | @Test 109 | public void getQuestionByIdWhenQuestionIdDoesNotExistsShouldReturn404() throws Exception { 110 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/-1", GET, professorHeader, String.class); 111 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 112 | } 113 | 114 | @Test 115 | public void getQuestionByIdWhenQuestionExistsShouldReturn200() throws Exception { 116 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/1", GET, professorHeader, String.class); 117 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 118 | } 119 | 120 | @Test 121 | public void deleteQuestionWhenIdExistsShouldReturn200() throws Exception { 122 | long id = 1L; 123 | BDDMockito.doNothing().when(questionRepository).delete(id); 124 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/{id}", DELETE, professorHeader, String.class, id); 125 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 126 | } 127 | 128 | @Test 129 | public void deleteQuestionWhenIdDoesNotExistsShouldReturn404() throws Exception { 130 | long id = -1L; 131 | BDDMockito.doNothing().when(questionRepository).delete(id); 132 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/{id}", DELETE, professorHeader, String.class, id); 133 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 134 | } 135 | 136 | @Test 137 | public void createQuestionWhenTitleIsNullShouldReturn400() throws Exception { 138 | Question question = questionRepository.findOne(1L); 139 | question.setTitle(null); 140 | assertThat(createQuestion(question).getStatusCodeValue()).isEqualTo(400); 141 | } 142 | 143 | @Test 144 | public void createQuestionWhenCourseDoesNotExistsShouldReturn404() throws Exception { 145 | Question question = questionRepository.findOne(1L); 146 | question.setCourse(new Course()); 147 | assertThat(createQuestion(question).getStatusCodeValue()).isEqualTo(404); 148 | } 149 | 150 | @Test 151 | public void createQuestionWhenEverythingIsRightShouldReturn200() throws Exception { 152 | Question question = questionRepository.findOne(1L); 153 | question.setId(null); 154 | assertThat(createQuestion(question).getStatusCodeValue()).isEqualTo(200); 155 | } 156 | 157 | private ResponseEntity createQuestion(Question question) { 158 | BDDMockito.when(questionRepository.save(question)).thenReturn(question); 159 | return testRestTemplate.exchange("/v1/professor/course/question/", POST, 160 | new HttpEntity<>(question, professorHeader.getHeaders()), String.class); 161 | } 162 | } -------------------------------------------------------------------------------- /src/main/java/br/com/devdojo/examgenerator/persistence/model/ExamAnswer.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.persistence.model; 2 | 3 | import javax.persistence.Entity; 4 | 5 | /** 6 | * @author William Suane on 29/08/2018 7 | */ 8 | @Entity 9 | public class ExamAnswer extends AbstractEntity { 10 | private Long questionId; 11 | private Long assignmentId; 12 | private Long questionAssignmentId; 13 | private Long professorId; 14 | private Long studentId; 15 | private String assignmentTitle; 16 | private String questionTitle; 17 | private Long selectedChoiceId; 18 | private Long correctChoiceId; 19 | private String selectedChoiceTitle; 20 | private String correctChoiceTitle; 21 | private double choiceGrade; 22 | private double answerGrade; 23 | 24 | public Long getQuestionId() { 25 | return questionId; 26 | } 27 | 28 | public void setQuestionId(Long questionId) { 29 | this.questionId = questionId; 30 | } 31 | 32 | public Long getAssignmentId() { 33 | return assignmentId; 34 | } 35 | 36 | public void setAssignmentId(Long assignmentId) { 37 | this.assignmentId = assignmentId; 38 | } 39 | 40 | public Long getQuestionAssignmentId() { 41 | return questionAssignmentId; 42 | } 43 | 44 | public void setQuestionAssignmentId(Long questionAssignmentId) { 45 | this.questionAssignmentId = questionAssignmentId; 46 | } 47 | 48 | public Long getProfessorId() { 49 | return professorId; 50 | } 51 | 52 | public void setProfessorId(Long professorId) { 53 | this.professorId = professorId; 54 | } 55 | 56 | public Long getStudentId() { 57 | return studentId; 58 | } 59 | 60 | public void setStudentId(Long studentId) { 61 | this.studentId = studentId; 62 | } 63 | 64 | public String getAssignmentTitle() { 65 | return assignmentTitle; 66 | } 67 | 68 | public void setAssignmentTitle(String assignmentTitle) { 69 | this.assignmentTitle = assignmentTitle; 70 | } 71 | 72 | public String getQuestionTitle() { 73 | return questionTitle; 74 | } 75 | 76 | public void setQuestionTitle(String questionTitle) { 77 | this.questionTitle = questionTitle; 78 | } 79 | 80 | public Long getSelectedChoiceId() { 81 | return selectedChoiceId; 82 | } 83 | 84 | public void setSelectedChoiceId(Long selectedChoiceId) { 85 | this.selectedChoiceId = selectedChoiceId; 86 | } 87 | 88 | public Long getCorrectChoiceId() { 89 | return correctChoiceId; 90 | } 91 | 92 | public void setCorrectChoiceId(Long correctChoiceId) { 93 | this.correctChoiceId = correctChoiceId; 94 | } 95 | 96 | public String getSelectedChoiceTitle() { 97 | return selectedChoiceTitle; 98 | } 99 | 100 | public void setSelectedChoiceTitle(String selectedChoiceTitle) { 101 | this.selectedChoiceTitle = selectedChoiceTitle; 102 | } 103 | 104 | public String getCorrectChoiceTitle() { 105 | return correctChoiceTitle; 106 | } 107 | 108 | public void setCorrectChoiceTitle(String correctChoiceTitle) { 109 | this.correctChoiceTitle = correctChoiceTitle; 110 | } 111 | 112 | public double getChoiceGrade() { 113 | return choiceGrade; 114 | } 115 | 116 | public void setChoiceGrade(double choiceGrade) { 117 | this.choiceGrade = choiceGrade; 118 | } 119 | 120 | public double getAnswerGrade() { 121 | return answerGrade; 122 | } 123 | 124 | public void setAnswerGrade(double answerGrade) { 125 | this.answerGrade = answerGrade; 126 | } 127 | 128 | public static final class ExamAnswerBuilder { 129 | protected Long id; 130 | private Long questionId; 131 | private Long assignmentId; 132 | private Long questionAssignmentId; 133 | private Long professorId; 134 | private Long studentId; 135 | private String assignmentTitle; 136 | private String questionTitle; 137 | private boolean enabled = true; 138 | private Long selectedChoiceId; 139 | private Long correctChoiceId; 140 | private String selectedChoiceTitle; 141 | private String correctChoiceTitle; 142 | private double choiceGrade; 143 | private double answerGrade; 144 | 145 | private ExamAnswerBuilder() { 146 | } 147 | 148 | public static ExamAnswerBuilder newExamAnswer() { 149 | return new ExamAnswerBuilder(); 150 | } 151 | 152 | public ExamAnswerBuilder questionId(Long questionId) { 153 | this.questionId = questionId; 154 | return this; 155 | } 156 | 157 | public ExamAnswerBuilder assignmentId(Long assignmentId) { 158 | this.assignmentId = assignmentId; 159 | return this; 160 | } 161 | 162 | public ExamAnswerBuilder questionAssignmentId(Long questionAssignmentId) { 163 | this.questionAssignmentId = questionAssignmentId; 164 | return this; 165 | } 166 | 167 | public ExamAnswerBuilder professorId(Long professorId) { 168 | this.professorId = professorId; 169 | return this; 170 | } 171 | 172 | public ExamAnswerBuilder id(Long id) { 173 | this.id = id; 174 | return this; 175 | } 176 | 177 | public ExamAnswerBuilder studentId(Long studentId) { 178 | this.studentId = studentId; 179 | return this; 180 | } 181 | 182 | public ExamAnswerBuilder assignmentTitle(String assignmentTitle) { 183 | this.assignmentTitle = assignmentTitle; 184 | return this; 185 | } 186 | 187 | public ExamAnswerBuilder questionTitle(String questionTitle) { 188 | this.questionTitle = questionTitle; 189 | return this; 190 | } 191 | 192 | public ExamAnswerBuilder enabled(boolean enabled) { 193 | this.enabled = enabled; 194 | return this; 195 | } 196 | 197 | public ExamAnswerBuilder selectedChoiceId(Long selectedChoiceId) { 198 | this.selectedChoiceId = selectedChoiceId; 199 | return this; 200 | } 201 | 202 | public ExamAnswerBuilder correctChoiceId(Long correctChoiceId) { 203 | this.correctChoiceId = correctChoiceId; 204 | return this; 205 | } 206 | 207 | public ExamAnswerBuilder selectedChoiceTitle(String selectedChoiceTitle) { 208 | this.selectedChoiceTitle = selectedChoiceTitle; 209 | return this; 210 | } 211 | 212 | public ExamAnswerBuilder correctChoiceTitle(String correctChoiceTitle) { 213 | this.correctChoiceTitle = correctChoiceTitle; 214 | return this; 215 | } 216 | 217 | public ExamAnswerBuilder choiceGrade(double choiceGrade) { 218 | this.choiceGrade = choiceGrade; 219 | return this; 220 | } 221 | 222 | public ExamAnswerBuilder answerGrade(double answerGrade) { 223 | this.answerGrade = answerGrade; 224 | return this; 225 | } 226 | 227 | public ExamAnswer build() { 228 | ExamAnswer examAnswer = new ExamAnswer(); 229 | examAnswer.setId(id); 230 | examAnswer.setEnabled(enabled); 231 | examAnswer.studentId = this.studentId; 232 | examAnswer.questionId = this.questionId; 233 | examAnswer.choiceGrade = this.choiceGrade; 234 | examAnswer.selectedChoiceId = this.selectedChoiceId; 235 | examAnswer.answerGrade = this.answerGrade; 236 | examAnswer.correctChoiceTitle = this.correctChoiceTitle; 237 | examAnswer.assignmentTitle = this.assignmentTitle; 238 | examAnswer.selectedChoiceTitle = this.selectedChoiceTitle; 239 | examAnswer.professorId = this.professorId; 240 | examAnswer.questionTitle = this.questionTitle; 241 | examAnswer.correctChoiceId = this.correctChoiceId; 242 | examAnswer.assignmentId = this.assignmentId; 243 | examAnswer.questionAssignmentId = this.questionAssignmentId; 244 | return examAnswer; 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/test/java/br/com/devdojo/examgenerator/endpoint/v1/choice/ChoiceEndpointTest.java: -------------------------------------------------------------------------------- 1 | package br.com.devdojo.examgenerator.endpoint.v1.choice; 2 | 3 | import br.com.devdojo.examgenerator.endpoint.v1.ProfessorEndpointTest; 4 | import br.com.devdojo.examgenerator.endpoint.v1.question.QuestionEndpointTest; 5 | import br.com.devdojo.examgenerator.endpoint.v1.questionassignment.QuestionAssignmentEndpointTest; 6 | import br.com.devdojo.examgenerator.persistence.model.Choice; 7 | import br.com.devdojo.examgenerator.persistence.model.Question; 8 | import br.com.devdojo.examgenerator.persistence.respository.ChoiceRepository; 9 | import br.com.devdojo.examgenerator.persistence.respository.QuestionAssignmentRepository; 10 | import br.com.devdojo.examgenerator.persistence.respository.QuestionRepository; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.mockito.BDDMockito; 15 | import org.mockito.Mockito; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.boot.test.mock.mockito.MockBean; 20 | import org.springframework.boot.test.web.client.TestRestTemplate; 21 | import org.springframework.core.ParameterizedTypeReference; 22 | import org.springframework.http.HttpEntity; 23 | import org.springframework.http.HttpHeaders; 24 | import org.springframework.http.ResponseEntity; 25 | import org.springframework.test.context.junit4.SpringRunner; 26 | 27 | import java.util.Collections; 28 | import java.util.List; 29 | 30 | import static java.util.Arrays.asList; 31 | import static org.assertj.core.api.Assertions.assertThat; 32 | import static org.springframework.http.HttpMethod.*; 33 | 34 | /** 35 | * @author William Suane for DevDojo on 11/20/17. 36 | */ 37 | @RunWith(SpringRunner.class) 38 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 39 | @AutoConfigureMockMvc 40 | public class ChoiceEndpointTest { 41 | @MockBean 42 | private QuestionRepository questionRepository; 43 | @MockBean 44 | private ChoiceRepository choiceRepository; 45 | @MockBean 46 | private QuestionAssignmentRepository questionAssignmentRepository; 47 | @Autowired 48 | private TestRestTemplate testRestTemplate; 49 | private HttpEntity professorHeader; 50 | private HttpEntity wrongHeader; 51 | private Choice choiceCorrectAnswerFalse = mockChoiceCorrectAnswerFalse(); 52 | private Choice choiceCorrectAnswerTrue = mockChoiceCorrectAnswerTrue(); 53 | private Choice choiceWithQuestionNotAssociatedWithAssignment = mockChoiceQuestionNotAssociatedWithAssignment(); 54 | 55 | public static Choice mockChoiceCorrectAnswerFalse() { 56 | return Choice.Builder.newChoice() 57 | .id(1L) 58 | .title("Is a room") 59 | .question(QuestionEndpointTest.mockQuestion()) 60 | .correctAnswer(false) 61 | .professor(ProfessorEndpointTest.mockProfessor()) 62 | .build(); 63 | } 64 | 65 | public static Choice mockChoiceCorrectAnswerTrue() { 66 | return Choice.Builder.newChoice() 67 | .id(2L) 68 | .title("is a template") 69 | .question(QuestionEndpointTest.mockQuestion()) 70 | .correctAnswer(true) 71 | .professor(ProfessorEndpointTest.mockProfessor()) 72 | .build(); 73 | } 74 | 75 | public static Choice mockChoiceQuestionNotAssociatedWithAssignment() { 76 | Question question = QuestionEndpointTest.mockQuestion(); 77 | question.setId(2L); 78 | return Choice.Builder.newChoice() 79 | .id(3L) 80 | .title("is a template") 81 | .question(question) 82 | .correctAnswer(true) 83 | .professor(ProfessorEndpointTest.mockProfessor()) 84 | .build(); 85 | } 86 | 87 | @Before 88 | public void configProfessorHeader() { 89 | String body = "{\"username\":\"william\",\"password\":\"devdojo\"}"; 90 | HttpHeaders headers = testRestTemplate.postForEntity("/login", body, String.class).getHeaders(); 91 | this.professorHeader = new HttpEntity<>(headers); 92 | } 93 | 94 | @Before 95 | public void configWrongHeader() { 96 | HttpHeaders headers = new HttpHeaders(); 97 | headers.add("Authorization", "1111"); 98 | this.wrongHeader = new HttpEntity<>(headers); 99 | } 100 | 101 | @Before 102 | public void setup() { 103 | BDDMockito.when(questionAssignmentRepository.listQuestionAssignmentByQuestionId(choiceCorrectAnswerTrue.getQuestion().getId())) 104 | .thenReturn(Collections.singletonList(QuestionAssignmentEndpointTest.mockQuestionAssignment())); 105 | 106 | BDDMockito.when(choiceRepository.findOne(choiceCorrectAnswerFalse.getId())).thenReturn(choiceCorrectAnswerFalse); 107 | BDDMockito.when(choiceRepository.findOne(choiceCorrectAnswerTrue.getId())).thenReturn(choiceCorrectAnswerTrue); 108 | BDDMockito.when(choiceRepository.findOne(choiceWithQuestionNotAssociatedWithAssignment.getId())).thenReturn(choiceWithQuestionNotAssociatedWithAssignment); 109 | BDDMockito.when(choiceRepository.listChoicesByQuestionId(choiceCorrectAnswerTrue.getQuestion().getId())) 110 | .thenReturn(asList(choiceCorrectAnswerFalse, choiceCorrectAnswerTrue)); 111 | BDDMockito.when(questionRepository.findOne(choiceCorrectAnswerTrue.getQuestion().getId())) 112 | .thenReturn(choiceCorrectAnswerTrue.getQuestion()); 113 | } 114 | 115 | @Test 116 | public void getChoiceByIdWhenTokenIsWrongShouldReturn403() throws Exception { 117 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/1", GET, wrongHeader, String.class); 118 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 119 | } 120 | 121 | @Test 122 | public void listChoicesByQuestionIdWhenTokenIsWrongShouldReturn403() throws Exception { 123 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/list/1/", GET, wrongHeader, String.class); 124 | assertThat(exchange.getStatusCodeValue()).isEqualTo(403); 125 | } 126 | 127 | @Test 128 | public void listChoicesByQuestionIdWhenQuestionIdDoesNotExistsShouldReturnEmptyList() throws Exception { 129 | ResponseEntity> exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/list/99/", GET, 130 | professorHeader, new ParameterizedTypeReference>() { 131 | }); 132 | assertThat(exchange.getBody()).isEmpty(); 133 | } 134 | 135 | @Test 136 | public void listChoicesByQuestionIdWhenQuestionIdExistsShouldReturn200() throws Exception { 137 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/list/1/", GET, professorHeader, String.class); 138 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 139 | } 140 | 141 | @Test 142 | public void getChoiceByIdWithoutIdShouldReturn400() throws Exception { 143 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/", GET, professorHeader, String.class); 144 | assertThat(exchange.getStatusCodeValue()).isEqualTo(400); 145 | } 146 | 147 | @Test 148 | public void getChoiceByIdWhenChoiceIdDoesNotExistsShouldReturn404() throws Exception { 149 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/-1", GET, professorHeader, String.class); 150 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 151 | } 152 | 153 | @Test 154 | public void getChoiceByIdWhenChoiceIdExistsShouldReturn200() throws Exception { 155 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/1", GET, professorHeader, String.class); 156 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 157 | } 158 | 159 | @Test 160 | public void deleteChoiceWhenIdExistsAndItsQuestionIsNotInAnyAssignmentShouldReturn200() throws Exception { 161 | long id = 3L; 162 | BDDMockito.doNothing().when(choiceRepository).delete(id); 163 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/{id}", DELETE, professorHeader, String.class, id); 164 | assertThat(exchange.getStatusCodeValue()).isEqualTo(200); 165 | } 166 | 167 | @Test 168 | public void deleteChoiceWhenIdExistsAndItsQuestionIsInAnyAssignmentShouldReturn409() throws Exception { 169 | long id = 1L; 170 | BDDMockito.doNothing().when(choiceRepository).delete(id); 171 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/{id}", DELETE, professorHeader, String.class, id); 172 | assertThat(exchange.getStatusCodeValue()).isEqualTo(409); 173 | } 174 | 175 | @Test 176 | public void deleteChoiceWhenIdDoesNotExistsShouldReturn404() throws Exception { 177 | long id = -1L; 178 | BDDMockito.doNothing().when(choiceRepository).delete(id); 179 | ResponseEntity exchange = testRestTemplate.exchange("/v1/professor/course/question/choice/{id}", DELETE, professorHeader, String.class, id); 180 | assertThat(exchange.getStatusCodeValue()).isEqualTo(404); 181 | } 182 | 183 | @Test 184 | public void createChoiceWhenTitleIsNullShouldReturn400() throws Exception { 185 | Choice choice = choiceRepository.findOne(1L); 186 | choice.setTitle(null); 187 | assertThat(createChoice(choice).getStatusCodeValue()).isEqualTo(400); 188 | } 189 | 190 | @Test 191 | public void createChoiceWhenQuestionDoesNotExistsShouldReturn404() throws Exception { 192 | Choice choice = choiceRepository.findOne(1L); 193 | choice.setQuestion(new Question()); 194 | assertThat(createChoice(choice).getStatusCodeValue()).isEqualTo(404); 195 | } 196 | 197 | @Test 198 | public void createChoiceWhenEverythingIsRightShouldReturn200() throws Exception { 199 | Choice choice = choiceRepository.findOne(1L); 200 | choice.setId(null); 201 | assertThat(createChoice(choice).getStatusCodeValue()).isEqualTo(200); 202 | } 203 | @Test 204 | public void createChoiceWithCorrectAnswerTrueShouldTriggerUpdateChoicesAndCreate() throws Exception { 205 | Choice choice = choiceRepository.findOne(2L); 206 | choice.setId(null); 207 | createChoice(choice); 208 | BDDMockito.verify(choiceRepository, Mockito.times(1)).save(choice); 209 | BDDMockito.verify(choiceRepository, 210 | Mockito.times(1)) 211 | .updateAllOtherChoicesCorrectAnswerToFalse(choice, choice.getQuestion()); 212 | } 213 | 214 | private ResponseEntity createChoice(Choice choice) { 215 | BDDMockito.when(choiceRepository.save(choice)).thenReturn(choice); 216 | return testRestTemplate.exchange("/v1/professor/course/question/choice/", POST, 217 | new HttpEntity<>(choice, professorHeader.getHeaders()), String.class); 218 | } 219 | } --------------------------------------------------------------------------------