├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── src ├── main │ ├── java │ │ └── br │ │ │ └── com │ │ │ └── caelum │ │ │ └── cursos │ │ │ ├── adapters │ │ │ ├── web │ │ │ │ ├── dto │ │ │ │ │ ├── DadosTelefoneDto.java │ │ │ │ │ ├── DadosSalaDto.java │ │ │ │ │ ├── DadosMatriculaDto.java │ │ │ │ │ ├── DadosCursoDto.java │ │ │ │ │ ├── DadosEstudanteDto.java │ │ │ │ │ ├── DadosParaRealizarMatriculaDto.java │ │ │ │ │ ├── DadosParaCadastrarSalaDto.java │ │ │ │ │ ├── DadosTurmaDto.java │ │ │ │ │ ├── DadosParaRealizarMatriculaPort.java │ │ │ │ │ ├── DadosParaCadastrarCursoDto.java │ │ │ │ │ ├── DadosParaAbrirTurmaPort.java │ │ │ │ │ ├── DadosParaAbrirTurmaDto.java │ │ │ │ │ └── DadosParaCadastrarEstudanteDto.java │ │ │ │ └── controller │ │ │ │ │ ├── SalaController.java │ │ │ │ │ ├── CursoController.java │ │ │ │ │ ├── EstudanteController.java │ │ │ │ │ ├── TurmaController.java │ │ │ │ │ └── MatriculaController.java │ │ │ └── database │ │ │ │ └── jpa │ │ │ │ ├── repository │ │ │ │ ├── MatriculaJpaRepository.java │ │ │ │ ├── SalaJpaRepository.java │ │ │ │ ├── CursoJpaRepository.java │ │ │ │ ├── EstudanteJpaRepository.java │ │ │ │ └── TurmaJpaRepository.java │ │ │ │ └── entity │ │ │ │ ├── TelefoneVo.java │ │ │ │ ├── SalaJpaEntity.java │ │ │ │ ├── MatriculaJpaEntity.java │ │ │ │ ├── CursoJpaEntity.java │ │ │ │ ├── EstudanteJpaEntity.java │ │ │ │ └── TurmaJpaEntity.java │ │ │ ├── domain │ │ │ ├── ports │ │ │ │ ├── sala │ │ │ │ │ ├── DadosParaCadastrarSala.java │ │ │ │ │ ├── CadastrarSalaUseCase.java │ │ │ │ │ └── SalaRepository.java │ │ │ │ ├── turma │ │ │ │ │ ├── AbrirTurmaUseCase.java │ │ │ │ │ ├── DadosParaAbrirTurma.java │ │ │ │ │ └── TurmaRepository.java │ │ │ │ ├── curso │ │ │ │ │ ├── CadastrarCursoUseCase.java │ │ │ │ │ ├── CursoRepository.java │ │ │ │ │ └── DadosParaCadastrarCurso.java │ │ │ │ ├── matricula │ │ │ │ │ ├── MatriculaRepository.java │ │ │ │ │ ├── DadosParaRealizarMatricula.java │ │ │ │ │ └── MatricularEstudanteEmTurmaUseCase.java │ │ │ │ └── estudante │ │ │ │ │ ├── CadastrarEstudanteUseCase.java │ │ │ │ │ ├── EstudanteRepository.java │ │ │ │ │ └── DadosParaCadastrarEstudante.java │ │ │ └── core │ │ │ │ ├── RegraDeNegocioException.java │ │ │ │ ├── estudante │ │ │ │ ├── Cpf.java │ │ │ │ ├── Email.java │ │ │ │ ├── Telefone.java │ │ │ │ ├── CadastrarEstudanteService.java │ │ │ │ └── Estudante.java │ │ │ │ ├── curso │ │ │ │ ├── Nivel.java │ │ │ │ ├── Curso.java │ │ │ │ └── CadastrarCursoService.java │ │ │ │ ├── turma │ │ │ │ ├── Turno.java │ │ │ │ ├── Turma.java │ │ │ │ └── AbrirTurmaService.java │ │ │ │ ├── matricula │ │ │ │ ├── Matricula.java │ │ │ │ └── MatricularEstudanteEmTurmaService.java │ │ │ │ └── sala │ │ │ │ ├── Sala.java │ │ │ │ └── CadastrarSalaService.java │ │ │ ├── CursosApplication.java │ │ │ └── infra │ │ │ └── spring │ │ │ ├── ExceptionHandlerAdvice.java │ │ │ └── UseCaseBeansConfiguration.java │ └── resources │ │ ├── db │ │ └── migration │ │ │ ├── V001__create-table-salas.sql │ │ │ ├── V002__create-table-cursos.sql │ │ │ ├── V003__create-table-estudantes.sql │ │ │ ├── V005__create-table-matriculas.sql │ │ │ └── V004__create-table-turmas.sql │ │ └── application.properties └── test │ └── java │ └── br │ └── com │ └── caelum │ └── cursos │ ├── builders │ ├── SalaBuilder.java │ ├── SalaJpaentityBuilder.java │ ├── CursoJpaEntityBuilder.java │ ├── CursoBuilder.java │ ├── EstudanteJpaEntityBuilder.java │ ├── EstudanteBuilder.java │ ├── TurmaJpaEntityBuilder.java │ └── TurmaBuilder.java │ ├── domain │ └── core │ │ ├── sala │ │ └── CadastrarSalaServiceTest.java │ │ ├── curso │ │ └── CadastrarCursoServiceTest.java │ │ ├── estudante │ │ └── CadastrarEstudanteServiceTest.java │ │ ├── turma │ │ └── AbrirTurmaServiceTest.java │ │ └── matricula │ │ └── MatricularEstudanteEmTurmaServiceTest.java │ └── adapters │ └── web │ └── controller │ ├── SalaControllerTest.java │ ├── CursoControllerTest.java │ ├── EstudanteControllerTest.java │ ├── TurmaControllerTest.java │ └── MatriculaControllerTest.java ├── .gitignore ├── README.md ├── pom.xml ├── mvnw.cmd └── mvnw /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcaneppele/hexagonal-architecture-java/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosTelefoneDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | public record DadosTelefoneDto(String ddd, String numero, Boolean whatsapp){} 4 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V001__create-table-salas.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE salas( 2 | id BIGINT NOT NULL AUTO_INCREMENT, 3 | nome VARCHAR(50) NOT NULL UNIQUE, 4 | capacidade INT NOT NULL, 5 | 6 | PRIMARY KEY(id) 7 | ); 8 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/sala/DadosParaCadastrarSala.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.sala; 2 | 3 | public interface DadosParaCadastrarSala { 4 | 5 | String nome(); 6 | Integer capacidade(); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/RegraDeNegocioException.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core; 2 | 3 | public class RegraDeNegocioException extends RuntimeException { 4 | 5 | public RegraDeNegocioException(String msg) { 6 | super(msg); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/turma/AbrirTurmaUseCase.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.turma; 2 | 3 | import br.com.caelum.cursos.domain.core.turma.Turma; 4 | 5 | public interface AbrirTurmaUseCase { 6 | 7 | Turma execute(DadosParaAbrirTurma dados); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/sala/CadastrarSalaUseCase.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.sala; 2 | 3 | import br.com.caelum.cursos.domain.core.sala.Sala; 4 | 5 | public interface CadastrarSalaUseCase { 6 | 7 | Sala execute(DadosParaCadastrarSala dados); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/curso/CadastrarCursoUseCase.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.curso; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | 5 | public interface CadastrarCursoUseCase { 6 | 7 | Curso execute(DadosParaCadastrarCurso dados); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/matricula/MatriculaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.matricula; 2 | 3 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 4 | 5 | public interface MatriculaRepository { 6 | 7 | void registrar(Matricula matricula); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V002__create-table-cursos.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE cursos( 2 | id BIGINT NOT NULL AUTO_INCREMENT, 3 | codigo VARCHAR(50) NOT NULL UNIQUE, 4 | nome VARCHAR(50) NOT NULL, 5 | nivel VARCHAR(50) NOT NULL, 6 | duracao_em_horas BIGINT NOT NULL, 7 | 8 | PRIMARY KEY(id) 9 | ); 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/sala/SalaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.sala; 2 | 3 | import br.com.caelum.cursos.domain.core.sala.Sala; 4 | 5 | public interface SalaRepository { 6 | 7 | Boolean nomeJaCadastrado(String nome); 8 | 9 | void cadastrar(Sala sala); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/matricula/DadosParaRealizarMatricula.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.matricula; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 4 | 5 | public interface DadosParaRealizarMatricula { 6 | 7 | Cpf cpfEstudante(); 8 | String codigoTurma(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/curso/CursoRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.curso; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | 5 | public interface CursoRepository { 6 | 7 | Boolean codigoJaCadastrado(String codigo); 8 | 9 | void cadastrar(Curso curso); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/estudante/CadastrarEstudanteUseCase.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 4 | 5 | public interface CadastrarEstudanteUseCase { 6 | 7 | Estudante execute(DadosParaCadastrarEstudante dados); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #DATASOURCE 2 | spring.datasource.url=${DATASOURCE_URL:jdbc:mysql://localhost:3306/caelum_cursos?createDatabaseIfNotExist=true} 3 | spring.datasource.username=${DATASOURCE_USERNAME:root} 4 | spring.datasource.password=${DATASOURCE_PASSWORD:root} 5 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 6 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/matricula/MatricularEstudanteEmTurmaUseCase.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.matricula; 2 | 3 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 4 | 5 | public interface MatricularEstudanteEmTurmaUseCase { 6 | 7 | Matricula execute(DadosParaRealizarMatricula dados); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/estudante/Cpf.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import java.util.Objects; 4 | 5 | public record Cpf(String numero) { 6 | 7 | public Cpf { 8 | validar(numero); 9 | } 10 | 11 | private void validar(String numero) { 12 | Objects.requireNonNull(numero); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/estudante/Email.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import java.util.Objects; 4 | 5 | public record Email(String email) { 6 | 7 | public Email { 8 | validar(email); 9 | } 10 | 11 | private void validar(String email) { 12 | Objects.requireNonNull(email); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/curso/DadosParaCadastrarCurso.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.curso; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Nivel; 4 | 5 | import java.time.Duration; 6 | 7 | public interface DadosParaCadastrarCurso { 8 | 9 | String codigo(); 10 | String nome(); 11 | Nivel nivel(); 12 | Duration duracaoEmHoras(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/CursosApplication.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CursosApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CursosApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/curso/Nivel.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.curso; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public 7 | enum Nivel { 8 | 9 | BASICO("Básico"), 10 | INTERMEDIARIO("Intermediário"), 11 | AVANCADO("Avançado"); 12 | 13 | private final String descricao; 14 | 15 | Nivel(String descricao) { 16 | this.descricao = descricao; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosSalaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 4 | 5 | public record DadosSalaDto(Long id, String nome, Integer capacidade) { 6 | 7 | public DadosSalaDto(SalaJpaEntity salaJpaEntity) { 8 | this(salaJpaEntity.getId(), salaJpaEntity.getNome(), salaJpaEntity.getCapacidade()); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V003__create-table-estudantes.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE estudantes( 2 | id BIGINT NOT NULL AUTO_INCREMENT, 3 | nome VARCHAR(100) NOT NULL, 4 | cpf VARCHAR(14) NOT NULL UNIQUE, 5 | data_de_nascimento DATE NOT NULL, 6 | email VARCHAR(100) NOT NULL UNIQUE, 7 | telefone_ddd CHAR(2) NOT NULL, 8 | telefone_numero VARCHAR(20) NOT NULL, 9 | telefone_whatsapp BOOLEAN NOT NULL, 10 | 11 | PRIMARY KEY(id) 12 | ); 13 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V005__create-table-matriculas.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE matriculas( 2 | id BIGINT NOT NULL AUTO_INCREMENT, 3 | codigo VARCHAR(50) NOT NULL UNIQUE, 4 | estudante_id BIGINT NOT NULL, 5 | turma_id BIGINT NOT NULL, 6 | 7 | PRIMARY KEY(id), 8 | CONSTRAINT MATRICULA_FK_ESTUDANTE FOREIGN KEY(estudante_id) REFERENCES estudantes(id), 9 | CONSTRAINT MATRICULA_FK_TURMA FOREIGN KEY(turma_id) REFERENCES turmas(id) 10 | ); 11 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/turma/Turno.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.turma; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum Turno { 7 | 8 | MATUTINO("Matutino"), 9 | VESPERTINO("Vespertino"), 10 | INTEGRAL("Integral"), 11 | NOTURNO("Noturno"); 12 | 13 | private final String descricao; 14 | 15 | Turno(String descricao) { 16 | this.descricao = descricao; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/estudante/Telefone.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import java.util.Objects; 4 | 5 | public record Telefone(String ddd, String numero, boolean whatsapp) { 6 | 7 | public Telefone { 8 | validar(ddd, numero); 9 | } 10 | 11 | private void validar(String ddd, String numero) { 12 | Objects.requireNonNull(ddd); 13 | Objects.requireNonNull(numero); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V004__create-table-turmas.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE turmas( 2 | id BIGINT NOT NULL AUTO_INCREMENT, 3 | codigo VARCHAR(50) NOT NULL UNIQUE, 4 | curso_id BIGINT NOT NULL, 5 | sala_id BIGINT NOT NULL, 6 | turno VARCHAR(50) NOT NULL, 7 | data_inicio DATE NOT NULL, 8 | data_fim DATE NOT NULL, 9 | 10 | PRIMARY KEY(id), 11 | CONSTRAINT TURMA_FK_CURSO FOREIGN KEY(curso_id) REFERENCES cursos(id), 12 | CONSTRAINT TURMA_FK_SALA FOREIGN KEY(sala_id) REFERENCES salas(id) 13 | ); 14 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/estudante/EstudanteRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 4 | import br.com.caelum.cursos.domain.core.estudante.Email; 5 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 6 | 7 | public interface EstudanteRepository { 8 | 9 | Estudante buscarPorCpf(Cpf cpf); 10 | 11 | void cadastrar(Estudante estudante); 12 | 13 | Boolean CpfOuEmailJaCadastrado(Cpf cpf, Email email); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/turma/DadosParaAbrirTurma.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.turma; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.sala.Sala; 5 | import br.com.caelum.cursos.domain.core.turma.Turno; 6 | 7 | import java.time.LocalDate; 8 | 9 | public interface DadosParaAbrirTurma { 10 | 11 | String codigo(); 12 | Curso curso(); 13 | Sala sala(); 14 | Turno turno(); 15 | LocalDate dataInicio(); 16 | LocalDate dataFim(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/estudante/DadosParaCadastrarEstudante.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 4 | import br.com.caelum.cursos.domain.core.estudante.Email; 5 | import br.com.caelum.cursos.domain.core.estudante.Telefone; 6 | 7 | import java.time.LocalDate; 8 | 9 | public interface DadosParaCadastrarEstudante { 10 | 11 | String nome(); 12 | Cpf cpf(); 13 | LocalDate dataDeNascimento(); 14 | Email email(); 15 | Telefone celular(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosMatriculaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.MatriculaJpaEntity; 4 | 5 | public record DadosMatriculaDto(Long id, String codigo, Long idEstudante, Long idTurma) { 6 | 7 | public DadosMatriculaDto(MatriculaJpaEntity entity) { 8 | this( 9 | entity.getId(), 10 | entity.getCodigo(), 11 | entity.getEstudante().getId(), 12 | entity.getTurma().getId() 13 | ); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosCursoDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.domain.core.curso.Nivel; 5 | 6 | public record DadosCursoDto(Long id, String codigo, String nome, Nivel nivel, Long duracaoEmHoras) { 7 | 8 | public DadosCursoDto(CursoJpaEntity cursoJpaEntity) { 9 | this(cursoJpaEntity.getId(), cursoJpaEntity.getNome(), cursoJpaEntity.getNome(), cursoJpaEntity.getNivel(), cursoJpaEntity.getDuracaoEmHoras().toHours()); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosEstudanteDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.TelefoneVo; 5 | 6 | import java.time.LocalDate; 7 | 8 | public record DadosEstudanteDto(Long id, String nome, String cpf, LocalDate dataDeNascimento, String email, TelefoneVo celular) { 9 | 10 | public DadosEstudanteDto(EstudanteJpaEntity entity) { 11 | this(entity.getId(), entity.getNome(), entity.getCpf(), entity.getDataDeNascimento(), entity.getEmail(), entity.getCelular()); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/SalaBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.domain.core.sala.Sala; 4 | import br.com.caelum.cursos.domain.ports.sala.DadosParaCadastrarSala; 5 | 6 | public class SalaBuilder { 7 | 8 | public static Sala build(String nome, int capacidade) { 9 | return new Sala(new DadosParaCadastrarSala() { 10 | @Override 11 | public String nome() { 12 | return nome; 13 | } 14 | 15 | @Override 16 | public Integer capacidade() { 17 | return capacidade; 18 | } 19 | }); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/ports/turma/TurmaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.ports.turma; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.sala.Sala; 5 | import br.com.caelum.cursos.domain.core.turma.Turma; 6 | 7 | import java.time.LocalDate; 8 | 9 | public interface TurmaRepository { 10 | 11 | Turma buscarPorCodigo(String codigo); 12 | 13 | void abrir(Turma turma); 14 | 15 | Boolean codigoJaCadastrado(String codigo); 16 | 17 | Boolean salaJaOcupadaNoPeriodo(Sala sala, LocalDate dataInicio, LocalDate dataFim); 18 | 19 | Integer quantidadeDeTurmasEmAbertoDoCurso(Curso curso); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaRealizarMatriculaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.MatriculaJpaEntity; 4 | import jakarta.validation.constraints.NotNull; 5 | 6 | public record DadosParaRealizarMatriculaDto( 7 | @NotNull(message = "Id do estudante é obrigatório!") 8 | Long idEstudante, 9 | @NotNull(message = "Id da turma é obrigatório!") 10 | Long idTurma 11 | ) { 12 | 13 | public DadosParaRealizarMatriculaDto(MatriculaJpaEntity entity) { 14 | this(entity.getEstudante().getId(),entity.getTurma().getId()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaCadastrarSalaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.domain.ports.sala.DadosParaCadastrarSala; 4 | import jakarta.validation.constraints.DecimalMin; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | public record DadosParaCadastrarSalaDto( 9 | @NotBlank(message = "Nome da sala é obrigatório!") 10 | String nome, 11 | @NotNull(message = "Capacidade da sala é obrigatória!") 12 | @DecimalMin(value = "8", message = "Capacidade mínima da sala deve ser 8!") 13 | Integer capacidade) implements DadosParaCadastrarSala { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/SalaJpaentityBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarSalaDto; 5 | 6 | public class SalaJpaentityBuilder { 7 | 8 | public static SalaJpaEntity build(Long id, String nome, Integer capacidade) { 9 | return new SalaJpaEntity(id, nome, capacidade); 10 | } 11 | 12 | public static SalaJpaEntity build() { 13 | return build(50l, "Sala 01", 20); 14 | } 15 | 16 | public static SalaJpaEntity build(DadosParaCadastrarSalaDto dadosCadastro) { 17 | return build(50l, dadosCadastro.nome(), dadosCadastro.capacidade()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/repository/MatriculaJpaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.repository; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.MatriculaJpaEntity; 4 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 5 | import br.com.caelum.cursos.domain.ports.matricula.MatriculaRepository; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | public interface MatriculaJpaRepository extends MatriculaRepository, JpaRepository { 9 | 10 | MatriculaJpaEntity findByCodigo(String codigo); 11 | 12 | @Override 13 | default void registrar(Matricula matricula) { 14 | //nao da para salvar a matricula daqui, pois precisa carregar os relacionamentos =/ 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosTurmaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 4 | import br.com.caelum.cursos.domain.core.turma.Turno; 5 | 6 | import java.time.LocalDate; 7 | 8 | public record DadosTurmaDto(Long id, String codigo, Long idCurso, Long idSala, Turno turno, LocalDate dataInicio, LocalDate dataFim) { 9 | 10 | public DadosTurmaDto(TurmaJpaEntity entity) { 11 | this( 12 | entity.getId(), 13 | entity.getCodigo(), 14 | entity.getCurso().getId(), 15 | entity.getSala().getId(), 16 | entity.getTurno(), 17 | entity.getDataInicio(), 18 | entity.getDataFim() 19 | ); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaRealizarMatriculaPort.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 5 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 6 | import br.com.caelum.cursos.domain.ports.matricula.DadosParaRealizarMatricula; 7 | 8 | public record DadosParaRealizarMatriculaPort( 9 | Cpf cpfEstudante, 10 | String codigoTurma 11 | ) implements DadosParaRealizarMatricula { 12 | 13 | public DadosParaRealizarMatriculaPort(DadosParaRealizarMatriculaDto dados, EstudanteJpaEntity estudanteJpaEntity, TurmaJpaEntity turmaJpaEntity) { 14 | this(new Cpf(estudanteJpaEntity.getCpf()), turmaJpaEntity.getCodigo()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/TelefoneVo.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Telefone; 4 | import jakarta.persistence.Column; 5 | import jakarta.persistence.Embeddable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | @Embeddable 11 | @Getter 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class TelefoneVo { 15 | 16 | @Column(name = "telefone_ddd") 17 | private String ddd; 18 | @Column(name = "telefone_numero") 19 | private String numero; 20 | @Column(name = "telefone_whatsapp") 21 | private Boolean whatsapp; 22 | 23 | public TelefoneVo(Telefone telefone) { 24 | this(telefone.ddd(), telefone.numero(), telefone.whatsapp()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/repository/SalaJpaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.repository; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 4 | import br.com.caelum.cursos.domain.core.sala.Sala; 5 | import br.com.caelum.cursos.domain.ports.sala.SalaRepository; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | public interface SalaJpaRepository extends SalaRepository, JpaRepository { 9 | 10 | Boolean existsByNomeIgnoringCase(String nome); 11 | 12 | SalaJpaEntity findByNome(String nome); 13 | 14 | @Override 15 | default void cadastrar(Sala sala) { 16 | this.save(new SalaJpaEntity(sala)); 17 | } 18 | 19 | @Override 20 | default Boolean nomeJaCadastrado(String nome) { 21 | return this.existsByNomeIgnoringCase(nome); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/matricula/Matricula.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.matricula; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 4 | import br.com.caelum.cursos.domain.core.turma.Turma; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.util.UUID; 10 | 11 | @Getter 12 | @EqualsAndHashCode(of = {"turma", "estudante"}) 13 | @ToString(of = {"codigo", "turma", "estudante"}) 14 | public class Matricula { 15 | 16 | private String codigo; 17 | private Turma turma; 18 | private Estudante estudante; 19 | 20 | public Matricula(Turma turma, Estudante estudante) { 21 | this.codigo = UUID.randomUUID().toString(); 22 | this.turma = turma; 23 | this.estudante = estudante; 24 | this.turma.registrarMatricula(this); 25 | this.estudante.registrarMatricula(this); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/sala/Sala.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.sala; 2 | 3 | import br.com.caelum.cursos.domain.ports.sala.DadosParaCadastrarSala; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | 8 | import java.util.Objects; 9 | 10 | @Getter 11 | @EqualsAndHashCode(of = "nome") 12 | @ToString(of = {"nome", "capacidade"}) 13 | public class Sala { 14 | 15 | protected String nome; 16 | protected Integer capacidade; 17 | 18 | public Sala(DadosParaCadastrarSala dados) { 19 | Objects.requireNonNull(dados, "Dados para criação da sala são obrigatórios!"); 20 | Objects.requireNonNull(dados.nome(), "Nome da sala é obrigatório!"); 21 | Objects.requireNonNull(dados.capacidade(), "Capacidade da sala é obrigatória!"); 22 | 23 | this.nome = dados.nome(); 24 | this.capacidade = dados.capacidade(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/repository/CursoJpaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.repository; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.domain.core.curso.Curso; 5 | import br.com.caelum.cursos.domain.ports.curso.CursoRepository; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | public interface CursoJpaRepository extends CursoRepository, JpaRepository { 9 | 10 | Boolean existsByCodigoIgnoringCase(String codigo); 11 | 12 | CursoJpaEntity findByCodigo(String codigo); 13 | 14 | 15 | @Override 16 | default void cadastrar(Curso curso) { 17 | this.save(new CursoJpaEntity(curso)); 18 | } 19 | 20 | @Override 21 | default Boolean codigoJaCadastrado(String codigo) { 22 | return this.existsByCodigoIgnoringCase(codigo); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/SalaJpaEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import br.com.caelum.cursos.domain.core.sala.Sala; 4 | import jakarta.persistence.*; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Entity 10 | @Table(name = "salas") 11 | @Getter 12 | @EqualsAndHashCode(of = "id") 13 | @NoArgsConstructor 14 | public class SalaJpaEntity { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | private Long id; 19 | private String nome; 20 | private Integer capacidade; 21 | 22 | public SalaJpaEntity(Long id, String nome, Integer capacidade) { 23 | this.id = id; 24 | this.nome = nome; 25 | this.capacidade = capacidade; 26 | } 27 | 28 | public SalaJpaEntity(Sala sala) { 29 | this(null, sala.getNome(), sala.getCapacidade()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaCadastrarCursoDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Nivel; 4 | import br.com.caelum.cursos.domain.ports.curso.DadosParaCadastrarCurso; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | import java.time.Duration; 9 | 10 | public record DadosParaCadastrarCursoDto( 11 | @NotBlank(message = "Código do curso é obrigatório!") 12 | String codigo, 13 | @NotBlank(message = "Nome do curso é obrigatório!") 14 | String nome, 15 | @NotNull(message = "Nível do curso é obrigatório!") 16 | Nivel nivel, 17 | @NotNull(message = "Duração do curso é obrigatória!") 18 | Integer duracao 19 | ) implements DadosParaCadastrarCurso { 20 | 21 | @Override 22 | public Duration duracaoEmHoras() { 23 | return Duration.ofHours(duracao); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/CursoJpaEntityBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarCursoDto; 5 | import br.com.caelum.cursos.domain.core.curso.Nivel; 6 | 7 | import java.time.Duration; 8 | 9 | public class CursoJpaEntityBuilder { 10 | 11 | public static CursoJpaEntity build(Long id, String codigo, String nome, Nivel nivel, int cargaHoraria) { 12 | return new CursoJpaEntity(id, codigo, nome, nivel, Duration.ofHours(cargaHoraria)); 13 | } 14 | 15 | public static CursoJpaEntity build() { 16 | return build(50l, "xpto", "curso xpto", Nivel.BASICO, 20); 17 | } 18 | 19 | public static CursoJpaEntity build(long id, DadosParaCadastrarCursoDto dadosCadastro) { 20 | return build(id, dadosCadastro.codigo(), dadosCadastro.nome(), dadosCadastro.nivel(), dadosCadastro.duracao()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/CursoBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.curso.Nivel; 5 | import br.com.caelum.cursos.domain.ports.curso.DadosParaCadastrarCurso; 6 | 7 | import java.time.Duration; 8 | 9 | public class CursoBuilder { 10 | 11 | public static Curso build(String codigo, String nome, int cargaHoraria) { 12 | return new Curso(new DadosParaCadastrarCurso() { 13 | @Override 14 | public String codigo() { 15 | return codigo; 16 | } 17 | 18 | @Override 19 | public String nome() { 20 | return nome; 21 | } 22 | 23 | @Override 24 | public Nivel nivel() { 25 | return Nivel.BASICO; 26 | } 27 | 28 | @Override 29 | public Duration duracaoEmHoras() { 30 | return Duration.ofHours(cargaHoraria); 31 | } 32 | }); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 💻 Sobre o projeto 2 | 3 | Projeto em Java com exemplo de utilização da Hexagonal Architecture (https://alistair.cockburn.us/hexagonal-architecture/) e da Clean Architecture (https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 4 | 5 | Para fins didáticos, os módulos foram separados em pacotes dentro do mesmo projeto. 6 | 7 | --- 8 | 9 | ## ⚙️ Funcionalidades 10 | 11 | - Cadastro de estudantes; 12 | - Cadastro de salas; 13 | - Cadastro de cursos; 14 | - Abertura de turmas; 15 | - Matricular estudantes em turmas. 16 | 17 | --- 18 | 19 | ## 🛠 Tecnologias 20 | 21 | O projeto utiliza as seguintes tecnologias: 22 | 23 | - **[Java 17](https://www.oracle.com/java)** 24 | - **[Spring Boot 3](https://spring.io/projects/spring-boot)** 25 | - **[Maven](https://maven.apache.org)** 26 | - **[MySQL](https://www.mysql.com)** 27 | - **[Hibernate](https://hibernate.org)** 28 | - **[Flyway](https://flywaydb.org)** 29 | - **[Lombok](https://projectlombok.org)** 30 | - **[JUnit 5](https://junit.org/junit5)** 31 | - **[Mockito](https://site.mockito.org)** 32 | 33 | --- 34 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/MatriculaJpaEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Entity 9 | @Table(name = "matriculas") 10 | @Getter 11 | @EqualsAndHashCode(of = "id") 12 | @NoArgsConstructor 13 | public class MatriculaJpaEntity { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | private Long id; 18 | private String codigo; 19 | 20 | @ManyToOne(fetch = FetchType.LAZY) 21 | private EstudanteJpaEntity estudante; 22 | 23 | @ManyToOne(fetch = FetchType.LAZY) 24 | private TurmaJpaEntity turma; 25 | 26 | public MatriculaJpaEntity(Long id, String codigo, EstudanteJpaEntity estudante, TurmaJpaEntity turma) { 27 | this.id = id; 28 | this.codigo = codigo; 29 | this.estudante = estudante; 30 | this.turma = turma; 31 | } 32 | 33 | public MatriculaJpaEntity(String codigo, EstudanteJpaEntity estudante, TurmaJpaEntity turma) { 34 | this(null, codigo, estudante, turma); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/curso/Curso.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.curso; 2 | 3 | import br.com.caelum.cursos.domain.ports.curso.DadosParaCadastrarCurso; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | 8 | import java.time.Duration; 9 | import java.util.Objects; 10 | 11 | @Getter 12 | @EqualsAndHashCode(of = "codigo") 13 | @ToString(of = {"codigo", "nome"}) 14 | public class Curso { 15 | 16 | private String codigo; 17 | private String nome; 18 | private Nivel nivel; 19 | private Duration duracaoEmHoras; 20 | 21 | public Curso(DadosParaCadastrarCurso dados) { 22 | Objects.requireNonNull(dados, "Dados para criação do curso são obrigatórios!"); 23 | Objects.requireNonNull(dados.codigo(), "Código do curso é obrigatório!"); 24 | Objects.requireNonNull(dados.nome(), "Nome do curso é obrigatório!"); 25 | Objects.requireNonNull(dados.nivel(), "Nível do curso é obrigatório!"); 26 | Objects.requireNonNull(dados.duracaoEmHoras(), "Duração do curso é obrigatória!"); 27 | 28 | this.codigo = dados.codigo(); 29 | this.nome = dados.nome(); 30 | this.nivel = dados.nivel(); 31 | this.duracaoEmHoras = dados.duracaoEmHoras(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/CursoJpaEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.curso.Nivel; 5 | import jakarta.persistence.*; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.time.Duration; 11 | 12 | @Entity 13 | @Table(name = "cursos") 14 | @Getter 15 | @EqualsAndHashCode(of = "id") 16 | @NoArgsConstructor 17 | public class CursoJpaEntity { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | private String codigo; 23 | private String nome; 24 | @Enumerated(EnumType.STRING) 25 | private Nivel nivel; 26 | private Duration duracaoEmHoras; 27 | 28 | public CursoJpaEntity(Long id, String codigo, String nome, Nivel nivel, Duration duracaoEmHoras) { 29 | this.id = id; 30 | this.codigo = codigo; 31 | this.nome = nome; 32 | this.nivel = nivel; 33 | this.duracaoEmHoras = duracaoEmHoras; 34 | } 35 | 36 | public CursoJpaEntity(Curso curso) { 37 | this(null, curso.getCodigo(), curso.getNome(), curso.getNivel(), curso.getDuracaoEmHoras()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/sala/CadastrarSalaService.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.sala; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.sala.CadastrarSalaUseCase; 5 | import br.com.caelum.cursos.domain.ports.sala.DadosParaCadastrarSala; 6 | import br.com.caelum.cursos.domain.ports.sala.SalaRepository; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @RequiredArgsConstructor 10 | public class CadastrarSalaService implements CadastrarSalaUseCase { 11 | 12 | private final SalaRepository repository; 13 | 14 | public Sala execute(DadosParaCadastrarSala dados) { 15 | validarSala(dados); 16 | var sala = new Sala(dados); 17 | repository.cadastrar(sala); 18 | return sala; 19 | } 20 | 21 | private void validarSala(DadosParaCadastrarSala dados) { 22 | if (dados.capacidade() < 8) { 23 | throw new RegraDeNegocioException("Cadastro não realizado: Capacidade mínima deve ser 8!"); 24 | } 25 | 26 | var salaJaCadastrada = repository.nomeJaCadastrado(dados.nome()); 27 | if (salaJaCadastrada) { 28 | throw new RegraDeNegocioException("Cadastro não realizado: Nome já utilizado em outra sala!"); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/EstudanteJpaEntityBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.TelefoneVo; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarEstudanteDto; 6 | 7 | import java.time.LocalDate; 8 | 9 | public class EstudanteJpaEntityBuilder { 10 | 11 | public static EstudanteJpaEntity build(Long id, String nome, String cpf, LocalDate dataDeNascimento, String email, TelefoneVo telefone) { 12 | return new EstudanteJpaEntity(id, nome, cpf, dataDeNascimento, email, telefone); 13 | } 14 | 15 | public static EstudanteJpaEntity build() { 16 | return build( 17 | 50l, 18 | "Fulano da Silva", 19 | "000.000.000-00", 20 | LocalDate.of(1980, 10, 10), 21 | "fulano@email.com", 22 | new TelefoneVo("99", "999999999", true)); 23 | } 24 | 25 | public static EstudanteJpaEntity build(Long id, DadosParaCadastrarEstudanteDto dadosCadastro) { 26 | return build(id, dadosCadastro.nome(), dadosCadastro.cpfAsString(), dadosCadastro.dataDeNascimento(), dadosCadastro.emailAsString(), new TelefoneVo(dadosCadastro.celular())); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/curso/CadastrarCursoService.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.curso; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.curso.CadastrarCursoUseCase; 5 | import br.com.caelum.cursos.domain.ports.curso.CursoRepository; 6 | import br.com.caelum.cursos.domain.ports.curso.DadosParaCadastrarCurso; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @RequiredArgsConstructor 10 | public class CadastrarCursoService implements CadastrarCursoUseCase { 11 | 12 | private final CursoRepository repository; 13 | 14 | public Curso execute(DadosParaCadastrarCurso dados) { 15 | validarCurso(dados); 16 | var curso = new Curso(dados); 17 | repository.cadastrar(curso); 18 | return curso; 19 | } 20 | 21 | private void validarCurso(DadosParaCadastrarCurso dados) { 22 | if (dados.duracaoEmHoras().toHours() < 4) { 23 | throw new RegraDeNegocioException("Cadastro não realizado: Duração mínima deve ser de 4 horas!"); 24 | } 25 | 26 | var cursoJaCadastrado = repository.codigoJaCadastrado(dados.codigo()); 27 | if (cursoJaCadastrado) { 28 | throw new RegraDeNegocioException("Cadastro não realizado: Código já utilizado em outro curso!"); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/EstudanteBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 4 | import br.com.caelum.cursos.domain.core.estudante.Email; 5 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 6 | import br.com.caelum.cursos.domain.core.estudante.Telefone; 7 | import br.com.caelum.cursos.domain.ports.estudante.DadosParaCadastrarEstudante; 8 | 9 | import java.time.LocalDate; 10 | 11 | public class EstudanteBuilder { 12 | 13 | public static Estudante build(String nome, String cpf, LocalDate dataNascimento, String email, Telefone telefone) { 14 | return new Estudante(new DadosParaCadastrarEstudante() { 15 | @Override 16 | public String nome() { 17 | return nome; 18 | } 19 | 20 | @Override 21 | public Cpf cpf() { 22 | return new Cpf(cpf); 23 | } 24 | 25 | @Override 26 | public LocalDate dataDeNascimento() { 27 | return dataNascimento; 28 | } 29 | 30 | @Override 31 | public Email email() { 32 | return new Email(email); 33 | } 34 | 35 | @Override 36 | public Telefone celular() { 37 | return telefone; 38 | } 39 | }); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/repository/EstudanteJpaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.repository; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarEstudanteDto; 5 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 6 | import br.com.caelum.cursos.domain.core.estudante.Email; 7 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 8 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 9 | import org.springframework.data.jpa.repository.JpaRepository; 10 | 11 | public interface EstudanteJpaRepository extends EstudanteRepository, JpaRepository { 12 | 13 | EstudanteJpaEntity findByCpf(String cpf); 14 | 15 | Boolean existsByCpfOrEmailIgnoringCase(String cpf, String email); 16 | 17 | @Override 18 | default void cadastrar(Estudante estudante) { 19 | this.save(new EstudanteJpaEntity(estudante)); 20 | } 21 | 22 | @Override 23 | default Estudante buscarPorCpf(Cpf cpf) { 24 | var entity = this.findByCpf(cpf.numero()); 25 | var dados = new DadosParaCadastrarEstudanteDto(entity); 26 | return new Estudante(dados); 27 | } 28 | 29 | @Override 30 | default Boolean CpfOuEmailJaCadastrado(Cpf cpf, Email email) { 31 | return this.existsByCpfOrEmailIgnoringCase(cpf.numero(), email.email()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/estudante/CadastrarEstudanteService.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.estudante.CadastrarEstudanteUseCase; 5 | import br.com.caelum.cursos.domain.ports.estudante.DadosParaCadastrarEstudante; 6 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.time.LocalDate; 10 | import java.time.Period; 11 | 12 | @RequiredArgsConstructor 13 | public class CadastrarEstudanteService implements CadastrarEstudanteUseCase { 14 | 15 | private final EstudanteRepository repository; 16 | 17 | public Estudante execute(DadosParaCadastrarEstudante dados) { 18 | validarEstudante(dados); 19 | var estudante = new Estudante(dados); 20 | repository.cadastrar(estudante); 21 | return estudante; 22 | } 23 | 24 | private void validarEstudante(DadosParaCadastrarEstudante dados) { 25 | if (Period.between(dados.dataDeNascimento(), LocalDate.now()).getYears() < 18) { 26 | throw new RegraDeNegocioException("Cadastro não realizado: Idade mínima é de 18 anos!"); 27 | } 28 | 29 | var estudanteJaCadastrado = repository.CpfOuEmailJaCadastrado(dados.cpf(), dados.email()); 30 | if (estudanteJaCadastrado) { 31 | throw new RegraDeNegocioException("Cadastro não realizado: CPF ou email já cadastrado para outro estudante!"); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaAbrirTurmaPort.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 5 | import br.com.caelum.cursos.domain.core.curso.Curso; 6 | import br.com.caelum.cursos.domain.core.sala.Sala; 7 | import br.com.caelum.cursos.domain.core.turma.Turno; 8 | import br.com.caelum.cursos.domain.ports.turma.DadosParaAbrirTurma; 9 | 10 | import java.time.LocalDate; 11 | 12 | public record DadosParaAbrirTurmaPort( 13 | String codigo, 14 | Curso curso, 15 | Sala sala, 16 | Turno turno, 17 | LocalDate dataInicio, 18 | LocalDate dataFim 19 | ) implements DadosParaAbrirTurma { 20 | 21 | public DadosParaAbrirTurmaPort(DadosParaAbrirTurmaDto dados, CursoJpaEntity cursoJpaEntity, SalaJpaEntity salaJpaEntity) { 22 | this( 23 | dados.codigo(), 24 | new Curso(new DadosParaCadastrarCursoDto( 25 | cursoJpaEntity.getCodigo(), 26 | cursoJpaEntity.getNome(), 27 | cursoJpaEntity.getNivel(), 28 | (int) cursoJpaEntity.getDuracaoEmHoras().toHours() 29 | )), 30 | new Sala(new DadosParaCadastrarSalaDto(salaJpaEntity.getNome(), salaJpaEntity.getCapacidade())), 31 | dados.turno(), 32 | dados.dataInicio(), 33 | dados.dataFim() 34 | ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/controller/SalaController.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.SalaJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarSalaDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosSalaDto; 6 | import br.com.caelum.cursos.domain.ports.sala.CadastrarSalaUseCase; 7 | import jakarta.validation.Valid; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.transaction.annotation.Transactional; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import org.springframework.web.util.UriComponentsBuilder; 16 | 17 | @RestController 18 | @RequestMapping("salas") 19 | @RequiredArgsConstructor 20 | public class SalaController { 21 | 22 | private final CadastrarSalaUseCase useCase; 23 | private final SalaJpaRepository repository; 24 | 25 | @PostMapping 26 | @Transactional 27 | public ResponseEntity cadastrar(@RequestBody @Valid DadosParaCadastrarSalaDto dados, UriComponentsBuilder uriBuilder) { 28 | useCase.execute(dados); 29 | var entity = repository.findByNome(dados.nome()); 30 | var uri = uriBuilder.path("salas/{id}").buildAndExpand(entity.getId()).toUri(); 31 | return ResponseEntity.created(uri).body(new DadosSalaDto(entity)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/controller/CursoController.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.CursoJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosCursoDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarCursoDto; 6 | import br.com.caelum.cursos.domain.ports.curso.CadastrarCursoUseCase; 7 | import jakarta.validation.Valid; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.transaction.annotation.Transactional; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import org.springframework.web.util.UriComponentsBuilder; 16 | 17 | @RestController 18 | @RequestMapping("cursos") 19 | @RequiredArgsConstructor 20 | public class CursoController { 21 | 22 | private final CadastrarCursoUseCase useCase; 23 | private final CursoJpaRepository repository; 24 | 25 | @PostMapping 26 | @Transactional 27 | public ResponseEntity cadastrar(@RequestBody @Valid DadosParaCadastrarCursoDto dados, UriComponentsBuilder uriBuilder) { 28 | useCase.execute(dados); 29 | var entity = repository.findByCodigo(dados.codigo()); 30 | var uri = uriBuilder.path("cursos/{id}").buildAndExpand(entity.getId()).toUri(); 31 | return ResponseEntity.created(uri).body(new DadosCursoDto(entity)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaAbrirTurmaDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 4 | import br.com.caelum.cursos.domain.core.turma.Turno; 5 | import jakarta.validation.constraints.Future; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | 9 | import java.time.LocalDate; 10 | 11 | public record DadosParaAbrirTurmaDto( 12 | @NotBlank(message = "Codigo da turma é obrigatório!") 13 | String codigo, 14 | @NotNull(message = "Id do curso da turma é obrigatório!") 15 | Long idCurso, 16 | @NotNull(message = "Id da sala da turma é obrigatório!") 17 | Long idSala, 18 | @NotNull(message = "Turno da turma é obrigatório!") 19 | Turno turno, 20 | @NotNull(message = "Data início da turma é obrigatória!") 21 | @Future(message = "Data início da turma deve ser uma data futura!") 22 | LocalDate dataInicio, 23 | @NotNull(message = "Data fim da turma é obrigatória!") 24 | @Future(message = "Data fim da turma deve ser uma data futura!") 25 | LocalDate dataFim 26 | ) { 27 | 28 | public DadosParaAbrirTurmaDto(TurmaJpaEntity entity) { 29 | this( 30 | entity.getCodigo(), 31 | entity.getCurso().getId(), 32 | entity.getSala().getId(), 33 | entity.getTurno(), 34 | entity.getDataInicio(), 35 | entity.getDataFim() 36 | ); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/controller/EstudanteController.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.EstudanteJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosEstudanteDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarEstudanteDto; 6 | import br.com.caelum.cursos.domain.ports.estudante.CadastrarEstudanteUseCase; 7 | import jakarta.validation.Valid; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.transaction.annotation.Transactional; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import org.springframework.web.util.UriComponentsBuilder; 16 | 17 | @RestController 18 | @RequestMapping("estudantes") 19 | @RequiredArgsConstructor 20 | public class EstudanteController { 21 | 22 | private final CadastrarEstudanteUseCase useCase; 23 | private final EstudanteJpaRepository repository; 24 | 25 | @PostMapping 26 | @Transactional 27 | public ResponseEntity cadastrar(@RequestBody @Valid DadosParaCadastrarEstudanteDto dados, UriComponentsBuilder uriBuilder) { 28 | useCase.execute(dados); 29 | var entity = repository.findByCpf(dados.cpf().numero()); 30 | var uri = uriBuilder.path("estudantes/{id}").buildAndExpand(entity.getId()).toUri(); 31 | return ResponseEntity.created(uri).body(new DadosEstudanteDto(entity)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/EstudanteJpaEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 4 | import jakarta.persistence.*; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.time.LocalDate; 10 | 11 | @Entity 12 | @Table(name = "estudantes") 13 | @Getter 14 | @EqualsAndHashCode(of = "id") 15 | @NoArgsConstructor 16 | public class EstudanteJpaEntity { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.IDENTITY) 20 | private Long id; 21 | private String nome; 22 | private String cpf; 23 | private LocalDate dataDeNascimento; 24 | private String email; 25 | 26 | @Embedded 27 | private TelefoneVo celular; 28 | 29 | public EstudanteJpaEntity(Long id, String nome, String cpf, LocalDate dataDeNascimento, String email, TelefoneVo celular) { 30 | this.id = id; 31 | this.nome = nome; 32 | this.cpf = cpf; 33 | this.dataDeNascimento = dataDeNascimento; 34 | this.email = email; 35 | this.celular = celular; 36 | } 37 | 38 | public EstudanteJpaEntity(Estudante estudante) { 39 | this( 40 | null, 41 | estudante.getNome(), 42 | estudante.getCpf().numero(), 43 | estudante.getDataDeNascimento(), 44 | estudante.getEmail().email(), 45 | new TelefoneVo( 46 | estudante.getCelular().ddd(), 47 | estudante.getCelular().numero(), 48 | estudante.getCelular().whatsapp())); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/entity/TurmaJpaEntity.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.entity; 2 | 3 | import br.com.caelum.cursos.domain.core.turma.Turma; 4 | import br.com.caelum.cursos.domain.core.turma.Turno; 5 | import jakarta.persistence.*; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.time.LocalDate; 11 | 12 | @Entity 13 | @Table(name = "turmas") 14 | @Getter 15 | @EqualsAndHashCode(of = "id") 16 | @NoArgsConstructor 17 | public class TurmaJpaEntity { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | private String codigo; 23 | 24 | @ManyToOne(fetch = FetchType.LAZY) 25 | private CursoJpaEntity curso; 26 | 27 | @ManyToOne(fetch = FetchType.LAZY) 28 | private SalaJpaEntity sala; 29 | 30 | @Enumerated(EnumType.STRING) 31 | private Turno turno; 32 | private LocalDate dataInicio; 33 | private LocalDate dataFim; 34 | 35 | public TurmaJpaEntity(Long id, String codigo, CursoJpaEntity curso, SalaJpaEntity sala, Turno turno, LocalDate dataInicio, LocalDate dataFim) { 36 | this.id = id; 37 | this.codigo = codigo; 38 | this.curso = curso; 39 | this.sala = sala; 40 | this.turno = turno; 41 | this.dataInicio = dataInicio; 42 | this.dataFim = dataFim; 43 | } 44 | 45 | public TurmaJpaEntity(Turma turma, CursoJpaEntity curso, SalaJpaEntity sala) { 46 | this( 47 | null, 48 | turma.getCodigo(), 49 | curso, 50 | sala, 51 | turma.getTurno(), 52 | turma.getDataInicio(), 53 | turma.getDataFim()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/TurmaJpaEntityBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 5 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 6 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaDto; 7 | import br.com.caelum.cursos.domain.core.curso.Nivel; 8 | import br.com.caelum.cursos.domain.core.turma.Turno; 9 | 10 | import java.time.LocalDate; 11 | 12 | public class TurmaJpaEntityBuilder { 13 | 14 | public static TurmaJpaEntity build(Long id, String codigo, CursoJpaEntity curso, SalaJpaEntity sala, Turno turno, LocalDate dataInicio, LocalDate dataFim) { 15 | return new TurmaJpaEntity(id, codigo, curso, sala, turno, dataInicio, dataFim); 16 | } 17 | 18 | public static TurmaJpaEntity build() { 19 | return build( 20 | 50l, 21 | "T-001", 22 | CursoJpaEntityBuilder.build(), 23 | SalaJpaentityBuilder.build(), 24 | Turno.INTEGRAL, 25 | LocalDate.now().plusDays(1), 26 | LocalDate.now().plusDays(6)); 27 | } 28 | 29 | public static TurmaJpaEntity build(long id, DadosParaAbrirTurmaDto dadosCadastro) { 30 | return build( 31 | id, 32 | dadosCadastro.codigo(), 33 | CursoJpaEntityBuilder.build(dadosCadastro.idCurso(),"C-001", "Curso", Nivel.BASICO, 10), 34 | SalaJpaentityBuilder.build(dadosCadastro.idSala(), "Sala 01", 10), 35 | dadosCadastro.turno(), 36 | dadosCadastro.dataInicio(), 37 | dadosCadastro.dataFim()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/builders/TurmaBuilder.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.builders; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.sala.Sala; 5 | import br.com.caelum.cursos.domain.core.turma.Turma; 6 | import br.com.caelum.cursos.domain.core.turma.Turno; 7 | import br.com.caelum.cursos.domain.ports.turma.DadosParaAbrirTurma; 8 | 9 | import java.time.LocalDate; 10 | 11 | public class TurmaBuilder { 12 | 13 | public static Turma build(String codigo, LocalDate dataInicio, LocalDate dataFim, Turno turno, Curso curso, Sala sala) { 14 | return new Turma(new DadosParaAbrirTurma() { 15 | @Override 16 | public String codigo() { 17 | return codigo; 18 | } 19 | 20 | @Override 21 | public Curso curso() { 22 | return curso; 23 | } 24 | 25 | @Override 26 | public Sala sala() { 27 | return sala; 28 | } 29 | 30 | @Override 31 | public Turno turno() { 32 | return turno; 33 | } 34 | 35 | @Override 36 | public LocalDate dataInicio() { 37 | return dataInicio; 38 | } 39 | 40 | @Override 41 | public LocalDate dataFim() { 42 | return dataFim; 43 | } 44 | }); 45 | } 46 | 47 | public static Turma buildFrom(Turma modelo, LocalDate dataInicio, LocalDate dataFim) { 48 | return build(modelo.getCodigo(), dataInicio, dataFim, modelo.getTurno(), modelo.getCurso(), modelo.getSala()); 49 | } 50 | 51 | public static Turma buildFrom(Turma modelo, Sala sala) { 52 | return build(modelo.getCodigo(), modelo.getDataInicio(), modelo.getDataFim(), modelo.getTurno(), modelo.getCurso(), sala); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/infra/spring/ExceptionHandlerAdvice.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.infra.spring; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import jakarta.persistence.EntityNotFoundException; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.validation.FieldError; 7 | import org.springframework.web.bind.MethodArgumentNotValidException; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | 11 | import java.util.List; 12 | 13 | @RestControllerAdvice 14 | public class ExceptionHandlerAdvice { 15 | 16 | record ErroDeValidacao(String campo, String erro){ 17 | ErroDeValidacao(FieldError field) { 18 | this(field.getField().replace("AsString", ""), field.getDefaultMessage()); 19 | } 20 | } 21 | 22 | @ExceptionHandler(MethodArgumentNotValidException.class) 23 | public ResponseEntity> handleValidationError(MethodArgumentNotValidException ex) { 24 | var errors = ex.getFieldErrors().stream().map(ErroDeValidacao::new).toList(); 25 | return ResponseEntity.badRequest().body(errors); 26 | } 27 | 28 | @ExceptionHandler(RegraDeNegocioException.class) 29 | public ResponseEntity handleRegraDeNegocioException(RegraDeNegocioException ex) { 30 | return ResponseEntity.badRequest().body(ex.getMessage()); 31 | } 32 | 33 | @ExceptionHandler(EntityNotFoundException.class) 34 | public ResponseEntity handleEntityNotfound(EntityNotFoundException ex) { 35 | return ResponseEntity.badRequest().body(ex.getMessage()); 36 | } 37 | 38 | @ExceptionHandler(Exception.class) 39 | public ResponseEntity handleException(Exception ex) { 40 | return ResponseEntity.internalServerError().body("Ocorreu um erro no servidor: " +ex.getMessage()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/infra/spring/UseCaseBeansConfiguration.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.infra.spring; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.CadastrarCursoService; 4 | import br.com.caelum.cursos.domain.core.estudante.CadastrarEstudanteService; 5 | import br.com.caelum.cursos.domain.core.matricula.MatricularEstudanteEmTurmaService; 6 | import br.com.caelum.cursos.domain.core.sala.CadastrarSalaService; 7 | import br.com.caelum.cursos.domain.core.turma.AbrirTurmaService; 8 | import br.com.caelum.cursos.domain.ports.curso.CursoRepository; 9 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 10 | import br.com.caelum.cursos.domain.ports.matricula.MatriculaRepository; 11 | import br.com.caelum.cursos.domain.ports.sala.SalaRepository; 12 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 13 | import lombok.RequiredArgsConstructor; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.Configuration; 16 | 17 | @Configuration 18 | @RequiredArgsConstructor 19 | public class UseCaseBeansConfiguration { 20 | 21 | private final SalaRepository salaRepository; 22 | private final CursoRepository cursoRepository; 23 | private final EstudanteRepository estudanteRepository; 24 | private final TurmaRepository turmaRepository; 25 | private final MatriculaRepository matriculaRepository; 26 | 27 | @Bean 28 | public CadastrarSalaService cadastrarSalaUseCase() { 29 | return new CadastrarSalaService(salaRepository); 30 | } 31 | 32 | @Bean 33 | public CadastrarCursoService cadastrarCursoUseCase() { 34 | return new CadastrarCursoService(cursoRepository); 35 | } 36 | 37 | @Bean 38 | public CadastrarEstudanteService cadastrarEstudanteUseCase() { 39 | return new CadastrarEstudanteService(estudanteRepository); 40 | } 41 | 42 | @Bean 43 | public AbrirTurmaService abrirTurmaUseCase() { 44 | return new AbrirTurmaService(turmaRepository); 45 | } 46 | 47 | @Bean 48 | public MatricularEstudanteEmTurmaService matricularEstudanteEmTurmaUseCase() { 49 | return new MatricularEstudanteEmTurmaService(turmaRepository, estudanteRepository, matriculaRepository); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/estudante/Estudante.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 4 | import br.com.caelum.cursos.domain.core.turma.Turma; 5 | import br.com.caelum.cursos.domain.ports.estudante.DadosParaCadastrarEstudante; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | import java.time.LocalDate; 11 | import java.util.HashSet; 12 | import java.util.Objects; 13 | import java.util.Set; 14 | 15 | @Getter 16 | @EqualsAndHashCode(of = "cpf") 17 | @ToString(of = {"nome", "cpf"}) 18 | public class Estudante { 19 | 20 | private String nome; 21 | private Cpf cpf; 22 | private LocalDate dataDeNascimento; 23 | private Email email; 24 | private Telefone celular; 25 | 26 | private Set matriculas = new HashSet<>(); 27 | 28 | public Estudante(DadosParaCadastrarEstudante dados) { 29 | Objects.requireNonNull(dados, "Dados para criação do estudante são obrigatórios!"); 30 | Objects.requireNonNull(dados.nome(), "Nome do estudante é obrigatório!"); 31 | Objects.requireNonNull(dados.cpf(), "CPF do estudante é obrigatório!"); 32 | Objects.requireNonNull(dados.dataDeNascimento(), "Data de nascimento do estudante é obrigatória!"); 33 | Objects.requireNonNull(dados.email(), "Email do estudante é obrigatório!"); 34 | Objects.requireNonNull(dados.celular(), "Celular do estudante é obrigatório!"); 35 | 36 | this.nome = dados.nome(); 37 | this.cpf = dados.cpf(); 38 | this.dataDeNascimento = dados.dataDeNascimento(); 39 | this.email = dados.email(); 40 | this.celular = dados.celular(); 41 | } 42 | 43 | public boolean estaComLimiteDeTurmasEmAndamento() { 44 | var turmasEmAndamento = matriculas 45 | .stream() 46 | .map(Matricula::getTurma) 47 | .filter(Turma::isEmAndamento) 48 | .count(); 49 | return turmasEmAndamento == 2; 50 | } 51 | 52 | public void registrarMatricula(Matricula matricula) { 53 | this.matriculas.add(matricula); 54 | } 55 | 56 | public boolean estaMatriculadoNaTurma(Turma turma) { 57 | return this.matriculas 58 | .stream() 59 | .map(Matricula::getTurma) 60 | .anyMatch(t -> t.equals(turma)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/matricula/MatricularEstudanteEmTurmaService.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.matricula; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 5 | import br.com.caelum.cursos.domain.core.turma.Turma; 6 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 7 | import br.com.caelum.cursos.domain.ports.matricula.DadosParaRealizarMatricula; 8 | import br.com.caelum.cursos.domain.ports.matricula.MatriculaRepository; 9 | import br.com.caelum.cursos.domain.ports.matricula.MatricularEstudanteEmTurmaUseCase; 10 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 11 | import lombok.RequiredArgsConstructor; 12 | 13 | @RequiredArgsConstructor 14 | public class MatricularEstudanteEmTurmaService implements MatricularEstudanteEmTurmaUseCase { 15 | 16 | private final TurmaRepository turmaRepository; 17 | private final EstudanteRepository estudanteRepository; 18 | private final MatriculaRepository matriculaRepository; 19 | 20 | public Matricula execute(DadosParaRealizarMatricula dados) { 21 | var turma = turmaRepository.buscarPorCodigo(dados.codigoTurma()); 22 | validarTurma(turma); 23 | 24 | var estudante = estudanteRepository.buscarPorCpf(dados.cpfEstudante()); 25 | validarEstudante(estudante, turma); 26 | 27 | var matricula = new Matricula(turma, estudante); 28 | matriculaRepository.registrar(matricula); 29 | return matricula; 30 | } 31 | 32 | private void validarTurma(Turma turma) { 33 | if (turma.estaLotada()) { 34 | throw new RegraDeNegocioException("Matricula não realizada: turma está lotada!"); 35 | } 36 | 37 | if (!turma.estaComMatriculasAbertas()) { 38 | throw new RegraDeNegocioException("Matricula não realizada: turma com período de matriculas encerado!"); 39 | } 40 | } 41 | 42 | private void validarEstudante(Estudante estudante, Turma turma) { 43 | if (estudante.estaMatriculadoNaTurma(turma)) { 44 | throw new RegraDeNegocioException("Matricula não realizada: estudante já possui matricula para esta turma!"); 45 | } 46 | 47 | if (estudante.estaComLimiteDeTurmasEmAndamento()) { 48 | throw new RegraDeNegocioException("Matricula não realizada: estudante com limite de turmas em andamento!"); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/dto/DadosParaCadastrarEstudanteDto.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.dto; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 5 | import br.com.caelum.cursos.domain.core.estudante.Email; 6 | import br.com.caelum.cursos.domain.core.estudante.Telefone; 7 | import br.com.caelum.cursos.domain.ports.estudante.DadosParaCadastrarEstudante; 8 | import com.fasterxml.jackson.annotation.JsonAlias; 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.NotNull; 11 | 12 | import java.time.LocalDate; 13 | 14 | public record DadosParaCadastrarEstudanteDto( 15 | @NotBlank(message = "Nome do estudante é obrigatório!") 16 | String nome, 17 | 18 | @NotBlank(message = "CPF do curso é obrigatório!") 19 | @JsonAlias("cpf") 20 | String cpfAsString, 21 | 22 | @NotNull(message = "Data de nascimento do curso é obrigatória!") 23 | LocalDate dataDeNascimento, 24 | 25 | @NotBlank(message = "Email do estudante é obrigatório!") 26 | @jakarta.validation.constraints.Email(message = "Email em formato inválido!") 27 | @JsonAlias("email") 28 | String emailAsString, 29 | 30 | @NotNull(message = "Celular do estudante é obrigatório!") 31 | @JsonAlias("celular") 32 | DadosTelefoneDto celularDto 33 | ) implements DadosParaCadastrarEstudante { 34 | 35 | public DadosParaCadastrarEstudanteDto(EstudanteJpaEntity entity) { 36 | this( 37 | entity.getNome(), 38 | entity.getCpf(), 39 | entity.getDataDeNascimento(), 40 | entity.getEmail(), 41 | new DadosTelefoneDto( 42 | entity.getCelular().getDdd(), 43 | entity.getCelular().getNumero(), 44 | entity.getCelular().getWhatsapp())); 45 | } 46 | 47 | @Override 48 | public Email email() { 49 | return new Email(emailAsString); 50 | } 51 | 52 | @Override 53 | public Cpf cpf() { 54 | return new Cpf(cpfAsString); 55 | } 56 | 57 | @Override 58 | public Telefone celular() { 59 | return new Telefone(celularDto.ddd(), celularDto.numero(), celularDto().whatsapp()); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/turma/Turma.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.turma; 2 | 3 | import br.com.caelum.cursos.domain.core.curso.Curso; 4 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 5 | import br.com.caelum.cursos.domain.core.sala.Sala; 6 | import br.com.caelum.cursos.domain.ports.turma.DadosParaAbrirTurma; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.ToString; 10 | 11 | import java.time.LocalDate; 12 | import java.util.HashSet; 13 | import java.util.Objects; 14 | import java.util.Set; 15 | 16 | @Getter 17 | @EqualsAndHashCode(of = "codigo") 18 | @ToString(of = {"codigo", "curso", "turno", "dataInicio", "dataFim"}) 19 | public class Turma { 20 | 21 | private String codigo; 22 | private Curso curso; 23 | private Sala sala; 24 | private Turno turno; 25 | private LocalDate dataInicio; 26 | private LocalDate dataFim; 27 | 28 | private Set matriculas = new HashSet<>(); 29 | 30 | public Turma(DadosParaAbrirTurma dados) { 31 | Objects.requireNonNull(dados, "Dados para criação da turma são obrigatórios!"); 32 | Objects.requireNonNull(dados.codigo(), "Código da turma é obrigatório!"); 33 | Objects.requireNonNull(dados.curso(), "Curso da turma é obrigatório!"); 34 | Objects.requireNonNull(dados.sala(), "Sala da turma é obrigatória!"); 35 | Objects.requireNonNull(dados.turno(), "Turno da turma é obrigatório!"); 36 | Objects.requireNonNull(dados.dataInicio(), "Data de início da turma é obrigatória!"); 37 | Objects.requireNonNull(dados.dataFim(), "Data fim da turma é obrigatória!"); 38 | 39 | this.codigo = dados.codigo(); 40 | this.curso = dados.curso(); 41 | this.sala = dados.sala(); 42 | this.turno = dados.turno(); 43 | this.dataInicio = dados.dataInicio(); 44 | this.dataFim = dados.dataFim(); 45 | } 46 | 47 | public boolean estaLotada() { 48 | return this.matriculas.size() == this.sala.getCapacidade(); 49 | } 50 | 51 | public boolean estaComMatriculasAbertas() { 52 | var dataLimiteParaMatriculas = this.dataInicio.plusDays(1); 53 | var hoje = LocalDate.now(); 54 | return !hoje.isAfter(dataLimiteParaMatriculas); 55 | } 56 | 57 | public boolean isEmAndamento() { 58 | var hoje = LocalDate.now(); 59 | return !hoje.isAfter(this.dataFim); 60 | } 61 | 62 | public void registrarMatricula(Matricula matricula) { 63 | this.matriculas.add(matricula); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/domain/core/turma/AbrirTurmaService.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.turma; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.turma.AbrirTurmaUseCase; 5 | import br.com.caelum.cursos.domain.ports.turma.DadosParaAbrirTurma; 6 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.time.LocalDate; 10 | 11 | @RequiredArgsConstructor 12 | public class AbrirTurmaService implements AbrirTurmaUseCase { 13 | 14 | private final TurmaRepository repository; 15 | 16 | public Turma execute(DadosParaAbrirTurma dados) { 17 | validarTurma(dados); 18 | var turma = new Turma(dados); 19 | repository.abrir(turma); 20 | return turma; 21 | } 22 | 23 | private void validarTurma(DadosParaAbrirTurma dados) { 24 | validarDatas(dados); 25 | validarCodigo(dados); 26 | validarSala(dados); 27 | validarLimiteDeTurmas(dados); 28 | } 29 | 30 | private void validarDatas(DadosParaAbrirTurma dados) { 31 | if (dados.dataInicio().isBefore(LocalDate.now())) { 32 | throw new RegraDeNegocioException("Cadastro não realizado: Data de início não pode ser anterior a hoje!"); 33 | } 34 | 35 | if (dados.dataFim().isBefore(dados.dataInicio())) { 36 | throw new RegraDeNegocioException("Cadastro não realizado: Data fim deve ser posterior a data de início!"); 37 | } 38 | } 39 | 40 | private void validarCodigo(DadosParaAbrirTurma dados) { 41 | var turmaJaCadastrada = repository.codigoJaCadastrado(dados.codigo()); 42 | if (turmaJaCadastrada) { 43 | throw new RegraDeNegocioException("Cadastro não realizado: Código já utilizado em outra turma!"); 44 | } 45 | } 46 | 47 | private void validarSala(DadosParaAbrirTurma dados) { 48 | var salaOcupada = repository.salaJaOcupadaNoPeriodo(dados.sala(), dados.dataInicio(), dados.dataFim()); 49 | if (salaOcupada) { 50 | throw new RegraDeNegocioException("Cadastro não realizado: Sala já utilizada por outra turma no mesmo período!"); 51 | } 52 | } 53 | 54 | private void validarLimiteDeTurmas(DadosParaAbrirTurma dados) { 55 | var turmasEmAndamento = repository.quantidadeDeTurmasEmAbertoDoCurso(dados.curso()); 56 | if (turmasEmAndamento == 4) { 57 | throw new RegraDeNegocioException("Cadastro não realizado: Curso atingiu limite de 4 turmas em andamento!"); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/domain/core/sala/CadastrarSalaServiceTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.sala; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.sala.DadosParaCadastrarSala; 5 | import br.com.caelum.cursos.domain.ports.sala.SalaRepository; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.DisplayName; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | import static org.mockito.BDDMockito.given; 16 | import static org.mockito.Mockito.lenient; 17 | import static org.mockito.Mockito.verify; 18 | 19 | @ExtendWith(MockitoExtension.class) 20 | class CadastrarSalaServiceTest { 21 | 22 | @InjectMocks 23 | private CadastrarSalaService service; 24 | @Mock 25 | private SalaRepository repository; 26 | @Mock 27 | private DadosParaCadastrarSala dados; 28 | 29 | @BeforeEach 30 | void setup() { 31 | lenient().when(dados.nome()).thenReturn("Sala 1"); 32 | lenient().when(dados.capacidade()).thenReturn(20); 33 | } 34 | 35 | @Test 36 | @DisplayName("Nao deveria cadastrar sala com nome ja cadastrado") 37 | void cenario1() { 38 | given(repository.nomeJaCadastrado(dados.nome())).willReturn(true); 39 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 40 | assertEquals("Cadastro não realizado: Nome já utilizado em outra sala!", ex.getMessage()); 41 | } 42 | 43 | @Test 44 | @DisplayName("Nao deveria cadastrar sala com capacidade menor do que 8") 45 | void cenario2() { 46 | given(dados.capacidade()).willReturn(5); 47 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 48 | assertEquals("Cadastro não realizado: Capacidade mínima deve ser 8!", ex.getMessage()); 49 | } 50 | 51 | @Test 52 | @DisplayName("Deveria cadastrar sala com capacidade igual a 8") 53 | void cenario3() { 54 | given(dados.capacidade()).willReturn(8); 55 | var sala = assertDoesNotThrow(() -> service.execute(dados)); 56 | verify(repository).cadastrar(sala); 57 | } 58 | 59 | @Test 60 | @DisplayName("Deveria cadastrar sala com capacidade maior do que 8") 61 | void cenario4() { 62 | var sala = assertDoesNotThrow(() -> service.execute(dados)); 63 | verify(repository).cadastrar(sala); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.1.5 9 | 10 | 11 | br.com.caelum 12 | cursos 13 | 0.0.1-SNAPSHOT 14 | cursos 15 | Demo project for Spring Boot 16 | 17 | 17 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-validation 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.flywaydb 34 | flyway-core 35 | 36 | 37 | org.flywaydb 38 | flyway-mysql 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-devtools 44 | runtime 45 | true 46 | 47 | 48 | com.mysql 49 | mysql-connector-j 50 | runtime 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | true 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-maven-plugin 69 | 70 | 71 | 72 | org.projectlombok 73 | lombok 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/database/jpa/repository/TurmaJpaRepository.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.database.jpa.repository; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaPort; 6 | import br.com.caelum.cursos.domain.core.curso.Curso; 7 | import br.com.caelum.cursos.domain.core.sala.Sala; 8 | import br.com.caelum.cursos.domain.core.turma.Turma; 9 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 10 | import org.springframework.data.jpa.repository.JpaRepository; 11 | import org.springframework.data.jpa.repository.Query; 12 | 13 | import java.time.LocalDate; 14 | 15 | public interface TurmaJpaRepository extends TurmaRepository, JpaRepository { 16 | 17 | TurmaJpaEntity findByCodigo(String codigo); 18 | 19 | Boolean existsByCodigo(String codigo); 20 | 21 | Integer countByCursoCodigoAndDataFimGreaterThan(String codigo, LocalDate data); 22 | 23 | @Query(""" 24 | SELECT 25 | CASE WHEN COUNT(*) > 0 THEN true ELSE false END 26 | FROM 27 | TurmaJpaEntity t 28 | WHERE 29 | t.sala.nome = :sala 30 | AND 31 | ( 32 | (t.dataInicio <= :dataFim AND t.dataFim >= :dataInicio) 33 | OR 34 | (t.dataInicio >= :dataInicio AND t.dataFim <= :dataFim) 35 | ) 36 | """) 37 | Boolean salaJaOcupadaNoPeriodo(String sala, LocalDate dataInicio, LocalDate dataFim); 38 | 39 | @Override 40 | default Boolean salaJaOcupadaNoPeriodo(Sala sala, LocalDate dataInicio, LocalDate dataFim) { 41 | return this.salaJaOcupadaNoPeriodo(sala.getNome(), dataInicio, dataFim); 42 | } 43 | 44 | @Override 45 | default Boolean codigoJaCadastrado(String codigo) { 46 | return this.existsByCodigo(codigo); 47 | } 48 | 49 | @Override 50 | default Turma buscarPorCodigo(String codigo) { 51 | var entity = this.findByCodigo(codigo); 52 | var dados = new DadosParaAbrirTurmaDto(entity); 53 | return new Turma(new DadosParaAbrirTurmaPort(dados, entity.getCurso(), entity.getSala())); 54 | } 55 | 56 | @Override 57 | default Integer quantidadeDeTurmasEmAbertoDoCurso(Curso curso) { 58 | return this.countByCursoCodigoAndDataFimGreaterThan(curso.getCodigo(), LocalDate.now()); 59 | } 60 | 61 | @Override 62 | default void abrir(Turma turma) { 63 | //nao da para salvar a turma daqui, pois precisa carregar os relacionamentos =/ 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/controller/TurmaController.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.repository.CursoJpaRepository; 5 | import br.com.caelum.cursos.adapters.database.jpa.repository.SalaJpaRepository; 6 | import br.com.caelum.cursos.adapters.database.jpa.repository.TurmaJpaRepository; 7 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaDto; 8 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaPort; 9 | import br.com.caelum.cursos.adapters.web.dto.DadosTurmaDto; 10 | import br.com.caelum.cursos.domain.ports.turma.AbrirTurmaUseCase; 11 | import jakarta.persistence.EntityNotFoundException; 12 | import jakarta.validation.Valid; 13 | import lombok.RequiredArgsConstructor; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.transaction.annotation.Transactional; 16 | import org.springframework.web.bind.annotation.PostMapping; 17 | import org.springframework.web.bind.annotation.RequestBody; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RestController; 20 | import org.springframework.web.util.UriComponentsBuilder; 21 | 22 | @RestController 23 | @RequestMapping("turmas") 24 | @RequiredArgsConstructor 25 | public class TurmaController { 26 | 27 | private final AbrirTurmaUseCase useCase; 28 | private final TurmaJpaRepository repository; 29 | private final CursoJpaRepository cursoRepository; 30 | private final SalaJpaRepository salaRepository; 31 | 32 | @PostMapping 33 | @Transactional 34 | public ResponseEntity cadastrar(@RequestBody @Valid DadosParaAbrirTurmaDto dados, UriComponentsBuilder uriBuilder) { 35 | var curso = cursoRepository.findById(dados.idCurso()).orElseThrow(() -> new EntityNotFoundException("Não existe curso cadastrado com o id informado!")); 36 | var sala = salaRepository.findById(dados.idSala()).orElseThrow(() -> new EntityNotFoundException("Não existe sala cadastrada com o id informado!")); 37 | 38 | var dadosParaAbrirTurmaPort = new DadosParaAbrirTurmaPort(dados, curso, sala); 39 | var turma = useCase.execute(dadosParaAbrirTurmaPort); 40 | 41 | // save tera que ser feito aqui e nao no repository, por conta de precisar carregar os relacionamentos 42 | var turmaEntity = new TurmaJpaEntity(turma, curso, sala); 43 | repository.save(turmaEntity); 44 | 45 | var entity = repository.findByCodigo(dados.codigo()); 46 | var uri = uriBuilder.path("turmas/{id}").buildAndExpand(entity.getId()).toUri(); 47 | return ResponseEntity.created(uri).body(new DadosTurmaDto(entity)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/domain/core/curso/CadastrarCursoServiceTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.curso; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.curso.CursoRepository; 5 | import br.com.caelum.cursos.domain.ports.curso.DadosParaCadastrarCurso; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.DisplayName; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | import java.time.Duration; 15 | 16 | import static org.junit.jupiter.api.Assertions.*; 17 | import static org.mockito.BDDMockito.given; 18 | import static org.mockito.Mockito.lenient; 19 | import static org.mockito.Mockito.verify; 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | class CadastrarCursoServiceTest { 23 | 24 | @InjectMocks 25 | private CadastrarCursoService service; 26 | @Mock 27 | private CursoRepository repository; 28 | @Mock 29 | private DadosParaCadastrarCurso dados; 30 | 31 | @BeforeEach 32 | void setup() { 33 | lenient().when(dados.codigo()).thenReturn("cs-xpto"); 34 | lenient().when(dados.nome()).thenReturn("Curso xpto"); 35 | lenient().when(dados.nivel()).thenReturn(Nivel.BASICO); 36 | lenient().when(dados.duracaoEmHoras()).thenReturn(Duration.ofHours(10)); 37 | } 38 | 39 | @Test 40 | @DisplayName("Nao deveria cadastrar curso com codigo ja cadastrado") 41 | void cenario1() { 42 | given(repository.codigoJaCadastrado(dados.codigo())).willReturn(true); 43 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 44 | assertEquals("Cadastro não realizado: Código já utilizado em outro curso!", ex.getMessage()); 45 | } 46 | 47 | @Test 48 | @DisplayName("Nao deveria cadastrar curso com carga horaria menor do que 4") 49 | void cenario2() { 50 | given(dados.duracaoEmHoras()).willReturn(Duration.ofHours(2)); 51 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 52 | assertEquals("Cadastro não realizado: Duração mínima deve ser de 4 horas!", ex.getMessage()); 53 | } 54 | 55 | @Test 56 | @DisplayName("Deveria cadastrar curso com carga horaria igual a 4") 57 | void cenario3() { 58 | given(dados.duracaoEmHoras()).willReturn(Duration.ofHours(4)); 59 | var curso = assertDoesNotThrow(() -> service.execute(dados)); 60 | verify(repository).cadastrar(curso); 61 | } 62 | 63 | @Test 64 | @DisplayName("Deveria cadastrar curso com carga horaria maior do que 4") 65 | void cenario4() { 66 | var curso = assertDoesNotThrow(() -> service.execute(dados)); 67 | verify(repository).cadastrar(curso); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/java/br/com/caelum/cursos/adapters/web/controller/MatriculaController.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.MatriculaJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.repository.EstudanteJpaRepository; 5 | import br.com.caelum.cursos.adapters.database.jpa.repository.MatriculaJpaRepository; 6 | import br.com.caelum.cursos.adapters.database.jpa.repository.TurmaJpaRepository; 7 | import br.com.caelum.cursos.adapters.web.dto.DadosMatriculaDto; 8 | import br.com.caelum.cursos.adapters.web.dto.DadosParaRealizarMatriculaDto; 9 | import br.com.caelum.cursos.adapters.web.dto.DadosParaRealizarMatriculaPort; 10 | import br.com.caelum.cursos.domain.ports.matricula.MatricularEstudanteEmTurmaUseCase; 11 | import jakarta.persistence.EntityNotFoundException; 12 | import jakarta.validation.Valid; 13 | import lombok.RequiredArgsConstructor; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.transaction.annotation.Transactional; 16 | import org.springframework.web.bind.annotation.PostMapping; 17 | import org.springframework.web.bind.annotation.RequestBody; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.bind.annotation.RestController; 20 | import org.springframework.web.util.UriComponentsBuilder; 21 | 22 | @RestController 23 | @RequestMapping("matriculas") 24 | @RequiredArgsConstructor 25 | public class MatriculaController { 26 | 27 | private final MatricularEstudanteEmTurmaUseCase useCase; 28 | private final EstudanteJpaRepository estudanteRepository; 29 | private final TurmaJpaRepository turmaRepository; 30 | private final MatriculaJpaRepository repository; 31 | 32 | @PostMapping 33 | @Transactional 34 | public ResponseEntity cadastrar(@RequestBody @Valid DadosParaRealizarMatriculaDto dados, UriComponentsBuilder uriBuilder) { 35 | var estudante = estudanteRepository.findById(dados.idEstudante()).orElseThrow(() -> new EntityNotFoundException("Não existe estudante cadastrado com o id informado!")); 36 | var turma = turmaRepository.findById(dados.idTurma()).orElseThrow(() -> new EntityNotFoundException("Não existe turma cadastrada com o id informado!")); 37 | 38 | var dadosParaRealizarMatriculaPort = new DadosParaRealizarMatriculaPort(dados, estudante, turma); 39 | var matricula = useCase.execute(dadosParaRealizarMatriculaPort); 40 | 41 | // save tera que ser feito aqui e nao no repository, por conta de precisar carregar os relacionamentos 42 | var matriculaEntity = new MatriculaJpaEntity(matricula.getCodigo(), estudante, turma); 43 | repository.save(matriculaEntity); 44 | 45 | var entity = repository.findByCodigo(matriculaEntity.getCodigo()); 46 | var uri = uriBuilder.path("matriculas/{id}").buildAndExpand(entity.getId()).toUri(); 47 | return ResponseEntity.created(uri).body(new DadosMatriculaDto(entity)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/domain/core/estudante/CadastrarEstudanteServiceTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.estudante; 2 | 3 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 4 | import br.com.caelum.cursos.domain.ports.estudante.DadosParaCadastrarEstudante; 5 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.DisplayName; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | import java.time.LocalDate; 15 | 16 | import static org.junit.jupiter.api.Assertions.*; 17 | import static org.mockito.BDDMockito.given; 18 | import static org.mockito.Mockito.lenient; 19 | import static org.mockito.Mockito.verify; 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | class CadastrarEstudanteServiceTest { 23 | 24 | @InjectMocks 25 | private CadastrarEstudanteService service; 26 | @Mock 27 | private EstudanteRepository repository; 28 | @Mock 29 | private DadosParaCadastrarEstudante dados; 30 | 31 | @BeforeEach 32 | void setup() { 33 | lenient().when(dados.nome()).thenReturn("Estudante"); 34 | lenient().when(dados.cpf()).thenReturn(new Cpf("000.000.00-00")); 35 | lenient().when(dados.email()).thenReturn(new Email("estudante@email.com.br")); 36 | lenient().when(dados.dataDeNascimento()).thenReturn(LocalDate.of(1980, 10, 10)); 37 | lenient().when(dados.celular()).thenReturn(new Telefone("99", "999999999", true)); 38 | } 39 | 40 | @Test 41 | @DisplayName("Nao deveria cadastrar estudante com cpf ou email ja cadastrado") 42 | void cenario1() { 43 | given(repository.CpfOuEmailJaCadastrado(dados.cpf(), dados.email())).willReturn(true); 44 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 45 | assertEquals("Cadastro não realizado: CPF ou email já cadastrado para outro estudante!", ex.getMessage()); 46 | } 47 | 48 | @Test 49 | @DisplayName("Nao deveria cadastrar estudante menor de idade") 50 | void cenario2() { 51 | given(dados.dataDeNascimento()).willReturn(LocalDate.now().minusYears(15)); 52 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 53 | assertEquals("Cadastro não realizado: Idade mínima é de 18 anos!", ex.getMessage()); 54 | } 55 | 56 | @Test 57 | @DisplayName("Deveria cadastrar estudante com 18 anos") 58 | void cenario3() { 59 | given(dados.dataDeNascimento()).willReturn(LocalDate.now().minusYears(18)); 60 | var estudante = assertDoesNotThrow(() -> service.execute(dados)); 61 | verify(repository).cadastrar(estudante); 62 | } 63 | 64 | @Test 65 | @DisplayName("Deveria cadastrar estudante com mais de 18 anos") 66 | void cenario4() { 67 | given(dados.dataDeNascimento()).willReturn(LocalDate.now().minusYears(20)); 68 | var estudante = assertDoesNotThrow(() -> service.execute(dados)); 69 | verify(repository).cadastrar(estudante); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/adapters/web/controller/SalaControllerTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.SalaJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarSalaDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosSalaDto; 6 | import br.com.caelum.cursos.builders.SalaJpaentityBuilder; 7 | import br.com.caelum.cursos.domain.ports.sala.CadastrarSalaUseCase; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.DisplayName; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; 13 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 14 | import org.springframework.boot.test.json.JacksonTester; 15 | import org.springframework.boot.test.mock.mockito.MockBean; 16 | import org.springframework.http.HttpStatus; 17 | import org.springframework.http.MediaType; 18 | import org.springframework.test.web.servlet.MockMvc; 19 | 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | import static org.mockito.BDDMockito.given; 22 | import static org.mockito.Mockito.verify; 23 | import static org.mockito.Mockito.verifyNoInteractions; 24 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 25 | 26 | @AutoConfigureJsonTesters 27 | @WebMvcTest(SalaController.class) 28 | class SalaControllerTest { 29 | 30 | private static final String URI = "/salas"; 31 | 32 | @Autowired 33 | private MockMvc mvc; 34 | 35 | @Autowired 36 | private JacksonTester jsonRequest; 37 | 38 | @Autowired 39 | private JacksonTester jsonResponse; 40 | 41 | @MockBean 42 | private CadastrarSalaUseCase useCase; 43 | 44 | @MockBean 45 | private SalaJpaRepository repository; 46 | 47 | private DadosParaCadastrarSalaDto dadosCadastro; 48 | 49 | @BeforeEach 50 | void setup() { 51 | dadosCadastro = new DadosParaCadastrarSalaDto("Sala 01", 10); 52 | } 53 | 54 | @Test 55 | @DisplayName("Nao deveria cadastrar sala com dados invalidos") 56 | void cenario1() throws Exception { 57 | var response = mvc 58 | .perform(post(URI) 59 | .contentType(MediaType.APPLICATION_JSON) 60 | .content("{}")) 61 | .andReturn().getResponse(); 62 | 63 | assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); 64 | verifyNoInteractions(useCase); 65 | } 66 | 67 | @Test 68 | @DisplayName("Deveria cadastrar sala com dados validos") 69 | void cenario2() throws Exception { 70 | var entity = SalaJpaentityBuilder.build(dadosCadastro); 71 | given(repository.findByNome(dadosCadastro.nome())).willReturn(entity); 72 | 73 | var response = mvc 74 | .perform(post(URI) 75 | .contentType(MediaType.APPLICATION_JSON) 76 | .content(jsonRequest.write(dadosCadastro).getJson())) 77 | .andReturn().getResponse(); 78 | 79 | verify(useCase).execute(dadosCadastro); 80 | assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); 81 | assertThat(response.getContentAsString()).isEqualTo(jsonResponse.write(new DadosSalaDto(entity)).getJson()); 82 | assertThat(response.getHeader("Location")).endsWith(String.format("%s/%d", URI, entity.getId())); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/adapters/web/controller/CursoControllerTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.CursoJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosCursoDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarCursoDto; 6 | import br.com.caelum.cursos.builders.CursoJpaEntityBuilder; 7 | import br.com.caelum.cursos.domain.core.curso.Nivel; 8 | import br.com.caelum.cursos.domain.ports.curso.CadastrarCursoUseCase; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.DisplayName; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; 14 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 15 | import org.springframework.boot.test.json.JacksonTester; 16 | import org.springframework.boot.test.mock.mockito.MockBean; 17 | import org.springframework.http.HttpStatus; 18 | import org.springframework.http.MediaType; 19 | import org.springframework.test.web.servlet.MockMvc; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.mockito.BDDMockito.given; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.verifyNoInteractions; 25 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 26 | 27 | @AutoConfigureJsonTesters 28 | @WebMvcTest(CursoController.class) 29 | class CursoControllerTest { 30 | 31 | private static final String URI = "/cursos"; 32 | 33 | @Autowired 34 | private MockMvc mvc; 35 | 36 | @Autowired 37 | private JacksonTester jsonRequest; 38 | 39 | @Autowired 40 | private JacksonTester jsonResponse; 41 | 42 | @MockBean 43 | private CadastrarCursoUseCase useCase; 44 | 45 | @MockBean 46 | private CursoJpaRepository repository; 47 | 48 | private DadosParaCadastrarCursoDto dadosCadastro; 49 | 50 | @BeforeEach 51 | void setup() { 52 | dadosCadastro = new DadosParaCadastrarCursoDto("xpto", "curso xpto", Nivel.BASICO, 10); 53 | } 54 | 55 | @Test 56 | @DisplayName("Nao deveria cadastrar curso com dados invalidos") 57 | void cenario1() throws Exception { 58 | var response = mvc 59 | .perform(post(URI) 60 | .contentType(MediaType.APPLICATION_JSON) 61 | .content("{}")) 62 | .andReturn().getResponse(); 63 | 64 | assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); 65 | verifyNoInteractions(useCase); 66 | } 67 | 68 | @Test 69 | @DisplayName("Deveria cadastrar curso com dados validos") 70 | void cenario2() throws Exception { 71 | var entity = CursoJpaEntityBuilder.build(50l, dadosCadastro); 72 | given(repository.findByCodigo(dadosCadastro.codigo())).willReturn(entity); 73 | 74 | var response = mvc 75 | .perform(post(URI) 76 | .contentType(MediaType.APPLICATION_JSON) 77 | .content(jsonRequest.write(dadosCadastro).getJson())) 78 | .andReturn().getResponse(); 79 | 80 | verify(useCase).execute(dadosCadastro); 81 | assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); 82 | assertThat(response.getContentAsString()).isEqualTo(jsonResponse.write(new DadosCursoDto(entity)).getJson()); 83 | assertThat(response.getHeader("Location")).endsWith(String.format("%s/%d", URI, entity.getId())); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/adapters/web/controller/EstudanteControllerTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.repository.EstudanteJpaRepository; 4 | import br.com.caelum.cursos.adapters.web.dto.DadosEstudanteDto; 5 | import br.com.caelum.cursos.adapters.web.dto.DadosParaCadastrarEstudanteDto; 6 | import br.com.caelum.cursos.adapters.web.dto.DadosTelefoneDto; 7 | import br.com.caelum.cursos.builders.EstudanteJpaEntityBuilder; 8 | import br.com.caelum.cursos.domain.ports.estudante.CadastrarEstudanteUseCase; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.DisplayName; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; 14 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 15 | import org.springframework.boot.test.json.JacksonTester; 16 | import org.springframework.boot.test.mock.mockito.MockBean; 17 | import org.springframework.http.HttpStatus; 18 | import org.springframework.http.MediaType; 19 | import org.springframework.test.web.servlet.MockMvc; 20 | 21 | import java.time.LocalDate; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | import static org.mockito.BDDMockito.given; 25 | import static org.mockito.Mockito.verify; 26 | import static org.mockito.Mockito.verifyNoInteractions; 27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 28 | 29 | @AutoConfigureJsonTesters 30 | @WebMvcTest(EstudanteController.class) 31 | class EstudanteControllerTest { 32 | 33 | private static final String URI = "/estudantes"; 34 | 35 | @Autowired 36 | private MockMvc mvc; 37 | 38 | @Autowired 39 | private JacksonTester jsonRequest; 40 | 41 | @Autowired 42 | private JacksonTester jsonResponse; 43 | 44 | @MockBean 45 | private CadastrarEstudanteUseCase useCase; 46 | 47 | @MockBean 48 | private EstudanteJpaRepository repository; 49 | 50 | private DadosParaCadastrarEstudanteDto dadosCadastro; 51 | 52 | @BeforeEach 53 | void setup() { 54 | dadosCadastro = new DadosParaCadastrarEstudanteDto( 55 | "Fulano da Silva", 56 | "000.000.000-00", 57 | LocalDate.of(1980, 10, 10), 58 | "fulano@email.com", 59 | new DadosTelefoneDto("00", "000000000", true)); 60 | } 61 | 62 | @Test 63 | @DisplayName("Nao deveria cadastrar estudante com dados invalidos") 64 | void cenario1() throws Exception { 65 | var response = mvc 66 | .perform(post(URI) 67 | .contentType(MediaType.APPLICATION_JSON) 68 | .content("{}")) 69 | .andReturn().getResponse(); 70 | 71 | assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); 72 | verifyNoInteractions(useCase); 73 | } 74 | 75 | @Test 76 | @DisplayName("Deveria cadastrar estudante com dados validos") 77 | void cenario2() throws Exception { 78 | var entity = EstudanteJpaEntityBuilder.build(50l, dadosCadastro); 79 | given(repository.findByCpf(dadosCadastro.cpfAsString())).willReturn(entity); 80 | 81 | var response = mvc 82 | .perform(post(URI) 83 | .contentType(MediaType.APPLICATION_JSON) 84 | .content(jsonRequest.write(dadosCadastro).getJson())) 85 | .andReturn().getResponse(); 86 | 87 | verify(useCase).execute(dadosCadastro); 88 | assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); 89 | assertThat(response.getContentAsString()).isEqualTo(jsonResponse.write(new DadosEstudanteDto(entity)).getJson()); 90 | assertThat(response.getHeader("Location")).endsWith(String.format("%s/%d", URI, entity.getId())); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/domain/core/turma/AbrirTurmaServiceTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.turma; 2 | 3 | import br.com.caelum.cursos.builders.CursoBuilder; 4 | import br.com.caelum.cursos.builders.SalaBuilder; 5 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 6 | import br.com.caelum.cursos.domain.ports.turma.DadosParaAbrirTurma; 7 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.DisplayName; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.mockito.InjectMocks; 13 | import org.mockito.Mock; 14 | import org.mockito.junit.jupiter.MockitoExtension; 15 | 16 | import java.time.LocalDate; 17 | 18 | import static org.junit.jupiter.api.Assertions.*; 19 | import static org.mockito.BDDMockito.given; 20 | import static org.mockito.Mockito.lenient; 21 | import static org.mockito.Mockito.verify; 22 | 23 | @ExtendWith(MockitoExtension.class) 24 | class AbrirTurmaServiceTest { 25 | 26 | @InjectMocks 27 | private AbrirTurmaService service; 28 | @Mock 29 | private TurmaRepository repository; 30 | @Mock 31 | private DadosParaAbrirTurma dados; 32 | 33 | @BeforeEach 34 | void setup() { 35 | lenient().when(dados.codigo()).thenReturn("XPTO-001"); 36 | lenient().when(dados.dataInicio()).thenReturn(LocalDate.now()); 37 | lenient().when(dados.dataFim()).thenReturn(LocalDate.now().plusDays(5)); 38 | lenient().when(dados.turno()).thenReturn(Turno.INTEGRAL); 39 | lenient().when(dados.sala()).thenReturn(SalaBuilder.build("Sala 01", 10)); 40 | lenient().when(dados.curso()).thenReturn(CursoBuilder.build("xpto", "curso xpto", 40)); 41 | } 42 | 43 | @Test 44 | @DisplayName("Nao deveria abrir turma com codigo ja cadastrado") 45 | void cenario1() { 46 | given(repository.codigoJaCadastrado(dados.codigo())).willReturn(true); 47 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 48 | assertEquals("Cadastro não realizado: Código já utilizado em outra turma!", ex.getMessage()); 49 | } 50 | 51 | @Test 52 | @DisplayName("Nao deveria abrir turma com data de inicio menor do que hoje") 53 | void cenario2() { 54 | given(dados.dataInicio()).willReturn(LocalDate.now().minusDays(1)); 55 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 56 | assertEquals("Cadastro não realizado: Data de início não pode ser anterior a hoje!", ex.getMessage()); 57 | } 58 | 59 | @Test 60 | @DisplayName("Nao deveria abrir turma com data de fim menor do que data de inicio") 61 | void cenario3() { 62 | given(dados.dataFim()).willReturn(LocalDate.now().minusDays(1)); 63 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 64 | assertEquals("Cadastro não realizado: Data fim deve ser posterior a data de início!", ex.getMessage()); 65 | } 66 | 67 | @Test 68 | @DisplayName("Nao deveria abrir turma com sala ja ocupada por outra turma") 69 | void cenario4() { 70 | given(repository.salaJaOcupadaNoPeriodo(dados.sala(), dados.dataInicio(), dados.dataFim())).willReturn(true); 71 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 72 | assertEquals("Cadastro não realizado: Sala já utilizada por outra turma no mesmo período!", ex.getMessage()); 73 | } 74 | 75 | @Test 76 | @DisplayName("Nao deveria abrir turma com curso que ja atingiu limite de turmas em andamento") 77 | void cenario5() { 78 | given(repository.quantidadeDeTurmasEmAbertoDoCurso(dados.curso())).willReturn(4); 79 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 80 | assertEquals("Cadastro não realizado: Curso atingiu limite de 4 turmas em andamento!", ex.getMessage()); 81 | } 82 | 83 | @Test 84 | @DisplayName("Deveria abrir turma com sala desocupada") 85 | void cenario6() { 86 | given(repository.salaJaOcupadaNoPeriodo(dados.sala(), dados.dataInicio(), dados.dataFim())).willReturn(false); 87 | var turma = assertDoesNotThrow(() -> service.execute(dados)); 88 | verify(repository).abrir(turma); 89 | } 90 | 91 | @Test 92 | @DisplayName("Deveria abrir turma no limite de turmas") 93 | void cenario7() { 94 | given(repository.quantidadeDeTurmasEmAbertoDoCurso(dados.curso())).willReturn(3); 95 | var turma = assertDoesNotThrow(() -> service.execute(dados)); 96 | verify(repository).abrir(turma); 97 | } 98 | 99 | @Test 100 | @DisplayName("Deveria abrir turma abaixo do limite de turmas") 101 | void cenario8() { 102 | given(repository.quantidadeDeTurmasEmAbertoDoCurso(dados.curso())).willReturn(2); 103 | var turma = assertDoesNotThrow(() -> service.execute(dados)); 104 | verify(repository).abrir(turma); 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/adapters/web/controller/TurmaControllerTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.CursoJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.SalaJpaEntity; 5 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 6 | import br.com.caelum.cursos.adapters.database.jpa.repository.CursoJpaRepository; 7 | import br.com.caelum.cursos.adapters.database.jpa.repository.SalaJpaRepository; 8 | import br.com.caelum.cursos.adapters.database.jpa.repository.TurmaJpaRepository; 9 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaDto; 10 | import br.com.caelum.cursos.adapters.web.dto.DadosParaAbrirTurmaPort; 11 | import br.com.caelum.cursos.adapters.web.dto.DadosTurmaDto; 12 | import br.com.caelum.cursos.builders.CursoJpaEntityBuilder; 13 | import br.com.caelum.cursos.builders.SalaJpaentityBuilder; 14 | import br.com.caelum.cursos.domain.core.turma.Turma; 15 | import br.com.caelum.cursos.domain.core.turma.Turno; 16 | import br.com.caelum.cursos.domain.ports.turma.AbrirTurmaUseCase; 17 | import org.junit.jupiter.api.BeforeEach; 18 | import org.junit.jupiter.api.DisplayName; 19 | import org.junit.jupiter.api.Test; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; 22 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 23 | import org.springframework.boot.test.json.JacksonTester; 24 | import org.springframework.boot.test.mock.mockito.MockBean; 25 | import org.springframework.http.HttpStatus; 26 | import org.springframework.http.MediaType; 27 | import org.springframework.test.web.servlet.MockMvc; 28 | 29 | import java.time.LocalDate; 30 | import java.util.Optional; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | import static org.mockito.BDDMockito.given; 34 | import static org.mockito.Mockito.verify; 35 | import static org.mockito.Mockito.verifyNoInteractions; 36 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 37 | 38 | @AutoConfigureJsonTesters 39 | @WebMvcTest(TurmaController.class) 40 | class TurmaControllerTest { 41 | 42 | private static final String URI = "/turmas"; 43 | 44 | @Autowired 45 | private MockMvc mvc; 46 | 47 | @Autowired 48 | private JacksonTester jsonRequest; 49 | 50 | @Autowired 51 | private JacksonTester jsonResponse; 52 | 53 | @MockBean 54 | private AbrirTurmaUseCase useCase; 55 | 56 | @MockBean 57 | private TurmaJpaRepository repository; 58 | 59 | @MockBean 60 | private CursoJpaRepository cursoRepository; 61 | 62 | @MockBean 63 | private SalaJpaRepository salaRepository; 64 | 65 | private CursoJpaEntity cursoJpaEntity; 66 | private SalaJpaEntity salaJpaEntity; 67 | private DadosParaAbrirTurmaDto dadosCadastro; 68 | 69 | @BeforeEach 70 | void setup() { 71 | cursoJpaEntity = CursoJpaEntityBuilder.build(); 72 | salaJpaEntity = SalaJpaentityBuilder.build(); 73 | dadosCadastro = new DadosParaAbrirTurmaDto( 74 | "T-001", 75 | cursoJpaEntity.getId(), 76 | salaJpaEntity.getId(), 77 | Turno.INTEGRAL, 78 | LocalDate.now().plusDays(1), 79 | LocalDate.now().plusDays(6)); 80 | 81 | } 82 | 83 | @Test 84 | @DisplayName("Nao deveria abrir turma com dados invalidos") 85 | void cenario1() throws Exception { 86 | var response = mvc 87 | .perform(post(URI) 88 | .contentType(MediaType.APPLICATION_JSON) 89 | .content("{}")) 90 | .andReturn().getResponse(); 91 | 92 | assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); 93 | verifyNoInteractions(useCase); 94 | verifyNoInteractions(repository); 95 | } 96 | 97 | @Test 98 | @DisplayName("Deveria abrir turma com dados validos") 99 | void cenario2() throws Exception { 100 | given(cursoRepository.findById(cursoJpaEntity.getId())).willReturn(Optional.ofNullable(cursoJpaEntity)); 101 | given(salaRepository.findById(salaJpaEntity.getId())).willReturn(Optional.ofNullable(salaJpaEntity)); 102 | 103 | var dadosParaAbrirTurmaPort = new DadosParaAbrirTurmaPort(dadosCadastro, cursoJpaEntity, salaJpaEntity); 104 | var turma = new Turma(dadosParaAbrirTurmaPort); 105 | var entity = new TurmaJpaEntity(50l, turma.getCodigo(), cursoJpaEntity, salaJpaEntity, turma.getTurno(), turma.getDataInicio(), turma.getDataFim()); 106 | 107 | given(useCase.execute(dadosParaAbrirTurmaPort)).willReturn(turma); 108 | given(repository.findByCodigo(turma.getCodigo())).willReturn(entity); 109 | 110 | var response = mvc 111 | .perform(post(URI) 112 | .contentType(MediaType.APPLICATION_JSON) 113 | .content(jsonRequest.write(dadosCadastro).getJson())) 114 | .andReturn().getResponse(); 115 | 116 | 117 | verify(useCase).execute(dadosParaAbrirTurmaPort); 118 | verify(repository).save(new TurmaJpaEntity(turma, cursoJpaEntity, salaJpaEntity)); 119 | assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); 120 | assertThat(response.getContentAsString()).isEqualTo(jsonResponse.write(new DadosTurmaDto(entity)).getJson()); 121 | assertThat(response.getHeader("Location")).endsWith(String.format("%s/%d", URI, entity.getId())); 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/adapters/web/controller/MatriculaControllerTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.adapters.web.controller; 2 | 3 | import br.com.caelum.cursos.adapters.database.jpa.entity.EstudanteJpaEntity; 4 | import br.com.caelum.cursos.adapters.database.jpa.entity.MatriculaJpaEntity; 5 | import br.com.caelum.cursos.adapters.database.jpa.entity.TurmaJpaEntity; 6 | import br.com.caelum.cursos.adapters.database.jpa.repository.EstudanteJpaRepository; 7 | import br.com.caelum.cursos.adapters.database.jpa.repository.MatriculaJpaRepository; 8 | import br.com.caelum.cursos.adapters.database.jpa.repository.TurmaJpaRepository; 9 | import br.com.caelum.cursos.adapters.web.dto.DadosMatriculaDto; 10 | import br.com.caelum.cursos.adapters.web.dto.DadosParaRealizarMatriculaDto; 11 | import br.com.caelum.cursos.adapters.web.dto.DadosParaRealizarMatriculaPort; 12 | import br.com.caelum.cursos.builders.EstudanteJpaEntityBuilder; 13 | import br.com.caelum.cursos.builders.TurmaJpaEntityBuilder; 14 | import br.com.caelum.cursos.domain.core.estudante.Cpf; 15 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 16 | import br.com.caelum.cursos.domain.core.matricula.Matricula; 17 | import br.com.caelum.cursos.domain.core.turma.Turma; 18 | import br.com.caelum.cursos.domain.ports.matricula.MatricularEstudanteEmTurmaUseCase; 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.DisplayName; 21 | import org.junit.jupiter.api.Test; 22 | import org.mockito.Mock; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters; 25 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 26 | import org.springframework.boot.test.json.JacksonTester; 27 | import org.springframework.boot.test.mock.mockito.MockBean; 28 | import org.springframework.http.HttpStatus; 29 | import org.springframework.http.MediaType; 30 | import org.springframework.test.web.servlet.MockMvc; 31 | 32 | import java.util.Optional; 33 | 34 | import static org.assertj.core.api.Assertions.assertThat; 35 | import static org.mockito.BDDMockito.given; 36 | import static org.mockito.Mockito.*; 37 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 38 | 39 | @AutoConfigureJsonTesters 40 | @WebMvcTest(MatriculaController.class) 41 | class MatriculaControllerTest { 42 | 43 | private static final String URI = "/matriculas"; 44 | 45 | @Autowired 46 | private MockMvc mvc; 47 | 48 | @Autowired 49 | private JacksonTester jsonRequest; 50 | 51 | @Autowired 52 | private JacksonTester jsonResponse; 53 | 54 | @MockBean 55 | private MatricularEstudanteEmTurmaUseCase useCase; 56 | 57 | @MockBean 58 | private MatriculaJpaRepository repository; 59 | 60 | @MockBean 61 | private EstudanteJpaRepository estudanteRepository; 62 | 63 | @MockBean 64 | private TurmaJpaRepository turmaRepository; 65 | 66 | @Mock 67 | private Turma turma; 68 | 69 | @Mock 70 | private Estudante estudante; 71 | 72 | private EstudanteJpaEntity estudanteJpaEntity; 73 | private TurmaJpaEntity turmaJpaEntity; 74 | private DadosParaRealizarMatriculaDto dadosCadastro; 75 | 76 | @BeforeEach 77 | void setup() { 78 | estudanteJpaEntity = EstudanteJpaEntityBuilder.build(); 79 | turmaJpaEntity = TurmaJpaEntityBuilder.build(); 80 | dadosCadastro = new DadosParaRealizarMatriculaDto(estudanteJpaEntity.getId(), turmaJpaEntity.getId()); 81 | 82 | lenient().when(turma.getCodigo()).thenReturn(turmaJpaEntity.getCodigo()); 83 | lenient().when(estudante.getCpf()).thenReturn(new Cpf(estudanteJpaEntity.getCpf())); 84 | } 85 | 86 | @Test 87 | @DisplayName("Nao deveria realizar matricula com dados invalidos") 88 | void cenario1() throws Exception { 89 | var response = mvc 90 | .perform(post(URI) 91 | .contentType(MediaType.APPLICATION_JSON) 92 | .content("{}")) 93 | .andReturn().getResponse(); 94 | 95 | assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); 96 | verifyNoInteractions(useCase); 97 | verifyNoInteractions(repository); 98 | } 99 | 100 | @Test 101 | @DisplayName("Deveria realizar matricula com dados validos") 102 | void cenario2() throws Exception { 103 | given(estudanteRepository.findById(estudanteJpaEntity.getId())).willReturn(Optional.ofNullable(estudanteJpaEntity)); 104 | given(turmaRepository.findById(turmaJpaEntity.getId())).willReturn(Optional.ofNullable(turmaJpaEntity)); 105 | 106 | var dadosParaRealizarMatriculaPort = new DadosParaRealizarMatriculaPort(dadosCadastro, estudanteJpaEntity, turmaJpaEntity); 107 | var matricula = new Matricula(turma, estudante); 108 | var entity = new MatriculaJpaEntity(50l, matricula.getCodigo(), estudanteJpaEntity, turmaJpaEntity); 109 | 110 | given(useCase.execute(dadosParaRealizarMatriculaPort)).willReturn(matricula); 111 | given(repository.findByCodigo(matricula.getCodigo())).willReturn(entity); 112 | 113 | var response = mvc 114 | .perform(post(URI) 115 | .contentType(MediaType.APPLICATION_JSON) 116 | .content(jsonRequest.write(dadosCadastro).getJson())) 117 | .andReturn().getResponse(); 118 | 119 | verify(useCase).execute(dadosParaRealizarMatriculaPort); 120 | verify(repository).save(new MatriculaJpaEntity(matricula.getCodigo(), estudanteJpaEntity, turmaJpaEntity)); 121 | assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); 122 | assertThat(response.getContentAsString()).isEqualTo(jsonResponse.write(new DadosMatriculaDto(entity)).getJson()); 123 | assertThat(response.getHeader("Location")).endsWith(String.format("%s/%d", URI, entity.getId())); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /src/test/java/br/com/caelum/cursos/domain/core/matricula/MatricularEstudanteEmTurmaServiceTest.java: -------------------------------------------------------------------------------- 1 | package br.com.caelum.cursos.domain.core.matricula; 2 | 3 | import br.com.caelum.cursos.builders.CursoBuilder; 4 | import br.com.caelum.cursos.builders.EstudanteBuilder; 5 | import br.com.caelum.cursos.builders.SalaBuilder; 6 | import br.com.caelum.cursos.builders.TurmaBuilder; 7 | import br.com.caelum.cursos.domain.core.RegraDeNegocioException; 8 | import br.com.caelum.cursos.domain.core.estudante.Estudante; 9 | import br.com.caelum.cursos.domain.core.estudante.Telefone; 10 | import br.com.caelum.cursos.domain.core.turma.Turma; 11 | import br.com.caelum.cursos.domain.core.turma.Turno; 12 | import br.com.caelum.cursos.domain.ports.estudante.EstudanteRepository; 13 | import br.com.caelum.cursos.domain.ports.matricula.DadosParaRealizarMatricula; 14 | import br.com.caelum.cursos.domain.ports.matricula.MatriculaRepository; 15 | import br.com.caelum.cursos.domain.ports.turma.TurmaRepository; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.DisplayName; 18 | import org.junit.jupiter.api.Test; 19 | import org.junit.jupiter.api.extension.ExtendWith; 20 | import org.mockito.InjectMocks; 21 | import org.mockito.Mock; 22 | import org.mockito.junit.jupiter.MockitoExtension; 23 | 24 | import java.time.LocalDate; 25 | 26 | import static org.junit.jupiter.api.Assertions.*; 27 | import static org.mockito.BDDMockito.given; 28 | import static org.mockito.Mockito.lenient; 29 | import static org.mockito.Mockito.verify; 30 | 31 | @ExtendWith(MockitoExtension.class) 32 | class MatricularEstudanteEmTurmaServiceTest { 33 | 34 | @InjectMocks 35 | private MatricularEstudanteEmTurmaService service; 36 | @Mock 37 | private TurmaRepository turmaRepository; 38 | @Mock 39 | private EstudanteRepository estudanteRepository; 40 | @Mock 41 | private MatriculaRepository matriculaRepository; 42 | @Mock 43 | private DadosParaRealizarMatricula dados; 44 | private Estudante estudante; 45 | private Turma turma; 46 | 47 | @BeforeEach 48 | void setup() { 49 | this.estudante = EstudanteBuilder.build( 50 | "Estudante", 51 | "000.000.000-00", 52 | LocalDate.of(2000, 1, 1), 53 | "estudante@email.com", 54 | new Telefone( 55 | "99", 56 | "999999999", 57 | true)); 58 | 59 | this.turma = TurmaBuilder.build( 60 | "T-0001", 61 | LocalDate.now(), 62 | LocalDate.now().plusDays(5), 63 | Turno.INTEGRAL, 64 | CursoBuilder.build( 65 | "C-X", 66 | "Curso X", 67 | 40), 68 | SalaBuilder.build( 69 | "Sala 01", 70 | 5)); 71 | 72 | lenient().when(dados.codigoTurma()).thenReturn(turma.getCodigo()); 73 | lenient().when(dados.cpfEstudante()).thenReturn(estudante.getCpf()); 74 | lenient().when(turmaRepository.buscarPorCodigo(dados.codigoTurma())).thenReturn(turma); 75 | lenient().when(estudanteRepository.buscarPorCpf(dados.cpfEstudante())).thenReturn(estudante); 76 | } 77 | 78 | @Test 79 | @DisplayName("Nao deveria matricular estudante em turma lotada") 80 | void cenario1() { 81 | var outraTurma = TurmaBuilder.buildFrom(turma, SalaBuilder.build("Sala 2", 1)); 82 | outraTurma.registrarMatricula(new Matricula(outraTurma, estudante)); 83 | given(dados.codigoTurma()).willReturn(outraTurma.getCodigo()); 84 | given(turmaRepository.buscarPorCodigo(dados.codigoTurma())).willReturn(outraTurma); 85 | 86 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 87 | assertEquals("Matricula não realizada: turma está lotada!", ex.getMessage()); 88 | } 89 | 90 | @Test 91 | @DisplayName("Nao deveria matricular estudante em turma com matriculas encerradas") 92 | void cenario2() { 93 | var outraTurma = TurmaBuilder.buildFrom(turma, turma.getDataInicio().minusDays(2), turma.getDataFim()); 94 | 95 | given(dados.codigoTurma()).willReturn(outraTurma.getCodigo()); 96 | given(turmaRepository.buscarPorCodigo(dados.codigoTurma())).willReturn(outraTurma); 97 | 98 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 99 | assertEquals("Matricula não realizada: turma com período de matriculas encerado!", ex.getMessage()); 100 | } 101 | 102 | @Test 103 | @DisplayName("Nao deveria matricular estudante que ja esta matriculado na turma") 104 | void cenario3() { 105 | estudante.registrarMatricula(new Matricula(turma, estudante)); 106 | 107 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 108 | assertEquals("Matricula não realizada: estudante já possui matricula para esta turma!", ex.getMessage()); 109 | } 110 | 111 | @Test 112 | @DisplayName("Nao deveria matricular estudante que ja atingiu limite de turmas simultaneas") 113 | void cenario4() { 114 | var turma2 = TurmaBuilder.build( 115 | "T-0002", 116 | LocalDate.now(), 117 | LocalDate.now().plusDays(5), 118 | Turno.INTEGRAL, 119 | CursoBuilder.build( 120 | "C-X", 121 | "Curso X", 122 | 40), 123 | SalaBuilder.build( 124 | "Sala 01", 125 | 2)); 126 | 127 | var turma3 = TurmaBuilder.build( 128 | "T-0003", 129 | LocalDate.now(), 130 | LocalDate.now().plusDays(5), 131 | Turno.INTEGRAL, 132 | CursoBuilder.build( 133 | "C-X", 134 | "Curso X", 135 | 40), 136 | SalaBuilder.build( 137 | "Sala 01", 138 | 2)); 139 | 140 | estudante.registrarMatricula(new Matricula(turma, estudante)); 141 | estudante.registrarMatricula(new Matricula(turma2, estudante)); 142 | 143 | given(dados.codigoTurma()).willReturn(turma3.getCodigo()); 144 | given(turmaRepository.buscarPorCodigo(dados.codigoTurma())).willReturn(turma3); 145 | 146 | var ex = assertThrows(RegraDeNegocioException.class, () -> service.execute(dados)); 147 | assertEquals("Matricula não realizada: estudante com limite de turmas em andamento!", ex.getMessage()); 148 | } 149 | 150 | @Test 151 | @DisplayName("Deveria matricular estudante com uma ou nenhuma matricula") 152 | void cenario5() { 153 | var matricula1 = assertDoesNotThrow(() -> service.execute(dados)); 154 | verify(matriculaRepository).registrar(matricula1); 155 | 156 | var turma2 = TurmaBuilder.build( 157 | "T-0002", 158 | LocalDate.now(), 159 | LocalDate.now().plusDays(5), 160 | Turno.INTEGRAL, 161 | CursoBuilder.build( 162 | "C-X", 163 | "Curso X", 164 | 40), 165 | SalaBuilder.build( 166 | "Sala 01", 167 | 2)); 168 | 169 | given(dados.codigoTurma()).willReturn(turma2.getCodigo()); 170 | given(turmaRepository.buscarPorCodigo(dados.codigoTurma())).willReturn(turma2); 171 | var matricula2 = assertDoesNotThrow(() -> service.execute(dados)); 172 | verify(matriculaRepository).registrar(matricula2); 173 | } 174 | 175 | @Test 176 | @DisplayName("Deveria aceitar matriculas no dia anterior ao inicio da turma") 177 | void cenario6() { 178 | var outraTurma = TurmaBuilder.buildFrom(turma, LocalDate.now().plusDays(1), turma.getDataFim()); 179 | 180 | given(dados.codigoTurma()).willReturn(outraTurma.getCodigo()); 181 | given(turmaRepository.buscarPorCodigo(outraTurma.getCodigo())).willReturn(outraTurma); 182 | 183 | var matricula = assertDoesNotThrow(() -> service.execute(dados)); 184 | verify(matriculaRepository).registrar(matricula); 185 | } 186 | 187 | @Test 188 | @DisplayName("Deveria aceitar matriculas no dia do inicio da turma") 189 | void cenario7() { 190 | var matricula = assertDoesNotThrow(() -> service.execute(dados)); 191 | verify(matriculaRepository).registrar(matricula); 192 | } 193 | 194 | @Test 195 | @DisplayName("Deveria aceitar matriculas uma dia apos o inicio da turma") 196 | void cenario8() { 197 | var outraTurma = TurmaBuilder.buildFrom(turma, LocalDate.now().minusDays(1), turma.getDataFim()); 198 | 199 | given(dados.codigoTurma()).willReturn(outraTurma.getCodigo()); 200 | given(turmaRepository.buscarPorCodigo(outraTurma.getCodigo())).willReturn(outraTurma); 201 | 202 | var matricula = assertDoesNotThrow(() -> service.execute(dados)); 203 | verify(matriculaRepository).registrar(matricula); 204 | } 205 | 206 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")" 103 | fi 104 | javaHome="$(dirname "\"$javaExecutable\"")" 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | --------------------------------------------------------------------------------