Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,17 @@ node_modules
coverage/
.vscode

dist/
dist/

.idea

# Ignorar archivos de estado de Terraform
.terraform.tfstate
.terraform.tfstate.backup

# Ignorar archivos de variables sensibles
*.tfvars
*.auto.tfvars

# Ignorar el archivo de bloqueo de Terraform
.terraform.lock.hcl
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Definir las variables
TERRAFORM_DIR = ./iac/terraform
TERRAFORM = terraform
AWS_CLI = aws
LOAD_SCRIPT = ./iac/terraform/modules/dynamodb/load_data.sh

all: build init apply

build:
@echo "build projects..."
cd ./microservices && npm run deploy

init:
@echo "Initializing Terraform..."
cd $(TERRAFORM_DIR) && $(TERRAFORM) init

apply:
@echo "Applying Terraform configuration..."
cd $(TERRAFORM_DIR) && $(TERRAFORM) apply -auto-approve

clean:
@echo "Destroying Terraform-managed infrastructure..."
cd $(TERRAFORM_DIR) && $(TERRAFORM) destroy -auto-approve
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,78 @@ La tabla debe de tener el siguiente contenido:
## Send us your challenge
Cuando termines el reto, luego de forkear el repositorio, debes crear un pull request to our repository indicando en la descripción de este tu nombre y correo.

### Tiempo de resolución: 3 días
### Tiempo de resolución: 3 días

# Datos del Proyecto

## Arquitectura
Este proyecto sigue los principios de Arquitectura Limpia para asegurar una estructura modular y escalable. La arquitectura se basa en separar las responsabilidades en diferentes capas, cada una con sus propias responsabilidades, para mejorar la mantenibilidad y escalabilidad del sistema.

## Endpoints de prueba:
- Get-Transactions
```json
Curl:
curl --location 'https://9yp31ebze1.execute-api.us-east-1.amazonaws.com/dev/v1/transactions?transaction_id=8db0a6fc-ad42-4974-ac1f-36bb90730afe'
```
```json
Response:
{
"message": "Transaction not found",
"transactionId": "8db0a6fc-ad42-4974-ac1f-36bb90730afe"
}
```
- Mock-Transaction
```json
curl --location 'https://9yp31ebze1.execute-api.us-east-1.amazonaws.com/dev/mock-transaction' \
--header 'Content-Type: application/json' \
--data '{
"userId" : "2222222"
}'
```
```json
Response:
{
"userId": "2222222",
"transactionId": "09a9c5ec-71ad-4692-9032-24d364118f71",
"status": "success"
}
```

- Excecute Payments
```json
curl --location 'https://9yp31ebze1.execute-api.us-east-1.amazonaws.com/dev/v1/payments' \
--header 'Content-Type: application/json' \
--data '{
"userId": "15f1c60a-2833-49b7-8660-065b58be2f89"
}'
```
Response:
```json
{
"billingDetails": {
"billedDurationInMilliseconds": 200,
"billedMemoryUsedInMB": 64
},
"executionArn": "arn:aws:states:us-east-1:922367397130:express:bcp_challenged_io_backend:4356725e-e3fb-49bc-b99d-9db7095cc881:dba255ef-e8fe-40bc-8ef4-1a9a628c5dd4",
"input": "{\n \"userId\": \"15f1c60a-2833-49b7-8660-065b58be2f89\"\n}",
"inputDetails": {
"__type": "com.amazonaws.swf.base.model#CloudWatchEventsExecutionDataDetails",
"included": true
},
"name": "4356725e-e3fb-49bc-b99d-9db7095cc881",
"output": "{}",
"outputDetails": {
"__type": "com.amazonaws.swf.base.model#CloudWatchEventsExecutionDataDetails",
"included": true
},
"startDate": 1.72170984853E9,
"stateMachineArn": "arn:aws:states:us-east-1:922367397130:stateMachine:bcp_challenged_io_backend",
"status": "SUCCEEDED",
"stopDate": 1.721709848725E9
}
```

## Inicio Rapido
```shell
make all
```
67 changes: 67 additions & 0 deletions iac/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
locals {
table_transactions = "transactions"
table_activity = "activity"
table_users = "users"
}

#Configuración de DynamoDB
module "bucket_resource" {
source = "./modules/bucket"
}

#Configuración de DynamoDB
module "dynamodb_resources" {
source = "./modules/dynamodb"

activity_table = local.table_activity
transactions_table = local.table_transactions
users_table = "users"
}

module "lambda_get_transactions_resources" {
source = "./modules/lambda/get-transactions"

table_name_transactions = local.table_transactions
lambda_bucket_name = module.bucket_resource.lambda_bucket_name
}

module "lambda_register_activity_resources" {
source = "./modules/lambda/register-activity"

table_name_activity = local.table_activity
lambda_bucket_name = module.bucket_resource.lambda_bucket_name
table_transactions_stream_arn = module.dynamodb_resources.table_transactions_stream_arn
}

module "lambda_api_mock_resources" {
source = "./modules/lambda/api-mock"
lambda_bucket_name = module.bucket_resource.lambda_bucket_name
}

module "lambda_execute_payment_resources" {
source = "./modules/lambda/execute-payments"
lambda_bucket_name = module.bucket_resource.lambda_bucket_name
api_gateway_url_api_mock = module.api_gateway_resources.base_url
}

module "api_gateway_resources" {
source = "./modules/api_gateway"
lambda_get_transaction_invoke_arn = module.lambda_get_transactions_resources.invoke_arn
lambda_get_transaction_function_name = module.lambda_get_transactions_resources.function_name
lambda_api_mock_invoke_arn = module.lambda_api_mock_resources.invoke_arn
lambda_api_mock_function_name = module.lambda_api_mock_resources.function_name
lambda_execute_payment_invoke_arn = module.lambda_execute_payment_resources.invoke_arn
lambda_execute_payment_function_name = module.lambda_execute_payment_resources.function_name
step_function_invoke_arn = module.step_function_resources.invoke_arn
}

module "step_function_resources" {
source = "./modules/step_function"
users_table = local.table_users
transactions_table = local.table_transactions
lambda_execution_payment = module.lambda_execute_payment_resources.invoke_arn

table_users_arn = module.dynamodb_resources.table_users_arn
table_transactions_arn = module.dynamodb_resources.table_transactions_arn
lambda_execute_payment_invoke_arn = module.lambda_execute_payment_resources.invoke_arn
}
16 changes: 16 additions & 0 deletions iac/terraform/modules/api_gateway/integrationLambdaApiMock.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Create an integration between the API Gateway and the Lambda Get-Transaction
resource "aws_apigatewayv2_integration" "api_mock" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id

integration_uri = var.lambda_api_mock_invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}

resource "aws_apigatewayv2_route" "api_mock" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id

route_key = "POST /mock-transaction"
target = "integrations/${aws_apigatewayv2_integration.api_mock.id}"
}
#END
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

# Create an integration between the API Gateway and the Lambda Get-Transaction
resource "aws_apigatewayv2_integration" "get_transactions" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id

integration_uri = var.lambda_get_transaction_invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}

resource "aws_apigatewayv2_route" "get_transactions" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id

route_key = "GET /v1/transactions"
target = "integrations/${aws_apigatewayv2_integration.get_transactions.id}"
}
#END
49 changes: 49 additions & 0 deletions iac/terraform/modules/api_gateway/integrationStepFunction.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Create an integration between the API Gateway and the Lambda Get-Transaction
resource "aws_apigatewayv2_integration" "execute_payment" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id
integration_type = "AWS_PROXY"
integration_subtype = "StepFunctions-StartSyncExecution"
credentials_arn = aws_iam_role.api_gateway_role.arn
request_parameters = {
"StateMachineArn" = var.step_function_invoke_arn
"Input" : "$request.body"
}
}

resource "aws_apigatewayv2_route" "execute_payment" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id

route_key = "POST /v1/payments"
target = "integrations/${aws_apigatewayv2_integration.execute_payment.id}"
}
#END

resource "aws_iam_role" "api_gateway_role" {
name = "api_gateway_role"

assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "apigateway.amazonaws.com"
}
}
]
})
inline_policy {
name = "allowStepFunction"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "states:StartSyncExecution"
Resource = "*"
}
]
})
}
}
55 changes: 55 additions & 0 deletions iac/terraform/modules/api_gateway/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#Create an HTTP API with API Gateway

resource "aws_apigatewayv2_api" "api_bcp_challenged" {
name = "bcp-challenged-apigatewayv2"
protocol_type = "HTTP"
}

resource "aws_apigatewayv2_stage" "api_bcp_challenged" {
api_id = aws_apigatewayv2_api.api_bcp_challenged.id
name = "dev"
auto_deploy = true

access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gw.arn

format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
protocol = "$context.protocol"
httpMethod = "$context.httpMethod"
resourcePath = "$context.resourcePath"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
integrationErrorMessage = "$context.integrationErrorMessage"
}
)
}
}

resource "aws_cloudwatch_log_group" "api_gw" {
name = "/aws/api_gw/${aws_apigatewayv2_api.api_bcp_challenged.name}"

retention_in_days = 30
}

resource "aws_lambda_permission" "api_gw_permission_get_transactions" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = var.lambda_get_transaction_function_name
principal = "apigateway.amazonaws.com"

source_arn = "${aws_apigatewayv2_api.api_bcp_challenged.execution_arn}/*/*"
}

resource "aws_lambda_permission" "api_gw_permission_api_mock" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = var.lambda_api_mock_function_name
principal = "apigateway.amazonaws.com"

source_arn = "${aws_apigatewayv2_api.api_bcp_challenged.execution_arn}/*/*"
}

9 changes: 9 additions & 0 deletions iac/terraform/modules/api_gateway/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "base_url" {
description = "Base URL for API Gateway stage."

value = aws_apigatewayv2_stage.api_bcp_challenged.invoke_url
}

output "invoke_arn" {
value = aws_apigatewayv2_api.api_bcp_challenged.arn
}
27 changes: 27 additions & 0 deletions iac/terraform/modules/api_gateway/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
variable "lambda_get_transaction_invoke_arn" {
description = "Arn of get-transactions lambda to invoke from api-gateway"
}

variable "lambda_get_transaction_function_name" {
description = "Lambda function name of get-transaction lambda"
}

variable "lambda_api_mock_invoke_arn" {
description = "Arn of api-mock lambda to invoke from api-gateway"
}

variable "lambda_api_mock_function_name" {
description = "Lambda function name of api-mock lambda"
}

variable "lambda_execute_payment_invoke_arn" {
description = "Arn of execute-payment lambda to invoke from api-gateway"
}

variable "lambda_execute_payment_function_name" {
description = "Lambda function name of execute-payment lambda"
}

variable "step_function_invoke_arn" {
description = "Arn of the Step Function."
}
24 changes: 24 additions & 0 deletions iac/terraform/modules/bucket/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
resource "random_pet" "lambda_bucket_name" {
prefix = "bcp-challenged-terraform"
length = 4
}

# Configuration to create your S3 bucket
resource "aws_s3_bucket" "lambda_bucket" {
bucket = random_pet.lambda_bucket_name.id
}

resource "aws_s3_bucket_ownership_controls" "lambda_bucket" {
bucket = aws_s3_bucket.lambda_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}

resource "aws_s3_bucket_acl" "lambda_bucket" {
depends_on = [aws_s3_bucket_ownership_controls.lambda_bucket]

bucket = aws_s3_bucket.lambda_bucket.id
acl = "private"
}
#END resource
Loading