Skip to content

Latest commit

 

History

History
842 lines (685 loc) · 26.9 KB

README.md

File metadata and controls

842 lines (685 loc) · 26.9 KB

Index app

CRUD_Amazon_DynamoDB_AWS

Modelo CRUD para el manejo de objetos payments de mercadopago con DynamoDB de aws implementado con Api-Gateway, Systems Manager Parameter Store, Serverless-Framework, Lambda, Typescript, DynamoDB, aws-sdk-v3, entre otros. Como representación se tomara la api de mercado pago para el manejo de pagos.


Índice 📜

Ver

Sección 1) Descripción, configuración y tecnologías

Sección 2) Endpoints y Ejemplos

Sección 3) Prueba de funcionalidad y Referencias



Sección 1) Descripción, configuración y tecnologías

1.0) Descripción 🔝

Ver

1.0.0) Descripción General

Aplicación CRUD para operaciones de objetos payments de MercadoPago a través de una arquitectura serverless con Typescript y una db de tipo dynamodb, las operaciones incluyen la creación de un pago, obtener todos los pagos creados con y sin filtros, editar y eliminar un pago. Importante: Hay alertas de seguridad de dependabot que apuntan contra el plugin "serverless-dynamodb-local". No aplicar parches de seguridad a dicho plugin, ya que la versión ^1.0.2 tiene problemas al momento de la creación de tablas y ejecución del servicio de dynamo. Se recomienda mantener la última versión estable ^0.2.40 con las alertas de seguridad generadas.

1.0.1) Descripción Arquitectura y Funcionamiento

La imagen de la arquitectura de aws empleada describe el flujo de funcionamiento de la app de forma general. Cualquier petición hacia la misma, parte desde un cliente (Ej: Postman, servidor, etc). La descripción y flujo de cada paso es la siguiente :

  • Paso 0 : Se genera una solicitud-petición hacia una de las funcionalidades desarrollada, la misma es recibida a través del api-gateway y solamente se validará si es que dentro de los encabezados de dicha solicitud se encuentra la x-api-key correcta.
  • Pasos 1A, 1B, etc : Todos estos pasos corresponden a un endpoint con su recurso especifico. Por ej. para create payment (1A) es http://localhost:4000/dev/v1/payments....etc. Revisar dichos endpoints en sección endpoints. Cada lambda realiza comprobación de x-api-key y token.
  • Pasos 2 : Las lambdas realizan las validaciones de las ssm correspondientes con el System Manager Paramater Store.. validan token, valores de conexión con la db etc.
  • Pasos 3 : Las lambdas realizan las transacciones y operaciones descritas con el tipo de base de datos Dynamodb.

1.1) Ejecución del Proyecto 🔝

Ver
  • Creamos un entorno de trabajo a través de algún ide, podemos o no crear una carpeta raíz para el proyecto, nos posicionamos sobre la misma
cd 'projectRootName'
  • Una vez creado un entorno de trabajo a través de algún ide, clonamos el proyecto
git clone https://github.com/andresWeitzel/CRUD_Amazon_DynamoDB_AWS
  • Instalamos la última versión LTS de Nodejs(v18)
  • Instalamos Serverless Framework de forma global si es que aún no lo hemos realizado
npm install -g serverless
  • Verificamos la versión de Serverless instalada
sls -v
  • Instalamos todos los paquetes necesarios
npm i

Importante: Hay alertas de seguridad de dependabot que apuntan contra el plugin "serverless-dynamodb-local". No aplicar parches de seguridad a dicho plugin, ya que la versión ^1.0.2 tiene problemas al momento de la creación de tablas y ejecución del servicio de dynamo. Se recomienda mantener la última versión estable ^0.2.40 con las alertas de seguridad generadas.

  • Las variables ssm utilizadas en el proyecto se mantienen para simplificar el proceso de configuración del mismo. Es recomendado agregar el archivo correspondiente (serverless_ssm.yml) al .gitignore.
  • El siguiente script configurado en el package.json del proyecto es el encargado de
    • Levantar serverless-offline (serverless-offline)
 "scripts": {
   "serverless-offline": "sls offline start",
   "start": "npm run serverless-offline"
 },
  • Ejecutamos la app desde terminal.
npm start
  • Si se presenta algún mensaje indicando qué el puerto 4000 u 8000 ya está en uso, podemos terminar todos los procesos dependientes y volver a ejecutar la app
npx kill-port 4000 (serverless)
npx kill-port 8000 (dynamo)
npm start

1.2) Configuración del proyecto desde cero 🔝

Ver
  • Creamos un entorno de trabajo a través de algún ide, luego de crear una carpeta nos posicionamos sobre la misma
cd 'projectName'
  • Instalamos la última versión LTS de Nodejs(v18)
  • Instalamos Serverless Framework de forma global si es que aún no lo tenemos instalado.
npm install -g serverless
  • Verificamos la versión de Serverless instalada
sls -v
  • Inicializamos un template ts de serverles
serverless create --template aws-nodejs-typescript
  • Comprobamos la versión de typescript
tsc -v
  • Instalamos los paquetes necesarios
npm i
  • Instalamos serverless offline
npm i serverless-offline --save-dev
  • Agregamos el plugin dentro del serverless.yml
plugins:
  - serverless-offlline
  • Instalamos serverless ssm
npm i serverless-offline-ssm --save-dev
  • Agregamos el plugin dentro del serverless.yml
plugins:
  - serverless-offlline-ssm
  • Instalamos esbuild para el compilado entre js y ts
npm i serverless-esbuild
  • Instalamos el plugin para el uso de dynamodb en local (No el servicio de dynamoDB, este viene configurado en los archivos dentro de .dynamodb). Importante: Hay alertas de seguridad de dependabot que apuntan contra el plugin "serverless-dynamodb-local". No aplicar parches de seguridad a dicho plugin, ya que la versión ^1.0.2 tiene problemas al momento de la creación de tablas y ejecución del servicio de dynamo. Se recomienda mantener la última versión estable ^0.2.40 con las alertas de seguridad generadas.
npm install serverless-dynamodb-local --save-dev
  • Agregamos el plugin dentro del serverless.yml
plugins:
  - serverless-dynamodb-local
  • Instalamos el sdk client de dynamodb para las operaciones de db necesarias
npm install @aws-sdk/client-dynamodb
  • Instalamos el sdk lib de dynamodb para las operaciones de db necesarias
npm i @aws-sdk/lib-dynamodb
  • Modificaremos la plantilla inicial. Cambiamos serverless.ts por serverless.yml para las configs estandarizadas.
  • Reemplazamos la plantila serverless.ts inicial por la siguiente como modelo (cambiar nombre, etc) según la creada...
service: nombre

frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region : us-west-1
  memorySize: 512
  timeout : 10

plugins:
    - serverless-dynamodb-local
    - serverless-esbuild
    - serverless-offline-ssm
    - serverless-offline  

functions:
  functions:
    hello:
      handler: src/functions/hello/handler.ts
      events:
        - http:
            path: /test
            method: POST
            private: true  

custom:
  serverless-offline:
    httpPort: 4000
    lambdaPort: 4002    
  serverless-offline-ssm:
    stages:
      - dev
  dynamodb:
    stages:
      - dev
  • Debemos descargar el .jar junto con su config para ejecutar el servicio de dynamodb. Descargar aquí
  • Una vez descargado el .jar en formato .tar descomprimimos y copiamos todo su contenido dentro de la carpeta .dynamodb.
  • Usaremos git como control de versiones. Nos posicionamos en la app e inicializamos git
git init
  • Creamos el repositorio en github (sin readme) y agregamos la url del repositorio creado (ej: la siguiente)
git remote add origin https://github.com/andresWeitzel/CRUD_Amazon_DynamoDB_AWS
  • Traemos los cambios del remoto, agregamos los nuevos cambios en local, commitiamos y los subimos al repo.
git pull origin master
git add *
git commit -m "Add app config"
git push origin master
  • Las variables ssm utilizadas en el proyecto se mantienen para simplificar el proceso de configuración del mismo. Es recomendado agregar el archivo correspondiente (serverless_ssm.yml) al .gitignore.
  • El siguiente script configurado en el package.json del proyecto es el encargado de
    • Levantar serverless-offline (serverless-offline)
 "scripts": {
   "serverless-offline": "sls offline start",
   "start": "npm run serverless-offline"
 },
  • Ejecutamos la app desde terminal.
npm start
  • Si se presenta algún mensaje indicando qué el puerto 4000 u 8000 ya está en uso, podemos terminar todos los procesos dependientes y volver a ejecutar la app
npx kill-port 4000 (serverless)
npx kill-port 8000 (dynamo)
npm start
  • Deberíamos esperar un output por consola con los siguiente servicios levantados cuando se ejecuta el comando anterior
> crud-amazon-dynamodb-aws@1.0.0 start
> npm run serverless-offline

> crud-amazon-dynamodb-aws@1.0.0 serverless-offline
> sls offline start

serverless-offline-ssm checking serverless version 3.31.0.
Dynamodb Local Started, Visit: http://localhost:8000/shell
DynamoDB - created table payments-table

etc.....
  • Ya tenemos una app funcional con una estructura inicial definida por Serverless-Framework. La aplicación queda deployada en http://localhost:4002 y podemos testear el endpoint declarado en el serverless desde postman
  • Aclaración : El resto de las modificaciones aplicadas sobre la plantilla inicial no se describen por temas de simplificación de doc. Para más info consultar el tutorial de Serverless-framework para el uso de servicios, plugins, etc.

1.3) Tecnologías 🔝

Ver
Tecnologías Versión Finalidad
SDK 4.3.2 Inyección Automática de Módulos para Lambdas
Serverless Framework Core v3 3.23.0 Core Servicios AWS
Systems Manager Parameter Store (SSM) 3.0 Manejo de Variables de Entorno
Amazon Api Gateway 2.0 Gestor, Autenticación, Control y Procesamiento de la Api
Amazon DynamoDB 2019.11.21 Servicio de base de datos NoSQL
Typescript 5.2 Lenguaje altamente tipado
NodeJS 14.18.1 Librería JS
VSC 1.72.2 IDE
Postman 10.11 Cliente Http
CMD 10 Símbolo del Sistema para linea de comandos
Git 2.29.1 Control de Versiones

Serverless Plugin Descripción
serverless-esbuild Complemento para transpilar código Typescript y Javascript
serverless-offline Este complemento sin servidor emula AWS λ y API Gateway en entorno local
serverless-offline-ssm Complemento para variables de entorno que cumplen los parámetros de SSM en el momento de la compilación y las sustituye desde un archivo
serverless-dynamodb-local Complemento para tipo de db NoSQL DynamoDB


Sección 2) Endpoints y Ejemplos.

2.0) Endpoints y recursos 🔝

Ver

Operaciones de tipo GET:

Operaciones de tipo POST:

Operaciones de tipo PUT:

Operaciones de tipo DELETE:

Aclaraciones

  • {valor-requerido}
  • Paginado por defecto : ?page=0&limit=5
  • Paginado opcional : ?page={nro}&limit={nro}

2.1) Ejemplos 🔝

Ver

2.1.0) Variables en Postman

Variable Initial value Current value
base_url http://localhost:4000/dev/v1 http://localhost:4000/dev/v1
x-api-key f98d8cd98h73s204e3456998ecl9427j f98d8cd98h73s204e3456998ecl9427j
bearer_token Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c


2.1.1) Crear un objeto payment

Request (POST) | Code snippet

curl --location 'http://localhost:4000/dev/v1/payments/' \
--header 'x-api-key: f98d8cd98h73s204e3456998ecl9427j' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
--header 'Content-Type: application/json' \
--data-raw '{
  "items": {
    "id": "MLB2907679857",
    "title": "Point Mini",
    "description": "Producto Point para cobros con tarjetas mediante bluetooth",
    "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
    "category_id": "electronics",
    "quantity": 1,
    "unit_price": 1000
  },
  "payer": {
    "id": "12",
    "first_name": "Test",
    "last_name": "Test"
  },
  "shipments": {
    "receiver_address": {
      "zip_code": "B16-2231FG",
      "state_name": "Rio de Janeiro",
      "city_name": "Buzios",
      "street_name": "Av das Nacoes Unidas",
      "street_number": 3003
    }
  },
  "description": "Payment for product",
  "external_reference": "MP0001",
  "payment_method_id": "visa",
  "token": "ff8080814c11e237014c1ff593b57b4d",
  "transaction_amount": 58.8
}'

Response (200 OK)

{
    "message": {
        "uuid": "d5d58c31-8c29-41d2-a2e0-88322cb0238d",
        "description": "Payment for product",
        "externalReference": "MP0001",
        "paymentMethodId": "visa",
        "token": "ff8080814c11e237014c1ff593b57b4d",
        "transactionAmount": 58.8,
        "items": {
            "id": "MLB2907679857",
            "title": "Point Mini",
            "description": "Producto Point para cobros con tarjetas mediante bluetooth",
            "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
            "category_id": "electronics",
            "quantity": 1,
            "unit_price": 1000
        },
        "payer": {
            "id": "12",
            "first_name": "Test",
            "last_name": "Test"
        },
        "shipments": {
            "receiver_address": {
                "street_number": 3003,
                "city_name": "Rio de Janeiro",
                "state_name": "Buzios",
                "zip_code": "B16-2231FG",
                "street_name": "Buzios"
            }
        }
    }
}

Response (400 Bad Request) --> Aplica a todos los atributos con validación

{
    "message": "Bad request, check request attributes for Item Object . Validate the following : The value of the item id must be between 1 and 25 characters,The id of item must be of type string,The id of item cannot be empty"
}

Response (400 Bad Request)

{
    "message": "Bad request, check missing or malformed headers"
}

Response (401 Unauthorized)

{
    "message": "Not authenticated, check x_api_key and Authorization"
}

Other responses



2.1.2) Obtener todos los objetos Payment según filtro aplicado (descripción)

Request (GET) | Code snippet

curl --location 'http://localhost:4000/dev/v1/payments/list-with-filters?filter=description&filterValue=Payment&limit=10&orderAt=asc' \
--header 'x-api-key: f98d8cd98h73s204e3456998ecl9427j' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
--header 'Content-Type: application/json' \
--data ''

Response (200 OK)

{
    "message": [
        {
            "externalReference": "MP0001",
            "paymentMethodId": "visa",
            "transactionAmount": 58.8,
            "description": "Payment for product",
            "uuid": "d5d58c31-8c29-41d2-a2e0-88322cb0238d",
            "items": {
                "quantity": 1,
                "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
                "category_id": "electronics",
                "description": "Producto Point para cobros con tarjetas mediante bluetooth",
                "id": "MLB2907679857",
                "title": "Point Mini",
                "unit_price": 1000
            },
            "payer": {
                "first_name": "Test",
                "last_name": "Test",
                "id": "12"
            },
            "shipments": {
                "receiver_address": {
                    "street_number": 3003,
                    "city_name": "Rio de Janeiro",
                    "state_name": "Buzios",
                    "zip_code": "B16-2231FG",
                    "street_name": "Buzios"
                }
            },
            "token": "ff8080814c11e237014c1ff593b57b4d"
        }
        ETC....
    ]
}

Response (400 Bad Request)

{
    "message": "Bad request, check missing or malformed headers"
}

Response (401 Unauthorized)

{
    "message": "Not authenticated, check x_api_key and Authorization"
}

Other responses



2.1.3) Obtener un objeto payment según su uuid

Request (GET) | Code snippet

curl --location 'http://localhost:4000/dev/v1/payments/uuid/d5d58c31-8c29-41d2-a2e0-88322cb0238d' \
--header 'x-api-key: f98d8cd98h73s204e3456998ecl9427j' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
--header 'Content-Type: application/json' \
--data ''

Response (200 OK)

{
    "message": {
        "externalReference": "MP0001",
        "paymentMethodId": "visa",
        "transactionAmount": 58.8,
        "description": "Payment for product",
        "uuid": "d5d58c31-8c29-41d2-a2e0-88322cb0238d",
        "items": {
            "quantity": 1,
            "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
            "category_id": "electronics",
            "description": "Producto Point para cobros con tarjetas mediante bluetooth",
            "id": "MLB2907679857",
            "title": "Point Mini",
            "unit_price": 1000
        },
        "payer": {
            "first_name": "Test",
            "last_name": "Test",
            "id": "12"
        },
        "shipments": {
            "receiver_address": {
                "street_number": 3003,
                "city_name": "Rio de Janeiro",
                "state_name": "Buzios",
                "zip_code": "B16-2231FG",
                "street_name": "Buzios"
            }
        },
        "token": "ff8080814c11e237014c1ff593b57b4d"
    }
}

Response (400 Bad Request)

{
    "message": "Bad request, check missing or malformed headers"
}

Response (401 Unauthorized)

{
    "message": "Not authenticated, check x_api_key and Authorization"
}

Response (500 Internal Server Error)

{
    "message": "Bad request, unable to update object in db as failed to get a payment by uuid. Check if the payment exists in the database and try again"
}

Other responses



2.1.4) Actualizar un objeto Payment según su uuid

Request (PUT) | Code snippet

curl --location --request PUT 'http://localhost:4000/dev/v1/payments/d5d58c31-8c29-41d2-a2e0-88322cb0238d' \
--header 'x-api-key: f98d8cd98h73s204e3456998ecl9427j' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
--header 'Content-Type: application/json' \
--data-raw '{
  "items": {
    "id": "test",
    "title": "test",
    "description": "test",
    "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
    "category_id": "electronics",
    "quantity": 1,
    "unit_price": 1000
  },
  "payer": {
    "id": "12",
    "first_name": "Test",
    "last_name": "Test"
  },
  "shipments": {
    "receiver_address": {
      "zip_code": "B16-2231FG",
      "state_name": "Rio de Janeiro",
      "city_name": "Buzios",
      "street_name": "Av das Nacoes Unidas",
      "street_number": 3003
    }
  },
  "description": "Payment for product",
  "external_reference": "MP0001",
  "payment_method_id": "visa",
  "token": "ff8080814c11e237014c1ff593b57b4d",
  "transaction_amount": 58.8
}'

Response (200 OK)

{
    "message": {
        "externalReference": "MP0001",
        "paymentMethodId": "visa",
        "transactionAmount": 58.8,
        "description": "Payment for product",
        "items": {
            "quantity": 1,
            "picture_url": "https://http2.mlstatic.com/resources/frontend/statics/growth-sellers-landings/device-mlb-point-i_medium@2x.png",
            "category_id": "electronics",
            "description": "test",
            "id": "test",
            "title": "test",
            "unit_price": 1000
        },
        "payer": {
            "first_name": "Test",
            "last_name": "Test",
            "id": "12"
        },
        "shipments": {
            "receiver_address": {
                "street_number": 3003,
                "city_name": "Rio de Janeiro",
                "state_name": "Buzios",
                "zip_code": "B16-2231FG",
                "street_name": "Buzios"
            }
        },
        "uuid": "d5d58c31-8c29-41d2-a2e0-88322cb0238d",
        "token": "ff8080814c11e237014c1ff593b57b4d"
    }
}

Response (400 Bad Request)

{
    "message": "Bad request, check missing or malformed headers"
}

Response (401 Unauthorized)

{
    "message": "Not authenticated, check x_api_key and Authorization"
}

Response (500 Internal Server Error)

{
    "message": "Bad request, unable to update object in db as failed to get a payment by uuid. Check if the payment exists in the database and try again"
}

Other responses



2.1.5) Eliminar un objeto Payment según su uuid

Request (DELETE) | Code snippet

curl --location --request DELETE 'http://localhost:4000/dev/v1/payments/d5d58c31-8c29-41d2-a2e0-88322cb0238d' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' \
--header 'x-api-key: f98d8cd98h73s204e3456998ecl9427j' \
--header 'Content-Type: application/json'

Response (200 ok)

{
    "message": "Successfully removed payment based on uuid d5d58c31-8c29-41d2-a2e0-88322cb0238d"
}

Response (400 Bad Request)

{
    "message": "Bad request, check missing or malformed headers"
}

Response (401 Unauthorized)

{
    "message": "Not authenticated, check x_api_key and Authorization"
}

Response (500 Internal Server Error)

{
    "message": "Unable to delete payment based on uuid d5d58c31-8S29-41d2-a2e0-88322cb0238d"
}

Other responses



Sección 3) Prueba de funcionalidad y Referencias.

3.0) Prueba de funcionalidad 🔝

Ver

Tipos de Operaciones | Ver

Index app

3.1) Referencias 🔝

Ver

Herramientas

Api Gateway

Librerías