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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.terraform/
.shelltool/
.serverless/
makefiles/
passwd

Expand Down
164 changes: 51 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,136 +1,74 @@
# Reto técnico iO - Backend
# Backend

## Descripción:
Se requiere implementar un sistema el cual simule el abono de saldo a una cuenta y su posterios consulta. A continuación se muestran los diagramas correspondientes:
## 1. Instalacion


### Funcionalidad de abono
#### Contexto
![POST-C4-L1](images/POST-C4-L1.drawio.png)
#### Contenedores
![POST-C4-L2](images/POST-C4-L2.drawio.png)
#### Componentes
![POST-C4-L3](images/POST-C4-L3.drawio.png)


Este API debe de llamar a un Step Function el cual debe:
- Validar el id de usuario comparándolo en la tabla **users**
- En caso el usuario exista, un lambda llamado **execute-payment** debe llamar a un API Mock que el postulante debe crear, el cual debe de regresar una transacción exitosa a modo de evento.
- Si la transacción es exitosa, como una siguiente tarea en el Step Function, debe de grabarse un registro en la tabla **transactions**
- De forma asincrona, por medio de DynamoDB Streams, se debe ejecutar un lambda llamado **update-account**, el cual actualizara el saldo de la cuenta a la que se realizo el abono en la tabla **accounts**.
- Al terminar todo de forma exitosa, debe dar una respuesta satisfactoria que contenga el id (source) de la transacción.

---

### Funcionalidad de consulta de cuenta
#### Contexto
![GET-C4-L1](images/GET-C4-L1.drawio.png)
#### Contenedores
![GET-C4-L2](images/GET-C4-L2.drawio.png)
#### Componentes
![GET-C4-L3](images/GET-C4-L3.drawio.png)

Este API debe de llamar a un Lambda Function la cual debe:
- Consultar por id a la tabla **accounts**
- En caso la cuenta exista, debe de regresar el registro.
- En caso la cuenta no exista, debe de regresar una respuesta con el mensaje "Cuenta no encontrada"

## Consideraciones:

### Datos de pruebas (IMPORTANTE):

Ya que no se contara con un servicio para crear las cuentas, estas deberan de ser creadas manualmente en DynamoDB.


### Obligatorio :
1. Respetar el arquetipo
2. Construir pruebas unitarias
3. Buenas Prácticas (SOLID, Clean Code, etc)

### Deseable:
1. Crear los componentes con IaC (Terraform, Cloudformation)
2. Logs usando CloudWatch
3. Formatters / Linters

## Anexos:

### Accounts (DynamoDB Table)
- PK: id (S)


### Transaction (DynamoDB Table)
- PK: source (S)
- SK: id (N)

### Account (Model)
### Instalar
```
{
"id": "{uuid}",
"amount": "{number}"
}
npm install
```

### Transaction (Model)
### Modificar archivo serverless.yaml
Ve a la línea 48 y reemplazar 1234567890 por la cuenta AWS donde se va desplegar el proyecto.
```
{
"source": "{uuid}",
"id": "{id}",
"data": {
"accountId": "{id}",
"amount": "{number}"
}
}
accountId: 1234567890
```

### POST /v1/payments

Payload
### Desplegar
```
{
"accountId": "f529177d-0521-414e-acd9-6ac840549e97",
"amount": 30
}
npm run deploy
```
En la cuenta AWS se crearán los recursos:

Respusta OK (201)
```
{
"message": "Payment registered successfully",
"transactionId": "8db0a6fc-ad42-4974-ac1f-36bb90730afe"
}
```
| Recurso | Nombre |
|----------------- |---------------------------- |
| Cloudformation | *PAYBALANCE-IO* |
| IAM Rol | *PayBalanceLambdaRole* |
| IAM Política | *PayBalanceLambdaPolicy* |
| Step Function | *execute-payment-stf* |
| Api Gateway | *PayBalanceApi* |
| Lambda | *execute-payment* |
| Lambda | *update-account* |
| Lambda | *get-account* |
| DynamoDb | *Accounts* |
| DynamoDb | *Transaction* |

Respuesta errada (400)
```
{
"message": "Something was wrong"
}
```

### GET /v1/accounts/{accountId}
## 2. Preparar ambiente AWS
### Implementar Api Gateway
* 1) Implementar manualmente el api gateway **PayBalanceApi**.
* 2) Copiar URL de invocación.
* 3) Modificar archivo **serverless.yaml**

Path Params
```
accountId: "8db0a6fc-ad42-4974-ac1f-36bb90730afe"
```
Ve a la línea 68 y reemplazar https://123ABC123.execute-api.us-east-2.amazonaws.com/desa por la ruta del api fake Transactions.
```
fakeApiTransaction: https://123ABC123.execute-api.us-east-2.amazonaws.com/desa
```

Respusta OK (200)
```
{
"id": "8db0a6fc-ad42-4974-ac1f-36bb90730afe",
"amount": "30",
}
```
* 4) Desplegar, para actualizar **ENV** en lambdas. (****Importante***)
```
npm run deploy
```

Respuesta errada (404)
### Registrar cuenta en tabla **Accounts**.
```
{
"message": "Account not found"
"id": {
"S": "914803b2-c865-4973-8dd2-8615b3710f08"
},
"amount": {
"N": "1992"
}
}
```

## 3. Ambiente Local

## 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.
### Inicializar
```
npm run start
```

### Tiempo de resolución: 3 días
### Ejecutar test
```
npm run test
```
153 changes: 153 additions & 0 deletions deploy/resources/api-gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
Resources:
PayBalanceRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: PayBalanceApi

PaymentResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- PayBalanceRestApi
- RootResourceId
PathPart: payments
RestApiId:
Ref: PayBalanceRestApi

TransactionResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- PayBalanceRestApi
- RootResourceId
PathPart: transactions
RestApiId:
Ref: PayBalanceRestApi

AccountsResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- PayBalanceRestApi
- RootResourceId
PathPart: accounts
RestApiId:
Ref: PayBalanceRestApi

AccountIdResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Ref: AccountsResource
PathPart: "{accountId}"
RestApiId:
Ref: PayBalanceRestApi

PaymentMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: POST
ResourceId:
Ref: PaymentResource
RestApiId:
Ref: PayBalanceRestApi
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri:
Fn::Sub: arn:aws:apigateway:${self:custom.region}:states:action/StartExecution
Credentials: ${self:custom.roleArn}
RequestTemplates:
application/json: |
#set($inputRoot = $input.path('$'))
#set($inputRoot.requestId = $context.requestId)
{
"input": "$util.escapeJavaScript($input.json('$'))",
"stateMachineArn": "${self:custom.arnPaymentStf}"
}
IntegrationResponses:
- StatusCode: 201
ResponseTemplates:
application/json: |
{
"message": "Payment registered successfully",
"transactionId": "$context.requestId"
}
- StatusCode: 400
SelectionPattern: '4\d{2}'
ResponseTemplates:
application/json: |
{
"message": "Something was wrong"
}
MethodResponses:
- StatusCode: 201
- StatusCode: 400

FakeTransactionMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: POST
ResourceId:
Ref: TransactionResource
RestApiId:
Ref: PayBalanceRestApi
Integration:
Type: MOCK
RequestTemplates:
application/json: |
{
"statusCode": 200
}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: |
{
"id": "$context.requestTimeEpoch",
"message": "Success"
}
MethodResponses:
- StatusCode: 200

GetAccountMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: GET
ResourceId:
Ref: AccountIdResource
RestApiId:
Ref: PayBalanceRestApi
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri:
Fn::Sub: arn:aws:apigateway:${self:custom.region}:lambda:path/2015-03-31/functions/${self:custom.arnGetAccount}/invocations
Credentials: ${self:custom.roleArn}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: |
{
"id": "$input.path('$.id')",
"amount": "$input.path('$.amount')"
}
- StatusCode: 400
ResponseTemplates:
application/json: |
{
"message": "$input.path('$.message')"
}
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: "Empty"
- StatusCode: 400
ResponseModels:
application/json: "Empty"
34 changes: 34 additions & 0 deletions deploy/resources/dynamodb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Resources:
AccountsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.accountsTable}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5

TransactionTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.transactionTable}
AttributeDefinitions:
- AttributeName: source
AttributeType: S
- AttributeName: id
AttributeType: N
KeySchema:
- AttributeName: source
KeyType: HASH
- AttributeName: id
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
StreamSpecification:
StreamViewType: NEW_IMAGE
Loading