AWS NodeJS App 06 – Carrinho de compras com Time To Live do DynamoDB – Parte 1
O objetivo deste tutorial é implementar a funcionalidade de carrinho de compras, para isso vamos armazenar temporariamente objetos em nosso DynamoDB. E para isso vamos utilizar no ID da sessão HTTP como chave para identificar nossos usuários. Para isso precisamos instalar duas bibliotecas em nosso projeto.
npm install express-session --save
npm install cookie-parser --save
Para que estas bibliotecas funcionem precisamos modificar nosso arquivo app.js para importar e coloca-las no pipeline de processamento do express.


//muito código antes disso
var cookieParser = require('cookie-parser');
var session = require('express-session');
//mais código ainda
app.use(cookieParser());
app.use(session({
secret: 'wV95QY67vRxWtDOQJO0194NUJdl5zYam', // just a long random string
resave: false,
saveUninitialized: true
}));
Em seguida na pasta models vamos criar uma nova classe chamada ShoppingCar para representar o carrinho de compras.

var {
DataMapper, DynamoDbSchema, DynamoDbTable
} = require('@aws/dynamodb-data-mapper');
class ShoppingCar{
}
Object.defineProperties(ShoppingCar.prototype, {
[DynamoDbTable]: {
value: 'shoppingcar'
},
[DynamoDbSchema]: {
value: {
id: {
type: 'String',
keyType: 'HASH'
},
expiration: {type: 'Number'},
itens: {
type: 'Collection'
}
},
},
});
module.exports = ShoppingCar;
Agora vamos ao DynamoDB criar nossa nova tabela chamada shoppingcar, e vamos definir a propriedade expiration como nos Time to live attribute. Assim os documentos desta tabela serão automaticamente excluídos após um período de tempo.

Para saber mais sobre o TTL veja este site: https://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/howitworks-ttl.html
Em seguida vamos retornar ao nosso projeto e criar uma nova classe de serviço chamada ShoppingCarService, nesta classe vamos implementar dois métodos um para salvar um novo carrinho de compras e outro para recuperar o carrinho de compras pela propriedade id.

var {
DataMapper, DynamoDbSchema, DynamoDbTable
} = require('@aws/dynamodb-data-mapper');
var ConditionExpression = require('@aws/dynamodb-expressions');
var {contains} = require('@aws/dynamodb-expressions');
var DynamoDB = require('aws-sdk/clients/dynamodb');
const client = new DynamoDB({region: 'us-east-2'});
const mapper = new DataMapper({client});
const Product = require("../models/product");
const ShoppingCar = require("../models/shoppingcar");
class ShoppingCarService{
async save(shoppingCar){
if(shoppingCar.id == null || shoppingCar.id === ""){
throw Error("Todo carrinho de compras deve ter um ID");
}
mapper.put({item: shoppingCar}).then(() => {
return shoppingCar;
});
}
async getById(idshoppingcar){
var resultado = mapper.get(Object.assign(new ShoppingCar(), {id: idshoppingcar}))
.catch(err => {
return null;
});
return resultado;
}
}
module.exports = ShoppingCarService;
Podemos agora modificar nossa view index2.hbs do controlador index para que o botão Buy now chame o novo controlador shoppingcar na action add product passando o ID do produto selecionado.

<a href="/shoppingcar/addproduct?productid={{this.id}}"
class="btn btn-primary"> Buy now </a>
O próximo passo será criar o ShoppingCarController, nesta classe vamos criar o código da action addProduct que irá recuperar o id do produto selecionado, o identificador único da sessão. Em seguida nosso código chama a classe de serviço para verificar se encontra no DynamoDB algum objeto com o id igual ao id da sessão do usuário, caso não encontre ele cria um novo objeto contendo o id da sessão, o valor de expiração do documento e uma array de itens. Em seguida ele insere um novo objeto na lista de itens contendo o ID do produto selecionado e a quantidade inicial de 1 item.

var ProductService = require("../services/productservice");
var productService = new ProductService();
var Product = require("../models/product");
var ShoppingCarService = require("../services/shoppingcarservice");
var shoppingCarService = new ShoppingCarService();
var ShoppingCar = require("../models/shoppingcar");
class ShoppingCarController{
async addProduct(req,res,next){
var idproduct = req.query.productid;
var sessionid = req.sessionID;
await shoppingCarService.getById(sessionid).then((shoppingcar)=>{
if(shoppingcar == null){
shoppingcar = new ShoppingCar();
shoppingcar.id = sessionid;
shoppingcar.expiration = Math.floor( Date.now() / 1000 ) + 60;
shoppingcar.itens = new Array();
}
//console.log(JSON.stringify(shoppingcar, null, 10));
shoppingcar.itens.push({id:idproduct,quantity:1});
shoppingCarService.save(shoppingcar);
});
res.redirect("/");
}
}
module.exports = ShoppingCarController;
Agora nos devemos registrar nosso controlador, para isso vamos criar um novo arquivo na pasta de routes chamado shoppingcar.js criando a rota /addproduct.

var express = require('express');
var router = express.Router();
var ShoppingCarController = require("../controller/shoppingcarcontroller");
var shoppingCarController = new ShoppingCarController();
router.get('/addproduct', function(req, res, next) {
shoppingCarController.addProduct(req,res,next);
});
module.exports = router;
E agora vamos novamente alterar nosso arquivo app.js para incluir nosso novo arquivo de rotas para o carrinho de compras.


//muito código antes disso
var shoppingcarRouter = require('./routes/shoppingcar');
//várias e várias linhas de código
app.use('/shoppingcar', shoppingcarRouter);
Agora execute a aplicação e clique no botão Buy now, em qualquer um dos produtos. Ao acessar o DynamoDB observe que um novo documento será inserido, contendo como chave o id da sessão do usuário e o id do produto.

Por fim podemos alterar nossa tela inicial para apresentar o número de itens que estão no carrinho de compras. Para isso vamos modificar o código do nosso IndexController para incluir o código necessário para carregar o serviço do carrinho de compras, e na action index recuperar o carrinho de compras do usuário e calcular o número de itens que ele já adicionou e então enviar essa informação para a view.

var ShoppingCarService = require("../services/shoppingcarservice");
var shoppingCarService = new ShoppingCarService();
var ShoppingCar = require("../models/shoppingcar");
class IndexController{
async index(req,res,next){
var listProduct = await productService.getAll();
var numitens = 0;
await shoppingCarService.getById(req.sessionID).then((shoppingcar)=>{
if(shoppingcar != null)
numitens = shoppingcar.itens.length;
});
//console.log(JSON.stringify(listProduct, null, 4));
res.render('index/index2', { listProduct: listProduct, numitensshoppingcar:numitens });
}
Para que o número de itens seja apresentado em tela, vamos substituir a informação do telefone de contato no header da tela pelo número de itens que está no carrinho de compras.

<div class="text-wrap">
Carrinho de compras {{numitensshoppingcar}} itens
</div>
Agora ao acessar a aplicação o número de itens no carrinho de compras será apresentado no topo da tela. Aguarde alguns minutos e ao atualizar a tela, o carrinho de compras aparecerá zerado pois o documento foi removido automaticamente pelo DynamoDB.
