diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..247111453 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM tomcat:8.5.50-jdk8-openjdk + +ARG WAR_FILE +ARG CONTEXT + +COPY ${WAR_FILE} /usr/local/tomcat/webapps/${CONTEXT}.war \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..f4852b70c --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,91 @@ +pipeline { + agent any + stages { + stage ('Build Backend'){ + steps { + bat 'mvn clean package -DskipTest=true' + } + } + stage ('Unit Test'){ + steps { + bat 'mvn test' + } + } + stage ('Sonar Analysis'){ + environment{ + scannerHome = tool 'SONAR_SCANNER' + } + steps { + withSonarQubeEnv('SONAR_LOCAL'){ + bat "${scannerHome}/bin/sonar-scanner -e -Dsonar.projectKey=DeployBack -Dsonar.host.url=http://localhost:9000 -Dsonar.login=0f445d2a90d58958a91fdbe10852696ba2ba2a0b -Dsonar.java.binaries=target -Dsonar.coverage.exclusions=**/.mvn/**,**/src/test/**,**/model/**,**Application.java" + } + } + } + /*stage ('Quality Gate'){ + steps { + sleep(30) + timeout(time: 1, unit: 'MINUTES'){ + waitForQualityGate abortPipeline: true + } + } + }*/ + stage ('Deploy Backend'){ + steps { + deploy adapters: [tomcat8(credentialsId: 'TomcatLogin', path: '', url: 'http://localhost:8001')], contextPath: 'tasks-backend', war: 'target/tasks-backend.war' + } + } + stage ('API Test'){ + steps { + dir('api-test') { + git branch: 'main', credentialsId: 'github_login', url: 'https://github.com/rodrigofa1305/tasks-api-test' + bat 'mvn test' + } + } + } + stage ('Deploy Frontend'){ + steps { + dir('frontend') { + git branch: 'master', credentialsId: 'github_login', url: 'https://github.com/rodrigofa1305/tasks-frontend' + bat 'mvn clean package' + deploy adapters: [tomcat8(credentialsId: 'TomcatLogin', path: '', url: 'http://localhost:8001')], contextPath: 'tasks', war: 'target/tasks.war' + } + } + } + stage ('Funcional Test'){ + steps { + dir('funcional-test') { + git branch: 'main', credentialsId: 'github_login', url: 'https://github.com/rodrigofa1305/tasks-funcional-tests' + bat 'mvn test' + } + } + } + stage ('Deploy Producao'){ + steps { + dir('funcional-test') { + bat 'docker-compose build' + bat 'docker-compose up -d' + } + } + } + stage ('HealthCheck'){ + steps { + sleep(270) + dir('funcional-test') { + bat 'mvn verify -Dskip.surefire.tests' + } + } + } + } + post{ + always{ + junit allowEmptyResults: true, testResults: 'target/surefire-reports/*.xml, api-test/target/surefire-reports/*.xml, funcional-test/target/surefire-reports/*.xml, funcional-test/target/failsafe-reports/*.xml' + archiveArtifacts artifacts: 'target/tasks-backend.war, frontend/target/tasks.war', onlyIfSuccessful: true + } + unsuccessful{ + emailext attachLog: true, body: 'See the attached log below', subject: 'Build $BUILD_NUMBER has failed', to: 'rodrigofa1305+jenkins@gmail.com' + } + fixed{ + emailext attachLog: true, body: 'See the attached log below', subject: 'Build is Fine!', to: 'rodrigofa1305+jenkins@gmail.com' + } + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..6bf30e27e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +version: "3" +services: + db: + container_name: pg-prod + image: postgres:9.6 + networks: + - prod_net_back + environment: + - POSTGRES_PASSWORD=passwd + - POSTGRES_DB=tasks + volumes: + - prod_postgresql:/var/lib/postgresql + - prod_postgresql_data:/var/lib/postgresql/data + + backend: + container_name: backend-prod + image: back_prod:build_${BUILD_NUMBER} + build: + context: . + args: + - WAR_FILE=target/tasks-backend.war + - CONTEXT=tasks-backend + networks: + - prod_net_back + - prod_net_front + #ports: + # - 9998:8080 + environment: + - DATABASE_HOST=db + - DATABASE_PORT=5432 + - DATABASE_USER=postgres + - DATABASE_PASSWD=passwd + - DATABASE_UPDATE=none + depends_on: + - db + + frontend: + container_name: frontend-prod + image: front_prod:build_${BUILD_NUMBER} + build: + context: . + args: + - WAR_FILE=frontend/target/tasks.war + - CONTEXT=tasks + networks: + - prod_net_front + ports: + - 9999:8080 + environment: + - BACKEND_HOST=backend + - BACKEND_PORT=8080 + - APP_VERSION=build_${BUILD_NUMBER} + #depends_on: + # - backend + +networks: + prod_net_front: + prod_net_back: + +volumes: + prod_postgresql: + prod_postgresql_data: \ No newline at end of file diff --git a/src/main/java/br/ce/wcaquino/taskbackend/controller/TaskController.java b/src/main/java/br/ce/wcaquino/taskbackend/controller/TaskController.java index 2b9281372..7521b78fd 100644 --- a/src/main/java/br/ce/wcaquino/taskbackend/controller/TaskController.java +++ b/src/main/java/br/ce/wcaquino/taskbackend/controller/TaskController.java @@ -5,10 +5,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import br.ce.wcaquino.taskbackend.model.Task; @@ -42,4 +45,10 @@ public ResponseEntity save(@RequestBody Task todo) throws ValidationExcept Task saved = todoRepo.save(todo); return new ResponseEntity(saved, HttpStatus.CREATED); } + + @DeleteMapping(value = "/{id}") + @ResponseStatus(code = HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long id){ + todoRepo.deleteById(id); + } } diff --git a/src/test/java/br/ce/wcaquino/taskbackend/controller/TaskControllerTest.java b/src/test/java/br/ce/wcaquino/taskbackend/controller/TaskControllerTest.java new file mode 100644 index 000000000..d41a09499 --- /dev/null +++ b/src/test/java/br/ce/wcaquino/taskbackend/controller/TaskControllerTest.java @@ -0,0 +1,75 @@ +package br.ce.wcaquino.taskbackend.controller; + +import java.time.LocalDate; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import br.ce.wcaquino.taskbackend.model.Task; +import br.ce.wcaquino.taskbackend.repo.TaskRepo; +import br.ce.wcaquino.taskbackend.utils.ValidationException; + +public class TaskControllerTest { + + @Mock + private TaskRepo taskRepo; + + @InjectMocks + private TaskController controller; + + @Before + public void SetupContext() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void naoDeveSalvarTarefaSemDescricao() { + Task todo = new Task(); + todo.setDueDate(LocalDate.now()); + try { + controller.save(todo); + Assert.fail("Não Deveria Chegar Nesse Ponto!!"); + } catch (ValidationException e) { + Assert.assertEquals("Fill the task description", e.getMessage()); + } + } + + @Test + public void naoDeveSalvarTarefaSemData() { + Task todo = new Task(); + todo.setTask("Descricao"); + try { + controller.save(todo); + Assert.fail("Não Deveria Chegar Nesse Ponto!!"); + } catch (ValidationException e) { + Assert.assertEquals("Fill the due date", e.getMessage()); + } + } + + @Test + public void naoDeveSalvarTarefaComDataPassada() { + Task todo = new Task(); + todo.setTask("Descricao"); + todo.setDueDate(LocalDate.of(2021, 03, 30)); + try { + controller.save(todo); + Assert.fail("Não Deveria Chegar Nesse Ponto!!"); + } catch (ValidationException e) { + Assert.assertEquals("Due date must not be in past", e.getMessage()); + } + } + + @Test + public void DeveSalvarTarefaComSucesso() throws ValidationException { + Task todo = new Task(); + todo.setTask("Descricao"); + todo.setDueDate(LocalDate.now()); + controller.save(todo); + Mockito.verify(taskRepo).save(todo); + } +} diff --git a/src/test/java/br/ce/wcaquino/taskbackend/utils/DateUtilsTest.java b/src/test/java/br/ce/wcaquino/taskbackend/utils/DateUtilsTest.java new file mode 100644 index 000000000..806129c25 --- /dev/null +++ b/src/test/java/br/ce/wcaquino/taskbackend/utils/DateUtilsTest.java @@ -0,0 +1,27 @@ +package br.ce.wcaquino.taskbackend.utils; + +import java.time.LocalDate; + +import org.junit.Assert; +import org.junit.Test; + +public class DateUtilsTest { + + @Test + public void deveRetornarTrueParaDatasFuturas() { + LocalDate date = LocalDate.of(2030, 01, 01); + Assert.assertTrue(DateUtils.isEqualOrFutureDate(date)); + } + + @Test + public void deveRetornarFalseParaDatasFuturas() { + LocalDate date = LocalDate.of(2021, 03, 30); + Assert.assertFalse(DateUtils.isEqualOrFutureDate(date)); + } + + @Test + public void deveRetornartrueParaDataAtual() { + LocalDate date = LocalDate.now(); + Assert.assertTrue(DateUtils.isEqualOrFutureDate(date)); + } +}