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..150f4f791 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,89 @@ +pipeline { + agent any + stages { + stage ('Build Backend') { + steps { + bat 'mvn clean package -DskipTests=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=DeploBack -Dsonar.host.url=http://127.0.0.1:9000 -Dsonar.login=ee946d1fb6cbc1a9cf240c8114506cbff3a08a41 -Dsonar.java.binaries=target -Dsonar.coverage.exclusions=**/.mvn/**,**/src/test/**,**/model/**,**Applicantion.java" + } + } + } + stage ('Quality Gate') { + options { + timeout(time: 5, unit: 'MINUTES') + retry(2) + } + steps { + sleep(10){ + waitForQualityGate(abortPipeline: true) + } + } + } + stage ('Deploy Backend') { + steps { + deploy adapters: [tomcat8(credentialsId: 'TomcatLogin2', path: '', url: 'http://127.0.0.1:8001/')], contextPath: 'tasks-backend', war: 'target\\tasks-backend.war' + } + } + stage ('API Test') { + steps { + dir('api-test') { + git credentialsId: 'github_login', url: 'https://github.com/gugafer/tasks-api-test' + bat 'mvn test' + } + } + } + stage ('Deply Frontend') { + steps { + dir('frontend') { + git credentialsId: 'github_login', url: 'https://github.com/gugafer/tasks-frontend' + bat 'mvn clean package' + deploy adapters: [tomcat8(credentialsId: 'TomcatLogin2', path: '', url: 'http://127.0.0.1:8001/')], contextPath: 'tasks', war: 'target\\tasks.war' + + } + } + } + stage ('Functional Test') { + steps { + dir('functional-test') { + git credentialsId: 'github_login', url: 'https://github.com/gugafer/tasks-functional-test' + bat 'mvn test' + } + } + } + stage ('Deploy Prod') { + steps { + bat 'docker-compose build' + bat 'docker-compose up -d' + } + } + stage ('Health check') { + steps { + sleep(20) + dir('functional-test') { + bat 'mvn verify' + } + } + } + } + post{ + always{ + junit allowEmptyResults: true, testResults: 'target/surefire-reports/*.xml, functional-test/target/surefire-reports/*.xml, functional-test/target/failsafe-reports/*.xml' + archiveArtifacts artifacts: 'target/tasks-backend.war, frontend/target/tasks.war', followSymlinks: false, onlyIfSuccessful: true + } + } +} + + 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..7328eab96 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,12 @@ 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..f9ffcdd96 --- /dev/null +++ b/src/test/java/br/ce/wcaquino/taskbackend/controller/TaskControllerTest.java @@ -0,0 +1,76 @@ +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 setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void naoDeveSalvarTarefaSemDescricao() { + Task todo = new Task(); + todo.setDueDate(LocalDate.now()); + try { + controller.save(todo); + Assert.fail("Nao 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("Nao 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(2010, 01, 01)); + try { + controller.save(todo); + Assert.fail("Nao deveria chegar nesse ponto!"); + } catch (ValidationException e) { + Assert.assertEquals("Due date must not be in past", e.getMessage()); + } + } + + @Test + public void naoDeveSalvarTarefaComSucesso() 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..4e28aa150 --- /dev/null +++ b/src/test/java/br/ce/wcaquino/taskbackend/utils/DateUtilsTest.java @@ -0,0 +1,31 @@ +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); + DateUtils.isEqualOrFutureDate(date); + Assert.assertTrue(DateUtils.isEqualOrFutureDate(date)); + } + + @Test + public void deveRetornarFalseParaDatasFuturas() { + LocalDate date = LocalDate.of(2010, 01, 01); + DateUtils.isEqualOrFutureDate(date); + Assert.assertFalse(DateUtils.isEqualOrFutureDate(date)); + } + + @Test + public void deveRetornarTrueParaDatasAtual() { + LocalDate date = LocalDate.now(); + DateUtils.isEqualOrFutureDate(date); + Assert.assertTrue(DateUtils.isEqualOrFutureDate(date)); + } + +}