Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import com.omatheusmesmo.shoppmate.category.entity.Category;
import com.omatheusmesmo.shoppmate.category.mapper.CategoryMapper;
import com.omatheusmesmo.shoppmate.category.service.CategoryService;
import com.omatheusmesmo.shoppmate.user.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -28,52 +30,47 @@
public class CategoryController {

private final CategoryService categoryService;

private final CategoryMapper categoryMapper;

public CategoryController(CategoryService categoryService, CategoryMapper categoryMapper) {
this.categoryService = categoryService;
this.categoryMapper = categoryMapper;
}

@Operation(summary = "Return all categories")
@Operation(summary = "Return all accessible categories")
@GetMapping
public ResponseEntity<List<CategoryResponseDTO>> getAllCategories() {
List<Category> categories = categoryService.findAll();
public ResponseEntity<List<CategoryResponseDTO>> getAllCategories(@AuthenticationPrincipal User user) {
List<Category> categories = categoryService.findAllAccessibleByUser(user.getId());
List<CategoryResponseDTO> responseDTOS = categories.stream().map(categoryMapper::toResponseDTO).toList();
return ResponseEntity.ok(responseDTOS);
}

@Operation(summary = "Add a new category")
@PostMapping
public ResponseEntity<CategoryResponseDTO> addCategory(@RequestBody @Valid CategoryRequestDTO categoryDTO) {

Category category = categoryMapper.toEntity(categoryDTO);
public ResponseEntity<CategoryResponseDTO> addCategory(@RequestBody @Valid CategoryRequestDTO categoryDTO,
@AuthenticationPrincipal User user) {
Category category = categoryMapper.toEntity(categoryDTO, user);
Category savedCategory = categoryService.saveCategory(category);
CategoryResponseDTO responseDTO = categoryMapper.toResponseDTO(savedCategory);

URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedCategory.getId()).toUri();

return ResponseEntity.created(location).body(responseDTO);
}

@Operation(summary = "Delete a category by id")
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCategory(@PathVariable Long id) {
categoryService.removeCategory(id);
public ResponseEntity<Void> deleteCategory(@PathVariable Long id, @AuthenticationPrincipal User user) {
categoryService.removeCategory(id, user);
return ResponseEntity.noContent().build();
}

@Operation(summary = "Update a category")
@PutMapping("/{id}")
public ResponseEntity<CategoryResponseDTO> updateCategory(@PathVariable Long id,
@RequestBody @Valid CategoryRequestDTO categoryDTO) {
Category category = categoryService.findCategoryById(id);
category.setName(categoryDTO.name());
Category updatedCategory = categoryService.saveCategory(category);
@RequestBody @Valid CategoryRequestDTO categoryDTO, @AuthenticationPrincipal User user) {
categoryService.editCategory(id, categoryDTO, user);
Category updatedCategory = categoryService.findCategoryById(id);
CategoryResponseDTO responseDTO = categoryMapper.toResponseDTO(updatedCategory);

return ResponseEntity.ok(responseDTO);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package com.omatheusmesmo.shoppmate.category.dto;

public record CategoryResponseDTO(Long id, String name) {
import com.omatheusmesmo.shoppmate.user.dtos.UserResponseDTO;

public record CategoryResponseDTO(Long id, String name, boolean isSystemStandard, UserResponseDTO owner) {
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
package com.omatheusmesmo.shoppmate.category.entity;

import com.omatheusmesmo.shoppmate.shared.domain.DomainEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import com.omatheusmesmo.shoppmate.shared.domain.DomainEntity;
import com.omatheusmesmo.shoppmate.user.entity.User;

@Entity
@Table(name = "categories")
@Getter
@Setter
@NoArgsConstructor
public class Category extends DomainEntity {

@Column(name = "is_system_standard", nullable = false)
private boolean isSystemStandard = false;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "owner_id")
private User owner;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,37 @@
import com.omatheusmesmo.shoppmate.category.dto.CategoryRequestDTO;
import com.omatheusmesmo.shoppmate.category.dto.CategoryResponseDTO;
import com.omatheusmesmo.shoppmate.category.entity.Category;
import com.omatheusmesmo.shoppmate.user.dtos.UserResponseDTO;
import com.omatheusmesmo.shoppmate.user.entity.User;
import com.omatheusmesmo.shoppmate.user.mapper.UserMapper;
import org.springframework.stereotype.Component;

@Component
public class CategoryMapper {

public Category toEntity(CategoryRequestDTO requestDTO) {
private final UserMapper userMapper;

public CategoryMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}

public Category toEntity(CategoryRequestDTO requestDTO, User owner) {
Category category = new Category();
category.setName(requestDTO.name());

category.setSystemStandard(false);
category.setOwner(owner);
return category;
}

public Category toEntity(Long id, CategoryRequestDTO requestDTO) {
Category category = new Category();
category.setName(requestDTO.name());
category.setId(id);

return category;
}
Comment thread
matheusandre1 marked this conversation as resolved.

public CategoryResponseDTO toResponseDTO(Category category) {
return new CategoryResponseDTO(category.getId(), category.getName());
UserResponseDTO ownerDTO = category.getOwner() != null ? userMapper.toResponseDTO(category.getOwner()) : null;
return new CategoryResponseDTO(category.getId(), category.getName(), category.isSystemStandard(), ownerDTO);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

import com.omatheusmesmo.shoppmate.category.entity.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {

Optional<Category> findByName(String name);

Optional<Category> findByIdAndDeletedFalse(Long id);

@Query("SELECT c FROM Category c LEFT JOIN c.owner o WHERE c.deleted = false AND (c.isSystemStandard = true OR o.id = :userId)")
List<Category> findAllAccessibleByUserId(@Param("userId") Long userId);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.omatheusmesmo.shoppmate.category.service;

import com.omatheusmesmo.shoppmate.category.dto.CategoryRequestDTO;
import com.omatheusmesmo.shoppmate.category.entity.Category;
import com.omatheusmesmo.shoppmate.category.repository.CategoryRepository;
import com.omatheusmesmo.shoppmate.shared.service.AuditService;
import com.omatheusmesmo.shoppmate.user.entity.User;
import com.omatheusmesmo.shoppmate.utils.exception.ResourceOwnershipException;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -13,7 +16,6 @@
public class CategoryService {

private final CategoryRepository categoryRepository;

private final AuditService auditService;

public CategoryService(CategoryRepository categoryRepository, AuditService auditService) {
Expand All @@ -29,24 +31,48 @@ public Category saveCategory(Category category) {
}

public Category findCategoryById(Long id) {
return categoryRepository.findById(id).orElseThrow(() -> new NoSuchElementException("Category not found"));
return categoryRepository.findByIdAndDeletedFalse(id)
.orElseThrow(() -> new NoSuchElementException("Item not found with id: " + id));
}

public Optional<Category> findCategoryByName(String name) {
return categoryRepository.findByName(name);
}

public void removeCategory(Long id) {
Category category = categoryRepository.findById(id).orElseThrow();
public void removeCategory(Long id, User currentUser) {
Category category = findCategoryById(id);
verifyOwnershipOrSystem(category, currentUser);
auditService.softDelete(category);
saveCategory(category);
auditService.setAuditData(category, false);
categoryRepository.save(category);
}

public void editCategory(Long id, CategoryRequestDTO requestDTO, User currentUser) {
Category existingCategory = findCategoryById(id);
verifyOwnershipOrSystem(existingCategory, currentUser);
existingCategory.setName(requestDTO.name());
isCategoryValid(existingCategory);
auditService.setAuditData(existingCategory, false);
categoryRepository.save(existingCategory);
}

public void verifyOwnershipOrSystem(Category category, User currentUser) {
if (category.isSystemStandard()) {
throw new ResourceOwnershipException("System standard categories cannot be modified or deleted!");
}
if (category.getOwner() == null) {
throw new ResourceOwnershipException("Non-system categories must have an owner; operation not permitted");
}
if (!category.getOwner().getId().equals(currentUser.getId())) {
throw new ResourceOwnershipException("You can only modify or delete categories you own!");
}
}
Comment thread
matheusandre1 marked this conversation as resolved.
Comment thread
matheusandre1 marked this conversation as resolved.

public void isCategoryValid(Category category) {
category.checkName();
}

public List<Category> findAll() {
return categoryRepository.findAll();
public List<Category> findAllAccessibleByUser(Long userId) {
return categoryRepository.findAllAccessibleByUserId(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,53 @@

import com.omatheusmesmo.shoppmate.category.dto.CategoryResponseDTO;
import com.omatheusmesmo.shoppmate.category.entity.Category;
import com.omatheusmesmo.shoppmate.category.mapper.CategoryMapper;
import com.omatheusmesmo.shoppmate.category.repository.CategoryRepository;
import com.omatheusmesmo.shoppmate.item.dto.ItemRequestDTO;
import com.omatheusmesmo.shoppmate.item.dto.ItemResponseDTO;
import com.omatheusmesmo.shoppmate.item.entity.Item;
import com.omatheusmesmo.shoppmate.unit.dto.UnitResponseDTO;
import com.omatheusmesmo.shoppmate.unit.entity.Unit;
import com.omatheusmesmo.shoppmate.unit.mapper.UnitMapper;
import com.omatheusmesmo.shoppmate.unit.repository.UnitRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.NoSuchElementException;

@Component
public class ItemMapper {

@Autowired
private CategoryRepository categoryRepository;

@Autowired
private UnitRepository unitRepository;
private final CategoryRepository categoryRepository;
private final UnitRepository unitRepository;
private final CategoryMapper categoryMapper;
private final UnitMapper unitMapper;

public ItemMapper(CategoryRepository categoryRepository, UnitRepository unitRepository,
CategoryMapper categoryMapper, UnitMapper unitMapper) {
this.categoryRepository = categoryRepository;
this.unitRepository = unitRepository;
this.categoryMapper = categoryMapper;
this.unitMapper = unitMapper;
}

public Item toEntity(ItemRequestDTO dto) {
Category category = categoryRepository.findById(dto.idCategory())
Category category = categoryRepository.findByIdAndDeletedFalse(dto.idCategory())
.orElseThrow(() -> new NoSuchElementException("Category not found with id: " + dto.idCategory()));

Unit unit = unitRepository.findById(dto.idUnit())
Unit unit = unitRepository.findByIdAndDeletedFalse(dto.idUnit())
.orElseThrow(() -> new NoSuchElementException("Unit not found with id: " + dto.idUnit()));

Item item = new Item();
var item = new Item();
item.setName(dto.name());
item.setCategory(category);
item.setUnit(unit);
return item;
}

public ItemResponseDTO toResponseDTO(Item entity) {

CategoryResponseDTO categoryDto = new CategoryResponseDTO(entity.getCategory().getId(),
entity.getCategory().getName());
UnitResponseDTO unitDto = new UnitResponseDTO(entity.getUnit().getId(), entity.getUnit().getSymbol());
var categoryDto = categoryMapper.toResponseDTO(entity.getCategory());
var unitDto = unitMapper.toResponseDTO(entity.getUnit());

return new ItemResponseDTO(entity.getId(), entity.getName(), categoryDto, unitDto);
}

}
Loading
Loading