RenanOfugi / PS-Java

Processo Seletivo Java

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Avaliação Java

Objetivo

Este projeto visa atender os requisitos do desafio proposto pela Supera Tecnologia.

Descrição

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

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". -

Detalhamento Técnico

Tecnologias e Justificativas

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.

Dados populados no Banco de Dados

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.

Observações - alterações na classe Product fornecida

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.

Como executar o projeto

Em um terminal, execute:

./mvnw install

e depois execute:

./mvnw spring-boot:run

Requisição API

Utilize Postman ou Insomnia para enviar requisições à API.

Endpoints

Ao executar o projeto, estes endpoints estarão disponíveis para atender os requisitos do desafio.

GET /api/v1/store/cart/{id do carrinho}

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": ""
    }
  ]
}

GET /api/v1/store/cart

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": ""
      }
    ]
  }
]

POST /api/v1/store/cart/{id do carrinho}/products/{id do produto para adicionar}/add

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.

DELETE /api/v1/store/cart/{id do carrinho}/products/{id do produto para adicionar}/remove

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.

GET /api/v1/store/cart/{id do carrinho}/sort/{atributo}/{tipo de ordenação}

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": ""
    }
  ]
}

Plus - endpoint

Alguns endpoints que não fazem parte dos requisitos do desafio, mas utilizei para consultar no processo de construção desde projeto.

GET /api/v1/store/products

Retorna uma lista todos os produtos cadastrados no banco de dados.

GET /api/v1/store/products/{id}

Retorna um produto específico do banco de dados

Testes

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.

About

Processo Seletivo Java

License:MIT License


Languages

Language:Java 100.0%