Este projeto visa atender os requisitos do desafio proposto pela Supera Tecnologia.
Projeto de API de um pseudo ecommerce de games mobile utilizando Spring, em que é possível ter múltiplos carrinhos e cada um podendo ter múltiplos produtos.
Requisitos | Status | Endpoint | Método Requisição |
---|---|---|---|
Os valores exibidos no checkout (frete, subtotal e total) devem ser calculados dinamicamente | Feito | /api/v1/store/cart/{id do carrinho} ou /api/v1/store/cart para ver todos os carrinhos. | GET |
O usuário poderá adicionar produtos do carrinho | Feito | /api/v1/store/cart/{id do carrinho}/products/{id do produto para adicionar}/add | POST |
O usuário poderá remover produtos do carrinho | Feito | /api/v1/store/cart/{id do carrinho}/products/{id do produto para adicionar}/remove | DELETE |
O usuário poderá ordenar os produtos por preço, popularidade (score) e ordem alfabética. | Feito | /api/v1/store/cart/{id do carrinho}/sort/{atributo}/{tipo de ordenação} - Atributos: name, price ou score; Tipo de ordenação permitidos: asc ou desc | GET |
A cada produto adicionado, deve-se somar R$ 10,00 ao frete. | Feito | Este requisito é atendido ao adicionar ou remover um produto ao carrinho - pode ser verificado juntamente com o requisito "Os valores exibidos no checkout (frete, subtotal e total) devem ser calculados dinamicamente". | - |
Quando o valor dos produtos adicionados ao carrinho for igual ou superior a R$ 250,00, o frete é grátis. | Feito | Este requisito é atendido ao adicionar ou remover um produto ao carrinho - pode ser verificado juntamente com o requisito "Os valores exibidos no checkout (frete, subtotal e total) devem ser calculados dinamicamente". | - |
Este projeto foi implementado usando:
- Spring Boot: Em razão ao curto tempo de entrega do desafio, optei por essa ferramenta pela facilidade de configuração, me permitindo focar mais nas regras de negócio do projeto.
- HSQLDB: Como o projeto inicial do desafio já utilizava este banco de dados em memória, optei por mantê-la. Banco de dados em memória tem a desvantagem de não manter a persistência dos dados, "zerando" tudo sempre que ela é inicializada, mas devido à restrição de tempo e por ser utilizada somente para este desafio, mantive a utilização do HSQLDB.
- Java 11: Versão Java já configurada na máquina.
- Maven: Por ser o gerenciador de dependências clássico do Spring Boot.
- Lombok: Visa diminuir o código, gerando métodos como getters, setters, construtores e o padrão Builder, por exemplo, utilizando somente anotações.
Todos os dados de produtos são populados no HSQLDB, ao inicializar o projeto, a partir do arquivo data.sql localizado em src/main/resources/data.sql. Portanto, se for preciso modificar os dados iniciais e/ou adicionar mais produtos, é necessário apenas modificar ou inserir novas linhas neste arquivo.
Como foi necessário modificar a classe Product, fornecido pela empresa, vale ressaltar as seguintes modificações:
- Foram alterados os modificadores de acesso iniciais da classe Product, de public para private, para manter o encapsulamento (um dos pilares da programação orientada a objetos).
- Foram também adicionados anotações de validação e persistência como @NotBlank, @Column, @Table.
- Foram adicionados anotações do Lombok, para reduzir código e aumentar a produtividade, gerando getters, setters e construtor com @Getter, @Setter e @RequiredArgsConstructor
- Foram criados os métodos equals e hashcode para uso das Collections.
Em um terminal, execute:
./mvnw install
e depois execute:
./mvnw spring-boot:run
Utilize Postman ou Insomnia para enviar requisições à API.
Ao executar o projeto, estes endpoints estarão disponíveis para atender os requisitos do desafio.
Retorna os dados de um carrinho específico.
Ex.: www.localhost:8080/api/v1/store/cart/1 : Irá retornar um Json referente ao carrinho 1.
{
"id": 1,
"subtotal": 250.58,
"total": 250.58,
"shippingCost": 0.00,
"products": [
{
"id": 1,
"name": "Garena Free Fire",
"price": 10.50,
"score": 10,
"image": ""
},
{
"id": 9,
"name": "Candy Crush Saga",
"price": 233.89,
"score": 755,
"image": ""
},
{
"id": 5,
"name": "Clash Royale",
"price": 6.19,
"score": 80,
"image": ""
}
]
}
Retorna os dados de todos os carrinhos da loja.
Ex.: www.localhost:8080/api/v1/store/cart
[
{
"id": 1,
"subtotal": 250.58,
"total": 250.58,
"shippingCost": 0.00,
"products": [
{
"id": 1,
"name": "Garena Free Fire",
"price": 10.50,
"score": 10,
"image": ""
},
{
"id": 9,
"name": "Candy Crush Saga",
"price": 233.89,
"score": 755,
"image": ""
},
{
"id": 5,
"name": "Clash Royale",
"price": 6.19,
"score": 80,
"image": ""
}
]
},
{
"id": 2,
"subtotal": 345.58,
"total": 345.58,
"shippingCost": 0.00,
"products": [
{
"id": 5,
"name": "Clash Royale",
"price": 6.19,
"score": 80,
"image": ""
},
{
"id": 9,
"name": "Candy Crush Saga",
"price": 233.89,
"score": 755,
"image": ""
},
{
"id": 3,
"name": "PUBG Mobile",
"price": 105.50,
"score": 90,
"image": ""
}
]
},
{
"id": 3,
"subtotal": 2.99,
"total": 12.99,
"shippingCost": 10.00,
"products": [
{
"id": 2,
"name": "Framed",
"price": 2.99,
"score": 20,
"image": ""
}
]
}
]
Adiciona um produto específico em um carrinho. Se o carrinho não existir, é criado um novo carrinho com este produto, retornando a mensagem 200 OK. E os dados do subtotal, frete e total são atualizados.
Ex.: www.localhost:8080/api/v1/store/cart/1/products/7/add - Irá adicionar o produto 7 no carrinho 1 e retornar:
200 OK
ou retornará um erro caso o produto não exista.
Remove um produto específico em um carrinho, retornando a mensagem 200 OK.
Ex.: www.localhost:8080/api/v1/store/cart/1/products/7/remove - Irá remover o produto 7 no carrinho 1 e retornar:
200 OK
ou retornará um erro caso o produto não exista.
Ordena os produtos de um carrinho específico de acordo com os parametros:
- atributo: ordena os produtos com base em um atributo do produto especificado. Pode-se ordenar com base no nome ("name"), preço("price") ou popularidade ("score").
- tipo de ordenação: ordena de forma crescente("asc") ou decrescente("desc").
Ex.: www.localhost:8080/api/v1/store/cart/1 - estado inicial
{
"id": 1,
"subtotal": 437.30,
"total": 437.30,
"shippingCost": 0.00,
"products": [
{
"id": 7,
"name": "Pokémon Go",
"price": 125.87,
"score": 500,
"image": ""
},
{
"id": 6,
"name": "Hearthstone",
"price": 74.55,
"score": 200,
"image": ""
},
{
"id": 2,
"name": "Framed",
"price": 2.99,
"score": 20,
"image": ""
},
{
"id": 9,
"name": "Candy Crush Saga",
"price": 233.89,
"score": 755,
"image": ""
}
]
}
www.localhost:8080/api/v1/store/cart/1/sort/price/desc - mesma lista anterior ordenado com base no preço dos produtos e de forma decrescente.
{
"id": 1,
"subtotal": 437.30,
"total": 437.30,
"shippingCost": 0.00,
"products": [
{
"id": 9,
"name": "Candy Crush Saga",
"price": 233.89,
"score": 755,
"image": ""
},
{
"id": 7,
"name": "Pokémon Go",
"price": 125.87,
"score": 500,
"image": ""
},
{
"id": 6,
"name": "Hearthstone",
"price": 74.55,
"score": 200,
"image": ""
},
{
"id": 2,
"name": "Framed",
"price": 2.99,
"score": 20,
"image": ""
}
]
}
Alguns endpoints que não fazem parte dos requisitos do desafio, mas utilizei para consultar no processo de construção desde projeto.
Retorna uma lista todos os produtos cadastrados no banco de dados.
Retorna um produto específico do banco de dados
Para executar os testes, basta rodar a classe CartServiceTest, referente à classe de serviços, em uma IDE. Foi gasto um certo tempo tentando entender e configurar para permitir que a classe de teste acessasse os dados em @Autowired, já que aparecia um erro mostrando que as classes de teste deveriam ter construtor vazio e as variaveis em final exigem um construtor para elas. Outro erro que aparecia com frequência era relacionado ao fato do relacionamento ManytoMany ser FetchType.LAZY. E como seria uma má pratica modificar simplesmente para EAGER, não o fiz. Por esse motivo somado a restrição de tempo, não foi possível cobrir totalmente os testes.