From 6d3a52ef67b97d8fab2437b2bef1d635988d5fee Mon Sep 17 00:00:00 2001 From: carlosbastoslaet Date: Sun, 4 Jan 2026 10:12:49 -0300 Subject: [PATCH] fix: att code dynamodb --- .github/workflows/deploy.yml | 6 - app/pom.xml | 5 + .../tech/buildrun/lambda/HandlerClient.java | 154 ++++++++++-------- lambda.tf | 46 ++++-- locals.tf | 3 - vars.tf | 17 -- 6 files changed, 124 insertions(+), 107 deletions(-) delete mode 100644 locals.tf diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f7f2d6d..0df2d3d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -63,9 +63,6 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} - TF_VAR_db_user: ${{ secrets.DB_USER }} - TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} - TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }} aws-region: us-east-1 TF_VAR_lambda_jar_path: app/target/lambda-identification-client.jar @@ -76,8 +73,5 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} - TF_VAR_db_user: ${{ secrets.DB_USER }} - TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} - TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }} aws-region: us-east-1 TF_VAR_lambda_jar_path: app/target/lambda-identification-client.jar diff --git a/app/pom.xml b/app/pom.xml index fc1df82..c305023 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -65,6 +65,11 @@ 0.12.3 runtime + + software.amazon.awssdk + dynamodb + 2.25.10 + diff --git a/app/src/main/java/tech/buildrun/lambda/HandlerClient.java b/app/src/main/java/tech/buildrun/lambda/HandlerClient.java index 0fb9695..0875f25 100644 --- a/app/src/main/java/tech/buildrun/lambda/HandlerClient.java +++ b/app/src/main/java/tech/buildrun/lambda/HandlerClient.java @@ -5,17 +5,18 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.*; -import java.sql.*; -import java.util.Map; +import java.util.*; -public class HandlerClient implements RequestHandler { +public class HandlerClient implements + RequestHandler { - private static final String DB_URL = System.getenv("DB_URL"); - private static final String DB_USER = System.getenv("DB_USER"); - private static final String DB_PASSWORD = System.getenv("DB_PASSWORD"); + private static final String TABLE_NAME = "tc-identification-table"; private final ObjectMapper mapper = new ObjectMapper(); + private final DynamoDbClient dynamo = DynamoDbClient.create(); @Override public APIGatewayProxyResponseEvent handleRequest( @@ -29,24 +30,18 @@ public APIGatewayProxyResponseEvent handleRequest( context.getLogger().log("METHOD=" + method); context.getLogger().log("PATH=" + path); - Class.forName("org.postgresql.Driver"); - - try (Connection conn = - DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) { - - // POST /clientes - if ("POST".equals(method) && "/clientes".equals(path)) { - return criarCliente(request, conn); - } - - // GET /clientes/{document} - if ("GET".equals(method) && path.startsWith("/clientes/")) { - return consultarCliente(path, conn); - } + // POST /clientes + if ("POST".equals(method) && "/clientes".equals(path)) { + return criarCliente(request); + } - return response(404, Map.of("message", "Endpoint não encontrado")); + // GET /clientes/{document} + if ("GET".equals(method) && path.startsWith("/clientes/")) { + return consultarCliente(path); } + return response(404, Map.of("message", "Endpoint não encontrado")); + } catch (Exception e) { e.printStackTrace(); return response(500, Map.of("message", "Erro interno")); @@ -56,14 +51,14 @@ public APIGatewayProxyResponseEvent handleRequest( // ===================== CRIAR CLIENTE ===================== private APIGatewayProxyResponseEvent criarCliente( - APIGatewayProxyRequestEvent request, - Connection conn) throws Exception { + APIGatewayProxyRequestEvent request) throws Exception { if (request.getBody() == null || request.getBody().isBlank()) { return response(400, Map.of("message", "Body obrigatório")); } - Map body = mapper.readValue(request.getBody(), Map.class); + Map body = + mapper.readValue(request.getBody(), Map.class); String document = body.get("document"); String name = body.get("name"); @@ -75,63 +70,78 @@ private APIGatewayProxyResponseEvent criarCliente( )); } - String sql = """ - INSERT INTO tb_cliente (nr_documento, nm_cliente, ds_email) - VALUES (?, ?, ?) - """; - - try (PreparedStatement ps = conn.prepareStatement(sql)) { - ps.setString(1, document); - ps.setString(2, name); - ps.setString(3, email); - ps.executeUpdate(); - - return response(201, Map.of( - "message", "Cliente criado com sucesso", - "document", document - )); - - } catch (SQLException e) { - if ("23505".equals(e.getSQLState())) { - return response(409, Map.of("message", "Cliente já existe")); - } - throw e; + // 🔍 Verifica se já existe (regra do SQL 23505) + if (clienteExistePorDocumento(document)) { + return response(409, Map.of("message", "Cliente já existe")); } + + Map item = new HashMap<>(); + item.put("id", AttributeValue.builder() + .s(UUID.randomUUID().toString()).build()); + item.put("nr_documento", AttributeValue.builder().s(document).build()); + item.put("nm_cliente", AttributeValue.builder().s(name).build()); + item.put("ds_email", AttributeValue.builder().s(email).build()); + + dynamo.putItem(PutItemRequest.builder() + .tableName(TABLE_NAME) + .item(item) + .build()); + + return response(201, Map.of( + "message", "Cliente criado com sucesso", + "document", document + )); } // ===================== CONSULTAR CLIENTE ===================== - private APIGatewayProxyResponseEvent consultarCliente( - String path, - Connection conn) throws Exception { + private APIGatewayProxyResponseEvent consultarCliente(String path) + throws Exception { - // /clientes/123456 String document = path.substring("/clientes/".length()); - String sql = """ - SELECT id, nr_documento, nm_cliente, ds_email - FROM tb_cliente - WHERE nr_documento = ? - """; - - try (PreparedStatement ps = conn.prepareStatement(sql)) { - ps.setString(1, document); - ResultSet rs = ps.executeQuery(); - - if (rs.next()) { - return response(200, Map.of( - "id", rs.getLong("id"), - "document", rs.getString("nr_documento"), - "name", rs.getString("nm_cliente"), - "email", rs.getString("ds_email") - )); - } + QueryRequest query = QueryRequest.builder() + .tableName(TABLE_NAME) + .indexName("DocumentoIndex") + .keyConditionExpression("nr_documento = :doc") + .expressionAttributeValues(Map.of( + ":doc", AttributeValue.builder().s(document).build() + )) + .limit(1) + .build(); + QueryResponse response = dynamo.query(query); + + if (response.count() == 0) { return response(404, Map.of("message", "Cliente não encontrado")); } + + Map item = response.items().get(0); + + return response(200, Map.of( + "id", item.get("id").s(), + "document", item.get("nr_documento").s(), + "name", item.get("nm_cliente").s(), + "email", item.get("ds_email").s() + )); } - // ===================== RESPONSE ===================== + // ===================== UTIL ===================== + + private boolean clienteExistePorDocumento(String document) { + + QueryRequest query = QueryRequest.builder() + .tableName(TABLE_NAME) + .indexName("DocumentoIndex") + .keyConditionExpression("nr_documento = :doc") + .expressionAttributeValues(Map.of( + ":doc", AttributeValue.builder().s(document).build() + )) + .limit(1) + .build(); + + return dynamo.query(query).count() > 0; + } private APIGatewayProxyResponseEvent response(int status, Object body) { try { @@ -140,8 +150,10 @@ private APIGatewayProxyResponseEvent response(int status, Object body) { .withHeaders(Map.of( "Content-Type", "application/json", "Access-Control-Allow-Origin", "*", - "Access-Control-Allow-Headers", "Content-Type,Authorization", - "Access-Control-Allow-Methods", "GET,POST,OPTIONS" + "Access-Control-Allow-Headers", + "Content-Type,Authorization", + "Access-Control-Allow-Methods", + "GET,POST,OPTIONS" )) .withBody(mapper.writeValueAsString(body)); } catch (Exception e) { @@ -150,4 +162,4 @@ private APIGatewayProxyResponseEvent response(int status, Object body) { .withBody("{\"message\":\"Erro ao serializar resposta\"}"); } } -} \ No newline at end of file +} diff --git a/lambda.tf b/lambda.tf index 129eabc..69579d6 100644 --- a/lambda.tf +++ b/lambda.tf @@ -2,6 +2,36 @@ data "aws_iam_role" "lambda_exec_role" { name = "tc-infra-id-lambda-exec-role" } + +data "aws_dynamodb_table" "identification_table" { + name = "tc-identification-table" +} + +resource "aws_iam_role_policy" "lambda_dynamodb_policy" { + role = data.aws_iam_role.lambda_exec_role.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = [ + "dynamodb:PutItem", + "dynamodb:Query", + "dynamodb:GetItem" + ] + Resource = [ + data.aws_dynamodb_table.identification_table.arn, + "${data.aws_dynamodb_table.identification_table.arn}/index/*" + ] + }] + }) +} + +resource "aws_iam_role_policy_attachment" "lambda_logs" { + role = data.aws_iam_role.lambda_exec_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} + data "aws_security_group" "id_lambda" { name = "tc-id-lambda-sg" vpc_id = data.aws_vpc.tc_lambda_vpc.id @@ -9,25 +39,21 @@ data "aws_security_group" "id_lambda" { resource "aws_lambda_function" "id_lambda" { function_name = "lambda-identification-client" - depends_on = [] - role = data.aws_iam_role.lambda_exec_role.arn - handler = "tech.buildrun.lambda.HandlerClient::handleRequest" - runtime = "java17" - timeout = 10 + role = data.aws_iam_role.lambda_exec_role.arn + handler = "tech.buildrun.lambda.HandlerClient::handleRequest" + runtime = "java17" + timeout = 30 - # Usa o caminho passado via variável filename = var.lambda_jar_path source_code_hash = filebase64sha256(var.lambda_jar_path) environment { variables = { - DB_URL = local.jdbc_url - DB_USER = var.db_user - DB_PASSWORD = var.db_password - JWT_SECRET = var.jwt_secret + TABLE_NAME = "tc-identification-table" } } + vpc_config { subnet_ids = data.aws_subnets.tc_lambda_subnets.ids security_group_ids = [data.aws_security_group.id_lambda.id] diff --git a/locals.tf b/locals.tf deleted file mode 100644 index f271e51..0000000 --- a/locals.tf +++ /dev/null @@ -1,3 +0,0 @@ -locals { - jdbc_url = "jdbc:postgresql://${data.aws_db_instance.db_instance.address}:${data.aws_db_instance.db_instance.port}/${data.aws_db_instance.db_instance.db_name}" -} diff --git a/vars.tf b/vars.tf index 285199c..306b400 100644 --- a/vars.tf +++ b/vars.tf @@ -11,23 +11,6 @@ variable "tags" { } } -# Banco de dados -variable "db_user" { - description = "Usuário do banco de dados" - sensitive = true -} - -variable "db_password" { - description = "Senha do banco de dados" - sensitive = true -} - -variable "jwt_secret" { - description = "Chave secreta para geração de tokens JWT" - type = string - sensitive = true -} - variable "lambda_jar_path" { description = "Caminho do fat JAR da Lambda" type = string