diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4406c10
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+.git/
+.gitignore
+*.md
+Dockerfile
+.dockerignore
diff --git a/.github/workflows/hello.yml b/.github/workflows/hello.yml
new file mode 100644
index 0000000..7e9bfbf
--- /dev/null
+++ b/.github/workflows/hello.yml
@@ -0,0 +1,10 @@
+name: hello.yml
+on:
+ push:
+
+jobs:
+ hello-cd-ci:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Print Hello CI/CD
+ run: echo "Hello CI/CD"
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c7f4360
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,33 @@
+#FROM amazoncorretto:21-alpine3.22
+#
+#WORKDIR /app
+#
+#COPY target/*.jar app.jar
+#
+#EXPOSE 8080
+#
+#ENTRYPOINT ["java","-jar","app.jar"]
+
+#===============================Multi-stage Dockerfile=================================
+
+#Build
+FROM maven:3.9-amazoncorretto-21-alpine AS builder
+
+WORKDIR /app
+
+COPY pom.xml .
+
+COPY src ./src
+
+RUN mvn clean test package -DskipTests=false
+
+#Runtime
+FROM amazoncorretto:21-alpine3.22 AS runtime
+
+WORKDIR /app
+
+COPY --from=builder /app/target/*.jar app.jar
+
+EXPOSE 8080
+
+ENTRYPOINT ["java", "-jar", "app.jar"]
diff --git a/compose.yaml b/compose.yaml
deleted file mode 100644
index db92ca3..0000000
--- a/compose.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-services:
- postgres:
- image: 'postgres:latest'
- environment:
- - 'POSTGRES_DB=mydatabase'
- - 'POSTGRES_PASSWORD=secret'
- - 'POSTGRES_USER=myuser'
- ports:
- - '5432'
- redis:
- image: 'redis:latest'
- ports:
- - '6379'
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..a61bd76
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,83 @@
+services:
+ postgres:
+ image: 'postgres:latest'
+ container_name: postgres_db
+ environment:
+ - 'POSTGRES_DB=mydatabase'
+ - 'POSTGRES_PASSWORD=secret'
+ - 'POSTGRES_USER=myuser'
+ ports:
+ - '5432:5432'
+ volumes:
+ - postgres_data:/var/lib/postgresql
+ - shared_volume:/shared
+ networks:
+ - app_network
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U myuser -d mydatabase"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 60s
+
+ redis:
+ image: 'redis:latest'
+ container_name: redis_cache
+ ports:
+ - '6379:6379'
+ volumes:
+ - redis_data:/data
+ - shared_volume:/shared
+ networks:
+ - app_network
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 30s
+
+ devcalc-api:
+ image: 'shyuu7/devcalc-api:latest'
+ container_name: devcalc_app
+ ports:
+ - '8080:8080'
+ depends_on:
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ volumes:
+ - shared_volume:/app/shared
+ networks:
+ - app_network
+ environment:
+ - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/mydatabase
+ - SPRING_DATASOURCE_USERNAME=myuser
+ - SPRING_DATASOURCE_PASSWORD=secret
+ - SPRING_REDIS_HOST=redis
+ - SPRING_REDIS_PORT=6379
+
+ test_container:
+ image: busybox
+ container_name: connectivity_test
+ command: sleep 3600
+ volumes:
+ - shared_volume:/shared
+ networks:
+ - app_network
+ depends_on:
+ - postgres
+ - redis
+
+volumes:
+ postgres_data:
+ driver: local
+ redis_data:
+ driver: local
+ shared_volume:
+ driver: local
+
+networks:
+ app_network:
+ driver: bridge
diff --git a/evidencias/CalculatorServiceTest.png b/evidencias/CalculatorServiceTest.png
new file mode 100644
index 0000000..bb673ad
Binary files /dev/null and b/evidencias/CalculatorServiceTest.png differ
diff --git a/evidencias/aplicacao rodando pelo docker.png b/evidencias/aplicacao rodando pelo docker.png
new file mode 100644
index 0000000..9373116
Binary files /dev/null and b/evidencias/aplicacao rodando pelo docker.png differ
diff --git a/evidencias/apontando tags 1.0.0 para latest.png b/evidencias/apontando tags 1.0.0 para latest.png
new file mode 100644
index 0000000..3531fc6
Binary files /dev/null and b/evidencias/apontando tags 1.0.0 para latest.png differ
diff --git a/evidencias/calculator controller.png b/evidencias/calculator controller.png
new file mode 100644
index 0000000..74c8dd8
Binary files /dev/null and b/evidencias/calculator controller.png differ
diff --git a/evidencias/calculator service.png b/evidencias/calculator service.png
new file mode 100644
index 0000000..4c02847
Binary files /dev/null and b/evidencias/calculator service.png differ
diff --git a/evidencias/conectividade com api.png b/evidencias/conectividade com api.png
new file mode 100644
index 0000000..dbd8d4e
Binary files /dev/null and b/evidencias/conectividade com api.png differ
diff --git a/evidencias/conectividade com redis e postgres.png b/evidencias/conectividade com redis e postgres.png
new file mode 100644
index 0000000..ca46551
Binary files /dev/null and b/evidencias/conectividade com redis e postgres.png differ
diff --git a/evidencias/conteineres rodando apos healthcheck.png b/evidencias/conteineres rodando apos healthcheck.png
new file mode 100644
index 0000000..f460067
Binary files /dev/null and b/evidencias/conteineres rodando apos healthcheck.png differ
diff --git a/evidencias/criacao da imagem docker.png b/evidencias/criacao da imagem docker.png
new file mode 100644
index 0000000..72702e7
Binary files /dev/null and b/evidencias/criacao da imagem docker.png differ
diff --git "a/evidencias/criando arquivo em volume compartilhado e checando exist\303\252ncia nos outros conteineres.png" "b/evidencias/criando arquivo em volume compartilhado e checando exist\303\252ncia nos outros conteineres.png"
new file mode 100644
index 0000000..1c3fe6d
Binary files /dev/null and "b/evidencias/criando arquivo em volume compartilhado e checando exist\303\252ncia nos outros conteineres.png" differ
diff --git a/evidencias/docker build multistage apos correcao de teste.png b/evidencias/docker build multistage apos correcao de teste.png
new file mode 100644
index 0000000..d1eff07
Binary files /dev/null and b/evidencias/docker build multistage apos correcao de teste.png differ
diff --git a/evidencias/erro de build - teste falhando.png b/evidencias/erro de build - teste falhando.png
new file mode 100644
index 0000000..7cca6d0
Binary files /dev/null and b/evidencias/erro de build - teste falhando.png differ
diff --git a/evidencias/git commit.png b/evidencias/git commit.png
new file mode 100644
index 0000000..f22d5f1
Binary files /dev/null and b/evidencias/git commit.png differ
diff --git a/evidencias/git push.png b/evidencias/git push.png
new file mode 100644
index 0000000..d9da627
Binary files /dev/null and b/evidencias/git push.png differ
diff --git a/evidencias/healthcheck postgres.png b/evidencias/healthcheck postgres.png
new file mode 100644
index 0000000..6ae91e9
Binary files /dev/null and b/evidencias/healthcheck postgres.png differ
diff --git a/evidencias/healthcheck redis.png b/evidencias/healthcheck redis.png
new file mode 100644
index 0000000..ab66137
Binary files /dev/null and b/evidencias/healthcheck redis.png differ
diff --git "a/evidencias/imagens j\303\241 no dockerhub.png" "b/evidencias/imagens j\303\241 no dockerhub.png"
new file mode 100644
index 0000000..d5e83f9
Binary files /dev/null and "b/evidencias/imagens j\303\241 no dockerhub.png" differ
diff --git a/evidencias/subindo imagens para o dockerhub.png b/evidencias/subindo imagens para o dockerhub.png
new file mode 100644
index 0000000..5773055
Binary files /dev/null and b/evidencias/subindo imagens para o dockerhub.png differ
diff --git a/pom.xml b/pom.xml
index c88d09b..df190aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 4.0.0-SNAPSHOT
+ 3.4.0
com
@@ -13,22 +13,11 @@
0.0.1-SNAPSHOT
DevCalc
DevCalc
-
-
-
-
-
-
-
-
-
-
-
-
-
+
21
+
org.springframework.boot
@@ -38,12 +27,11 @@
org.springframework.boot
spring-boot-starter-thymeleaf
-
- org.springframework.boot
- spring-boot-starter-webmvc
-
-
-
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
org.springframework.boot
spring-boot-docker-compose
runtime
@@ -54,65 +42,28 @@
postgresql
runtime
-
- org.projectlombok
- lombok
- true
-
org.springframework.boot
spring-boot-starter-test
test
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.2.0
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- false
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/snapshot
-
- false
-
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
diff --git a/readme.md b/readme.md
index dde4f35..6d7eb0a 100644
--- a/readme.md
+++ b/readme.md
@@ -61,11 +61,65 @@ O projeto utiliza **Apache Maven** como ferramenta de build e gerenciamento de d
```bash
mvn clean
```
-4. **Acesse a API**
+
+### Opção 2: usando Docker
+
+Pré-requisitos:
+- Docker instalado
+- Docker Compose instalado
+
+Executar apenas a API:
+1. Pull da imagem do Docker Hub:
+ ```bash
+ docker pull shyuu7/devcalc:latest
+ ```
+2. Executar o container:
+ ```bash
+ docker run -d -p 8080:8080 shyuu7/devcalc:latest
+ ```
+
+Executar ambiente completo com Docker Compose:
+1. Clone o repositório:
+ ```bash
+ git clone https://github.com/Shyuu7/devcalc-api.git
+ cd devcalc-api
+ ```
+2. Iniciar os serviços:
+ ```bash
+ docker-compose up -d
+ ```
+3. Verificar os containers em execução:
+ ```bash
+ docker ps
+ ```
+4. Parar os serviços:
+ ```bash
+ docker-compose down
+ ```
+5. Parar e remover todos os containers, redes e volumes:
+ ```bash
+ docker-compose down -v
+ ```
+
+
+### **Acesse a API**
+
+Independente do método de execução escolhido, a API estará disponível em:
+
+URL: http://localhost:8080
+
+Swagger UI: http://localhost:8080/swagger-ui/index.html
- A API estará disponível em `http://localhost:8080`.
+### **Teste os endpoints**
-5. **Teste os endpoints**
+Você pode testar os endpoints usando:
+- Swagger UI - Interface web disponível na página principal
+- Curl - Comandos HTTP via terminal
+- Postman - Cliente REST gráfico
+- Container de teste - Para verificar conectividade interna
-Você pode testar os endpoints usando ferramentas como Postman ou cURL.
\ No newline at end of file
+Exemplo de comando curl para somar dois números:
+```bash
+curl -X GET "http://localhost:8080/api/calc/sum?a=5&b=3" -H "accept: application/json"
+```
\ No newline at end of file
diff --git a/src/main/java/com/devcalc/config/SwaggerConfig.java b/src/main/java/com/devcalc/config/SwaggerConfig.java
new file mode 100644
index 0000000..e1007b8
--- /dev/null
+++ b/src/main/java/com/devcalc/config/SwaggerConfig.java
@@ -0,0 +1,23 @@
+package com.devcalc.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.Contact;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SwaggerConfig {
+
+ @Bean
+ public OpenAPI customOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("DevCalc API")
+ .version("1.0.0")
+ .description("API REST para operações matemáticas básicas")
+ .contact(new Contact()
+ .name("DevCalc Team")
+ .url("https://github.com/Shyuu7/devcalc-api")));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/devcalc/controller/CalculatorController.java b/src/main/java/com/devcalc/controller/CalculatorController.java
new file mode 100644
index 0000000..06c5f1a
--- /dev/null
+++ b/src/main/java/com/devcalc/controller/CalculatorController.java
@@ -0,0 +1,64 @@
+package com.devcalc.controller;
+
+import com.devcalc.model.CalculatorModel;
+import com.devcalc.model.ErrorResponse;
+import com.devcalc.service.CalculatorService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@Tag(name = "Calculadora", description = "Operações matemáticas básicas")
+public class CalculatorController {
+
+ private final CalculatorService calculatorService;
+
+ public CalculatorController(CalculatorService calculatorService) {
+ this.calculatorService = calculatorService;
+ }
+
+ @GetMapping("/add")
+ @Operation(summary = "Adição", description = "Realiza a soma de dois números")
+ public ResponseEntity add(
+ @Parameter(description = "Primeiro número") @RequestParam double a,
+ @Parameter(description = "Segundo número") @RequestParam double b) {
+ CalculatorModel result = calculatorService.adicao(a, b);
+ return ResponseEntity.ok(result.getResultado());
+ }
+
+ @GetMapping("/subtract")
+ @Operation(summary = "Subtração", description = "Realiza a subtração de dois números")
+ public ResponseEntity subtract(
+ @Parameter(description = "Primeiro número") @RequestParam double a,
+ @Parameter(description = "Segundo número") @RequestParam double b) {
+ CalculatorModel result = calculatorService.subtracao(a, b);
+ return ResponseEntity.ok(result.getResultado());
+ }
+
+ @GetMapping("/multiply")
+ @Operation(summary = "Multiplicação", description = "Realiza a multiplicação de dois números")
+ public ResponseEntity multiply(
+ @Parameter(description = "Primeiro número") @RequestParam double a,
+ @Parameter(description = "Segundo número") @RequestParam double b) {
+ CalculatorModel result = calculatorService.multiplicacao(a, b);
+ return ResponseEntity.ok(result.getResultado());
+ }
+
+ @GetMapping("/divide")
+ @Operation(summary = "Divisão", description = "Realiza a divisão de dois números")
+ public ResponseEntity> divide(
+ @Parameter(description = "Dividendo") @RequestParam double a,
+ @Parameter(description = "Divisor") @RequestParam double b) {
+ try {
+ CalculatorModel result = calculatorService.divisao(a, b);
+ return ResponseEntity.ok(result.getResultado());
+ } catch (ArithmeticException e) {
+ ErrorResponse error = new ErrorResponse("DIVISION_BY_ZERO", e.getMessage());
+ return ResponseEntity.badRequest().body(error);
+ }
+ }
+}
diff --git a/src/main/java/com/devcalc/model/CalculatorModel.java b/src/main/java/com/devcalc/model/CalculatorModel.java
new file mode 100644
index 0000000..953efb5
--- /dev/null
+++ b/src/main/java/com/devcalc/model/CalculatorModel.java
@@ -0,0 +1,32 @@
+package com.devcalc.model;
+
+public class CalculatorModel {
+ private final double numeroA;
+ private final double numeroB;
+ private final double result;
+ private final String operation;
+
+ public CalculatorModel(double numeroA, double numeroB, double result, String operation) {
+ this.numeroA = numeroA;
+ this.numeroB = numeroB;
+ this.result = result;
+ this.operation = operation;
+ }
+
+ public double getNumeroA() {
+ return numeroA;
+ }
+
+ public double getNumeroB() {
+ return numeroB;
+ }
+
+ public double getResultado() {
+ return result;
+ }
+
+ public String getOperacao() {
+ return operation;
+ }
+
+}
diff --git a/src/main/java/com/devcalc/model/ErrorResponse.java b/src/main/java/com/devcalc/model/ErrorResponse.java
new file mode 100644
index 0000000..3570da5
--- /dev/null
+++ b/src/main/java/com/devcalc/model/ErrorResponse.java
@@ -0,0 +1,27 @@
+package com.devcalc.model;
+
+public class ErrorResponse {
+ private String code;
+ private String message;
+
+ public ErrorResponse(String code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/com/devcalc/service/CalculatorService.java b/src/main/java/com/devcalc/service/CalculatorService.java
new file mode 100644
index 0000000..b3d7bb4
--- /dev/null
+++ b/src/main/java/com/devcalc/service/CalculatorService.java
@@ -0,0 +1,31 @@
+package com.devcalc.service;
+
+import com.devcalc.model.CalculatorModel;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CalculatorService {
+
+ public CalculatorModel adicao(double a, double b) {
+ double result = a + b;
+ return new CalculatorModel(a, b, result, "adição");
+ }
+
+ public CalculatorModel subtracao(double a, double b) {
+ double result = a - b;
+ return new CalculatorModel(a, b, result, "subtração");
+ }
+
+ public CalculatorModel multiplicacao(double a, double b) {
+ double result = a * b;
+ return new CalculatorModel(a, b, result, "multiplicação");
+ }
+
+ public CalculatorModel divisao(double a, double b) {
+ if (b == 0) {
+ throw new ArithmeticException("Divisão por zero não é permitida");
+ }
+ double result = a / b;
+ return new CalculatorModel(a, b, result, "divisão");
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index dd0a88d..cdcedc3 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,5 @@
spring.application.name=DevCalc
+spring.docker.compose.enabled=false
+springdoc.swagger-ui.path=/
+springdoc.api-docs.path=/api-docs
+
diff --git a/src/test/java/com/devcalc/service/CalculatorServiceTest.java b/src/test/java/com/devcalc/service/CalculatorServiceTest.java
new file mode 100644
index 0000000..3089443
--- /dev/null
+++ b/src/test/java/com/devcalc/service/CalculatorServiceTest.java
@@ -0,0 +1,89 @@
+package com.devcalc.service;
+
+import com.devcalc.model.CalculatorModel;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+class CalculatorServiceTest {
+
+ private CalculatorService calculatorService;
+
+ @BeforeEach
+ void setUp() {
+ calculatorService = new CalculatorService();
+ }
+
+ @Test
+ void adicao() {
+ double a = 50;
+ double b = 5;
+ CalculatorModel result = calculatorService.adicao(a, b);
+ assertEquals(50, result.getNumeroA());
+ assertEquals(5, result.getNumeroB());
+ assertEquals(55, result.getResultado());
+ assertEquals("adição", result.getOperacao());
+ }
+
+ @Test
+ void subtracao() {
+ double a = 75;
+ double b = 5;
+ CalculatorModel result = calculatorService.subtracao(a, b);
+ assertEquals(75, result.getNumeroA());
+ assertEquals(5, result.getNumeroB());
+ assertEquals(70, result.getResultado());
+ assertEquals("subtração", result.getOperacao());
+ }
+
+ @Test
+ void multiplicacao() {
+ double a = 10;
+ double b = 5;
+ CalculatorModel result = calculatorService.multiplicacao(a, b);
+ assertEquals(10, result.getNumeroA());
+ assertEquals(5, result.getNumeroB());
+ assertEquals(50, result.getResultado());
+ assertEquals("multiplicação", result.getOperacao());
+ }
+
+ @Test
+ void divisao() {
+ double a = 10;
+ double b = 5;
+ CalculatorModel result = calculatorService.divisao(a, b);
+ assertEquals(10, result.getNumeroA());
+ assertEquals(5, result.getNumeroB());
+ assertEquals(2, result.getResultado());
+ assertEquals("divisão", result.getOperacao());
+ }
+
+ @Test
+ void divisaoPorZero() {
+ double a = 10;
+ double b = 0;
+ ArithmeticException exception = assertThrows(
+ ArithmeticException.class,
+ () -> calculatorService.divisao(a, b)
+ );
+ assertEquals("Divisão por zero não é permitida", exception.getMessage());
+ }
+
+ @Test
+ void adicaoNumerosNegativos() {
+ double a = -5;
+ double b = -3;
+ CalculatorModel result = calculatorService.adicao(a, b);
+ assertEquals(-8, result.getResultado());
+ assertEquals("adição", result.getOperacao());
+ }
+
+ @Test
+ void multiplicacaoPorZero() {
+ double a = 10;
+ double b = 0;
+ CalculatorModel result = calculatorService.multiplicacao(a, b);
+ assertEquals(0, result.getResultado());
+ assertEquals("multiplicação", result.getOperacao());
+ }
+}