Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #5 from 7SOATSquad30/feature/product
Browse files Browse the repository at this point in the history
feat: add product resource
  • Loading branch information
jonasmzsouza authored May 4, 2024
2 parents 9b811e1 + 0d60364 commit e505cf4
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/main/java/br/com/fiap/grupo30/fastfood/dto/ProductDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package br.com.fiap.grupo30.fastfood.dto;

import br.com.fiap.grupo30.fastfood.entities.Product;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductDTO {

private Long id;
private String name;
private String description;
private Double price;
private String imgUrl;
private CategoryDTO category;

public ProductDTO(Product entity) {
id = entity.getId();
name = entity.getName();
description = entity.getDescription();
price = entity.getPrice();
imgUrl = entity.getImgUrl();
category = new CategoryDTO(entity.getCategory());
}
}
79 changes: 79 additions & 0 deletions src/main/java/br/com/fiap/grupo30/fastfood/entities/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package br.com.fiap.grupo30.fastfood.entities;

import jakarta.persistence.*;
import java.time.Instant;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@EqualsAndHashCode
@Entity
@Table(name = "product")
public class Product {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

@Column(columnDefinition = "TEXT")
private String description;

private Double price;
private String imgUrl;

@ManyToOne
@JoinColumn(name = "category_id")
private Category category;

@Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE")
private Instant createdAt;

@Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE")
private Instant updatedAt;

@Column(columnDefinition = "TIMESTAMP WITHOUT TIME ZONE")
private Instant deletedAt;

public void setId(Long id) {
this.id = id;
}

public void setName(String name) {
this.name = name;
}

public void setDescription(String description) {
this.description = description;
}

public void setPrice(Double price) {
this.price = price;
}

public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}

public void setCategory(Category category) {
this.category = category;
}

@PrePersist
public void prePersist() {
createdAt = Instant.now();
}

@PreUpdate
public void preUpdate() {
updatedAt = Instant.now();
}

@PreRemove
public void preRemove() {
deletedAt = Instant.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package br.com.fiap.grupo30.fastfood.repositories;

import br.com.fiap.grupo30.fastfood.entities.Product;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

@Query(
"SELECT obj FROM Product obj "
+ "WHERE (:category IS NULL OR obj.category.id = :category)")
List<Product> findProductsByCategoryId(Long category);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package br.com.fiap.grupo30.fastfood.resources;

import br.com.fiap.grupo30.fastfood.dto.ProductDTO;
import br.com.fiap.grupo30.fastfood.services.ProductService;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@RestController
@RequestMapping(value = "/products")
public class ProductResource {

private static final String PATH_VARIABLE_ID = "/{id}";

@Autowired private ProductService service;

@GetMapping
public ResponseEntity<List<ProductDTO>> findAll(
@RequestParam(value = "categoryId", defaultValue = "0") Long categoryId) {
List<ProductDTO> list = service.findAll(categoryId);
return ResponseEntity.ok().body(list);
}

@GetMapping(value = PATH_VARIABLE_ID)
public ResponseEntity<ProductDTO> findById(@PathVariable Long id) {
ProductDTO dto = service.findById(id);
return ResponseEntity.ok().body(dto);
}

@PostMapping
public ResponseEntity<ProductDTO> insert(@RequestBody ProductDTO dto) {
ProductDTO dtoCreated = service.insert(dto);
URI uri =
ServletUriComponentsBuilder.fromCurrentRequest()
.path(PATH_VARIABLE_ID)
.buildAndExpand(dto.getId())
.toUri();
return ResponseEntity.created(uri).body(dtoCreated);
}

@PutMapping(value = PATH_VARIABLE_ID)
public ResponseEntity<ProductDTO> update(@PathVariable Long id, @RequestBody ProductDTO dto) {
ProductDTO dtoUpdated = service.update(id, dto);
return ResponseEntity.ok().body(dtoUpdated);
}

@DeleteMapping(value = PATH_VARIABLE_ID)
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package br.com.fiap.grupo30.fastfood.resources;

import java.time.Instant;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class StandardError {
private Instant timestamp;
private Integer status;
private String error;
private String message;
private String path;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package br.com.fiap.grupo30.fastfood.resources.exceptions;

import br.com.fiap.grupo30.fastfood.resources.StandardError;
import br.com.fiap.grupo30.fastfood.services.exceptions.DatabaseException;
import br.com.fiap.grupo30.fastfood.services.exceptions.ResourceNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import java.time.Instant;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ResourceExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<StandardError> entityNotFound(
ResourceNotFoundException e, HttpServletRequest request) {
HttpStatus status = HttpStatus.NOT_FOUND;
StandardError err = new StandardError();
err.setTimestamp(Instant.now());
err.setStatus(status.value());
err.setError("Resource not found");
err.setMessage(e.getMessage());
err.setPath(request.getRequestURI());
return ResponseEntity.status(status).body(err);
}

@ExceptionHandler(DatabaseException.class)
public ResponseEntity<StandardError> database(DatabaseException e, HttpServletRequest request) {
HttpStatus status = HttpStatus.BAD_REQUEST;
StandardError err = new StandardError();
err.setTimestamp(Instant.now());
err.setStatus(status.value());
err.setError("Database exception");
err.setMessage(e.getMessage());
err.setPath(request.getRequestURI());
return ResponseEntity.status(status).body(err);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package br.com.fiap.grupo30.fastfood.services;

import br.com.fiap.grupo30.fastfood.dto.ProductDTO;
import br.com.fiap.grupo30.fastfood.entities.Category;
import br.com.fiap.grupo30.fastfood.entities.Product;
import br.com.fiap.grupo30.fastfood.repositories.CategoryRepository;
import br.com.fiap.grupo30.fastfood.repositories.ProductRepository;
import br.com.fiap.grupo30.fastfood.services.exceptions.DatabaseException;
import br.com.fiap.grupo30.fastfood.services.exceptions.ResourceNotFoundException;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProductService {

@Autowired private ProductRepository repository;

@Autowired private CategoryRepository categoryRepository;

@Transactional(readOnly = true)
public List<ProductDTO> findAll(Long categoryId) {
Long category = categoryId == 0 ? null : categoryId;
return repository.findProductsByCategoryId(category).stream().map(ProductDTO::new).toList();
}

@Transactional(readOnly = true)
public ProductDTO findById(Long id) {
Optional<Product> obj = repository.findById(id);
Product entity = obj.orElseThrow(() -> new ResourceNotFoundException("Entity not found"));
return new ProductDTO(entity);
}

@Transactional
public ProductDTO insert(ProductDTO dto) {
Product entity = new Product();
copyDtoToEntity(dto, entity);
entity = repository.save(entity);
return new ProductDTO(entity);
}

@Transactional
public ProductDTO update(Long id, ProductDTO dto) {
try {
Product entity = repository.getReferenceById(id);
copyDtoToEntity(dto, entity);
entity = repository.save(entity);
return new ProductDTO(entity);
} catch (EntityNotFoundException e) {
throw new ResourceNotFoundException("Id not found " + id, e);
}
}

public void delete(Long id) {
try {
repository.deleteById(id);
} catch (EmptyResultDataAccessException e) {
throw new ResourceNotFoundException("Id not found " + id, e);
} catch (DataIntegrityViolationException e) {
throw new DatabaseException("Integrity violation", e);
}
}

private void copyDtoToEntity(ProductDTO dto, Product entity) {
entity.setName(dto.getName());
entity.setDescription(dto.getDescription());
entity.setImgUrl(dto.getImgUrl());
entity.setPrice(dto.getPrice());
Category category = categoryRepository.getReferenceById(dto.getCategory().getId());
entity.setCategory(category);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package br.com.fiap.grupo30.fastfood.services.exceptions;

import java.io.Serial;

public class DatabaseException extends RuntimeException {
@Serial private static final long serialVersionUID = 1L;

public DatabaseException(String msg, Throwable exception) {
super(msg, exception);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package br.com.fiap.grupo30.fastfood.services.exceptions;

import java.io.Serial;

public class ResourceNotFoundException extends RuntimeException {

@Serial private static final long serialVersionUID = 1L;

public ResourceNotFoundException(String msg) {
super(msg);
}

public ResourceNotFoundException(String msg, Throwable exception) {
super(msg, exception);
}
}
30 changes: 29 additions & 1 deletion src/main/resources/import.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
INSERT INTO category(name, created_at) VALUES ('Snacks', NOW());
INSERT INTO category(name, created_at) VALUES ('Drinks', NOW());
INSERT INTO category(name, created_at) VALUES ('Desserts', NOW());
INSERT INTO category(name, created_at) VALUES ('Side Dishes', NOW());
INSERT INTO category(name, created_at) VALUES ('Sides', NOW());

INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('X-Burguer', 14.9, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('X-Bacon', 12.9, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('X-Chicken', 14.9, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('X-Fish', 15.9, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('CheeseBurguer', 12.9, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Coca-Cola', 9.9, 2, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Lemonade', 10.9, 2, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Orange Juice', 11.9, 2, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Water', 7.9, 2, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Ice Tea', 10.9, 2, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Vanilla Sundae', 8.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Strawberry Sundae', 8.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Chocolate Sundae', 8.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Vanilla Shake', 13.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Strawberry Shake', 13.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Chocolate Shake', 13.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Vanilla Cone', 5.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Chocolate Cone', 5.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Mixed Cone', 5.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Banana Pie', 7.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Apple Cone', 7.9, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('French Fries', 8.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Mustard', 1.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Ketchup', 1.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Mayonnaise', 1.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Barbeque Sauce', 1.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());
INSERT INTO product (name, price, category_id, description, img_url, created_at) VALUES ('Chicken Nuggets', 10.9, 4, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate rutrum ullamcorper.', 'https://', NOW());

0 comments on commit e505cf4

Please sign in to comment.