├── system.properties ├── settings.gradle ├── Procfile ├── src ├── main │ ├── resources │ │ ├── application.properties │ │ ├── application-dev.properties │ │ └── application-pdn.properties │ └── java │ │ └── com │ │ └── platzi │ │ └── market │ │ ├── domain │ │ ├── repository │ │ │ ├── PurchaseRepository.java │ │ │ └── ProductRepository.java │ │ ├── dto │ │ │ ├── AuthenticationResponse.java │ │ │ └── AuthenticationRequest.java │ │ ├── Category.java │ │ ├── service │ │ │ ├── PurchaseService.java │ │ │ ├── PlatziUserDetailsService.java │ │ │ └── ProductService.java │ │ ├── PurchaseItem.java │ │ ├── Product.java │ │ └── Purchase.java │ │ ├── PlatziMarketApplication.java │ │ ├── persistence │ │ ├── crud │ │ │ ├── CompraCrudRepository.java │ │ │ └── ProductoCrudRepository.java │ │ ├── entity │ │ │ ├── ComprasProductoPK.java │ │ │ ├── Categoria.java │ │ │ ├── ComprasProducto.java │ │ │ ├── Cliente.java │ │ │ ├── Producto.java │ │ │ └── Compra.java │ │ ├── mapper │ │ │ ├── CategoryMapper.java │ │ │ ├── PurchaseItemMapper.java │ │ │ ├── PurchaseMapper.java │ │ │ └── ProductMapper.java │ │ ├── CompraRepository.java │ │ └── ProductoRepository.java │ │ └── web │ │ ├── config │ │ └── SwaggerConfig.java │ │ ├── security │ │ ├── JWTUtil.java │ │ ├── filter │ │ │ └── JwtFilterRequest.java │ │ └── SecurityConfig.java │ │ └── controller │ │ ├── PurchaseController.java │ │ ├── AuthController.java │ │ └── ProductController.java └── test │ └── java │ └── com │ └── platzi │ └── market │ └── PlatziMarketApplicationTests.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradlew.bat ├── gradlew └── platzi-market.postman_collection.json /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=11 -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'platzi-market' 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -jar -Dspring.profiles.active=pdn build/libs/platzi-market-1.0.jar -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev 2 | server.servlet.context-path=/platzi-market/api -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soyalejoramirez/platzi_market/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | server.port=8090 2 | 3 | # Database 4 | spring.datasource.url=jdbc:postgresql://localhost:5432/platzi-market 5 | spring.datasource.username=postgres 6 | spring.datasource.password=platzi -------------------------------------------------------------------------------- /src/main/resources/application-pdn.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT} 2 | 3 | # Database 4 | spring.datasource.url=${SPRING_DATASOURCE_URL} 5 | spring.datasource.username=${SPRING_DATASOURCE_USERNAME} 6 | spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/test/java/com/platzi/market/PlatziMarketApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class PlatziMarketApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/repository/PurchaseRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.repository; 2 | 3 | import com.platzi.market.domain.Purchase; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface PurchaseRepository { 9 | List getAll(); 10 | Optional> getByClient(String clientId); 11 | Purchase save(Purchase purchase); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/PlatziMarketApplication.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class PlatziMarketApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(PlatziMarketApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/dto/AuthenticationResponse.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.dto; 2 | 3 | public class AuthenticationResponse { 4 | private String jwt; 5 | 6 | public AuthenticationResponse(String jwt) { 7 | this.jwt = jwt; 8 | } 9 | 10 | public String getJwt() { 11 | return jwt; 12 | } 13 | 14 | public void setJwt(String jwt) { 15 | this.jwt = jwt; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/crud/CompraCrudRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.crud; 2 | 3 | import com.platzi.market.persistence.entity.Compra; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface CompraCrudRepository extends CrudRepository { 10 | Optional> findByIdCliente(String idCliente); 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | !**/src/main/**/out/ 24 | !**/src/test/**/out/ 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbbuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/repository/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.repository; 2 | 3 | import com.platzi.market.domain.Product; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface ProductRepository { 9 | List getAll(); 10 | Optional> getByCategory(int categoryId); 11 | Optional> getScarseProducts(int quantity); 12 | Optional getProduct(int productId); 13 | Product save(Product product); 14 | void delete(int productId); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/crud/ProductoCrudRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.crud; 2 | 3 | import com.platzi.market.persistence.entity.Producto; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface ProductoCrudRepository extends CrudRepository { 10 | List findByIdCategoriaOrderByNombreAsc(int idCategoria); 11 | Optional> findByCantidadStockLessThanAndEstado(int cantidadStock, boolean estado); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/dto/AuthenticationRequest.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.dto; 2 | 3 | public class AuthenticationRequest { 4 | private String username; 5 | private String password; 6 | 7 | public String getUsername() { 8 | return username; 9 | } 10 | 11 | public void setUsername(String username) { 12 | this.username = username; 13 | } 14 | 15 | public String getPassword() { 16 | return password; 17 | } 18 | 19 | public void setPassword(String password) { 20 | this.password = password; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/Category.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain; 2 | 3 | public class Category { 4 | private int categoryId; 5 | private String category; 6 | private boolean active; 7 | 8 | public int getCategoryId() { 9 | return categoryId; 10 | } 11 | 12 | public void setCategoryId(int categoryId) { 13 | this.categoryId = categoryId; 14 | } 15 | 16 | public String getCategory() { 17 | return category; 18 | } 19 | 20 | public void setCategory(String category) { 21 | this.category = category; 22 | } 23 | 24 | public boolean isActive() { 25 | return active; 26 | } 27 | 28 | public void setActive(boolean active) { 29 | this.active = active; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/ComprasProductoPK.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Embeddable; 5 | import java.io.Serializable; 6 | 7 | @Embeddable 8 | public class ComprasProductoPK implements Serializable { 9 | @Column(name = "id_compra") 10 | private Integer idCompra; 11 | 12 | @Column(name = "id_producto") 13 | private Integer idProducto; 14 | 15 | public Integer getIdCompra() { 16 | return idCompra; 17 | } 18 | 19 | public void setIdCompra(Integer idCompra) { 20 | this.idCompra = idCompra; 21 | } 22 | 23 | public Integer getIdProducto() { 24 | return idProducto; 25 | } 26 | 27 | public void setIdProducto(Integer idProducto) { 28 | this.idProducto = idProducto; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.mapper; 2 | 3 | import com.platzi.market.domain.Category; 4 | import com.platzi.market.persistence.entity.Categoria; 5 | import org.mapstruct.InheritInverseConfiguration; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.mapstruct.Mappings; 9 | 10 | @Mapper(componentModel = "spring") 11 | public interface CategoryMapper { 12 | @Mappings({ 13 | @Mapping(source = "idCategoria", target = "categoryId"), 14 | @Mapping(source = "descripcion", target = "category"), 15 | @Mapping(source = "estado", target = "active"), 16 | }) 17 | Category toCategory(Categoria categoria); 18 | 19 | @InheritInverseConfiguration 20 | @Mapping(target = "productos", ignore = true) 21 | Categoria toCategoria(Category category); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/service/PurchaseService.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.service; 2 | 3 | import com.platzi.market.domain.Purchase; 4 | import com.platzi.market.domain.repository.PurchaseRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Service 12 | public class PurchaseService { 13 | @Autowired 14 | private PurchaseRepository purchaseRepository; 15 | 16 | public List getAll() { 17 | return purchaseRepository.getAll(); 18 | } 19 | 20 | public Optional> getByClient(String clientId) { 21 | return purchaseRepository.getByClient(clientId); 22 | } 23 | 24 | public Purchase save(Purchase purchase) { 25 | return purchaseRepository.save(purchase); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/PurchaseItem.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain; 2 | 3 | public class PurchaseItem { 4 | private int productId; 5 | private int quantity; 6 | private double total; 7 | private boolean active; 8 | 9 | public int getProductId() { 10 | return productId; 11 | } 12 | 13 | public void setProductId(int productId) { 14 | this.productId = productId; 15 | } 16 | 17 | public int getQuantity() { 18 | return quantity; 19 | } 20 | 21 | public void setQuantity(int quantity) { 22 | this.quantity = quantity; 23 | } 24 | 25 | public double getTotal() { 26 | return total; 27 | } 28 | 29 | public void setTotal(double total) { 30 | this.total = total; 31 | } 32 | 33 | public boolean isActive() { 34 | return active; 35 | } 36 | 37 | public void setActive(boolean active) { 38 | this.active = active; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.RequestHandlerSelectors; 6 | import springfox.documentation.service.ApiKey; 7 | import springfox.documentation.spi.DocumentationType; 8 | import springfox.documentation.spring.web.plugins.Docket; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | import java.util.Arrays; 12 | 13 | @Configuration 14 | @EnableSwagger2 15 | public class SwaggerConfig { 16 | 17 | @Bean 18 | public Docket api() { 19 | return new Docket(DocumentationType.SWAGGER_2) 20 | .securitySchemes(Arrays.asList(apiKey())) 21 | .select() 22 | .apis(RequestHandlerSelectors.basePackage("com.platzi.market.web.controller")) 23 | .build(); 24 | } 25 | 26 | private ApiKey apiKey() { 27 | return new ApiKey("JWT", "Authorization", "header"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/mapper/PurchaseItemMapper.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.mapper; 2 | 3 | import com.platzi.market.domain.PurchaseItem; 4 | import com.platzi.market.persistence.entity.ComprasProducto; 5 | import org.mapstruct.InheritInverseConfiguration; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.mapstruct.Mappings; 9 | 10 | @Mapper(componentModel = "spring") 11 | public interface PurchaseItemMapper { 12 | 13 | @Mappings({ 14 | @Mapping(source = "id.idProducto", target = "productId"), 15 | @Mapping(source = "cantidad", target = "quantity"), 16 | @Mapping(source = "estado", target = "active") 17 | }) 18 | PurchaseItem toPurchaseItem(ComprasProducto producto); 19 | 20 | @InheritInverseConfiguration 21 | @Mappings({ 22 | @Mapping(target = "compra", ignore = true), 23 | @Mapping(target = "producto", ignore = true), 24 | @Mapping(target = "id.idCompra", ignore = true) 25 | }) 26 | ComprasProducto toComprasProducto(PurchaseItem item); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/Categoria.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.*; 4 | import java.util.List; 5 | 6 | @Entity 7 | @Table(name = "categorias") 8 | public class Categoria { 9 | 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | @Column(name = "id_categoria") 13 | private Integer idCategoria; 14 | 15 | private String descripcion; 16 | private Boolean estado; 17 | 18 | @OneToMany(mappedBy = "categoria") 19 | private List productos; 20 | 21 | public Integer getIdCategoria() { 22 | return idCategoria; 23 | } 24 | 25 | public String getDescripcion() { 26 | return descripcion; 27 | } 28 | 29 | public void setDescripcion(String descripcion) { 30 | this.descripcion = descripcion; 31 | } 32 | 33 | public Boolean getEstado() { 34 | return estado; 35 | } 36 | 37 | public void setEstado(Boolean estado) { 38 | this.estado = estado; 39 | } 40 | 41 | public List getProductos() { 42 | return productos; 43 | } 44 | 45 | public void setProductos(List productos) { 46 | this.productos = productos; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/service/PlatziUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.service; 2 | 3 | import org.springframework.security.core.userdetails.User; 4 | import org.springframework.security.core.userdetails.UserDetails; 5 | import org.springframework.security.core.userdetails.UserDetailsService; 6 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | @Service 14 | public class PlatziUserDetailsService implements UserDetailsService { 15 | private static List users = new ArrayList(); 16 | 17 | public PlatziUserDetailsService() { 18 | users.add(new User("alejandro", "{noop}platzi", new ArrayList<>())); 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | Optional user = users.stream().filter(u -> u.getUsername().equals(username)).findAny(); 24 | 25 | if (!user.isPresent()) { 26 | throw new UsernameNotFoundException("User not found by name: " + username); 27 | } 28 | 29 | return user.get(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/mapper/PurchaseMapper.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.mapper; 2 | 3 | import com.platzi.market.domain.Purchase; 4 | import com.platzi.market.persistence.entity.Compra; 5 | import org.mapstruct.InheritInverseConfiguration; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.mapstruct.Mappings; 9 | 10 | import java.util.List; 11 | 12 | @Mapper(componentModel = "spring", uses = {PurchaseItemMapper.class}) 13 | public interface PurchaseMapper { 14 | 15 | @Mappings({ 16 | @Mapping(source = "idCompra", target = "purchaseId"), 17 | @Mapping(source = "idCliente", target = "clientId"), 18 | @Mapping(source = "fecha", target = "date"), 19 | @Mapping(source = "medioPago", target = "paymentMethod"), 20 | @Mapping(source = "comentario", target = "comment"), 21 | @Mapping(source = "estado", target = "state"), 22 | @Mapping(source = "productos", target = "items") 23 | }) 24 | Purchase toPurchase(Compra compra); 25 | List toPurchases(List compras); 26 | 27 | @InheritInverseConfiguration 28 | @Mapping(target = "cliente", ignore = true) 29 | Compra toCompra(Purchase purchase); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/mapper/ProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.mapper; 2 | 3 | import com.platzi.market.domain.Product; 4 | import com.platzi.market.persistence.entity.Producto; 5 | import org.mapstruct.InheritInverseConfiguration; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.mapstruct.Mappings; 9 | 10 | import java.util.List; 11 | 12 | @Mapper(componentModel = "spring", uses = {CategoryMapper.class}) 13 | public interface ProductMapper { 14 | @Mappings({ 15 | @Mapping(source = "idProducto", target = "productId"), 16 | @Mapping(source = "nombre", target = "name"), 17 | @Mapping(source = "idCategoria", target = "categoryId"), 18 | @Mapping(source = "precioVenta", target = "price"), 19 | @Mapping(source = "cantidadStock", target = "stock"), 20 | @Mapping(source = "estado", target = "active"), 21 | @Mapping(source = "categoria", target = "category"), 22 | }) 23 | Product toProduct(Producto producto); 24 | List toProducts(List productos); 25 | 26 | @InheritInverseConfiguration 27 | @Mapping(target = "codigoBarras", ignore = true) 28 | Producto toProducto(Product product); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain.service; 2 | 3 | import com.platzi.market.domain.Product; 4 | import com.platzi.market.domain.repository.ProductRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.dao.EmptyResultDataAccessException; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | @Service 13 | public class ProductService { 14 | @Autowired 15 | private ProductRepository productRepository; 16 | 17 | public List getAll() { 18 | return productRepository.getAll(); 19 | } 20 | 21 | public Optional getProduct(int productId) { 22 | return productRepository.getProduct(productId); 23 | } 24 | 25 | public Optional> getByCategory(int categoryId) { 26 | return productRepository.getByCategory(categoryId); 27 | } 28 | 29 | public Product save(Product product) { 30 | return productRepository.save(product); 31 | } 32 | 33 | public boolean delete(int productId) { 34 | try { 35 | productRepository.delete(productId); 36 | return true; 37 | } catch (EmptyResultDataAccessException e) { 38 | return false; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/security/JWTUtil.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.security; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.Date; 10 | 11 | @Component 12 | public class JWTUtil { 13 | private static final String KEY = "pl4tz1"; 14 | 15 | public String generateToken(UserDetails userDetails) { 16 | return Jwts.builder().setSubject(userDetails.getUsername()).setIssuedAt(new Date()) 17 | .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) 18 | .signWith(SignatureAlgorithm.HS256, KEY).compact(); 19 | } 20 | 21 | public boolean validateToken(String token, UserDetails userDetails) { 22 | return userDetails.getUsername().equals(extractUsername(token)) && !isTokenExpired(token); 23 | } 24 | 25 | public String extractUsername(String token) { 26 | return getClaims(token).getSubject(); 27 | } 28 | 29 | public boolean isTokenExpired(String token) { 30 | return getClaims(token).getExpiration().before(new Date()); 31 | } 32 | 33 | private Claims getClaims(String token) { 34 | return Jwts.parser().setSigningKey(KEY).parseClaimsJws(token).getBody(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/controller/PurchaseController.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.controller; 2 | 3 | import com.platzi.market.domain.Purchase; 4 | import com.platzi.market.domain.service.PurchaseService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | import java.util.function.Predicate; 12 | 13 | @RestController 14 | @RequestMapping("/purchases") 15 | public class PurchaseController { 16 | @Autowired 17 | private PurchaseService purchaseService; 18 | 19 | @GetMapping("/all") 20 | public ResponseEntity> getAll() { 21 | return new ResponseEntity<>(purchaseService.getAll(), HttpStatus.OK); 22 | } 23 | 24 | @GetMapping("/client/{idClient}") 25 | public ResponseEntity> getByClient(@PathVariable("idClient") String clientId) { 26 | return purchaseService.getByClient(clientId).filter(Predicate.not(List::isEmpty)) 27 | .map(purchases -> new ResponseEntity<>(purchases, HttpStatus.OK)) 28 | .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); 29 | } 30 | 31 | @PostMapping("/save") 32 | public ResponseEntity save(@RequestBody Purchase purchase) { 33 | return new ResponseEntity<>(purchaseService.save(purchase), HttpStatus.CREATED); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/CompraRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence; 2 | 3 | import com.platzi.market.domain.Purchase; 4 | import com.platzi.market.domain.repository.PurchaseRepository; 5 | import com.platzi.market.persistence.crud.CompraCrudRepository; 6 | import com.platzi.market.persistence.entity.Compra; 7 | import com.platzi.market.persistence.mapper.PurchaseMapper; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | @Repository 15 | public class CompraRepository implements PurchaseRepository { 16 | @Autowired 17 | private CompraCrudRepository compraCrudRepository; 18 | 19 | @Autowired 20 | private PurchaseMapper mapper; 21 | 22 | @Override 23 | public List getAll() { 24 | return mapper.toPurchases((List) compraCrudRepository.findAll()); 25 | } 26 | 27 | @Override 28 | public Optional> getByClient(String clientId) { 29 | return compraCrudRepository.findByIdCliente(clientId) 30 | .map(compras -> mapper.toPurchases(compras)); 31 | } 32 | 33 | @Override 34 | public Purchase save(Purchase purchase) { 35 | Compra compra = mapper.toCompra(purchase); 36 | compra.getProductos().forEach(producto -> producto.setCompra(compra)); 37 | 38 | return mapper.toPurchase(compraCrudRepository.save(compra)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/Product.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain; 2 | 3 | public class Product { 4 | private int productId; 5 | private String name; 6 | private int categoryId; 7 | private double price; 8 | private int stock; 9 | private boolean active; 10 | private Category category; 11 | 12 | public int getProductId() { 13 | return productId; 14 | } 15 | 16 | public void setProductId(int productId) { 17 | this.productId = productId; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public int getCategoryId() { 29 | return categoryId; 30 | } 31 | 32 | public void setCategoryId(int categoryId) { 33 | this.categoryId = categoryId; 34 | } 35 | 36 | public double getPrice() { 37 | return price; 38 | } 39 | 40 | public void setPrice(double price) { 41 | this.price = price; 42 | } 43 | 44 | public int getStock() { 45 | return stock; 46 | } 47 | 48 | public void setStock(int stock) { 49 | this.stock = stock; 50 | } 51 | 52 | public boolean isActive() { 53 | return active; 54 | } 55 | 56 | public void setActive(boolean active) { 57 | this.active = active; 58 | } 59 | 60 | public Category getCategory() { 61 | return category; 62 | } 63 | 64 | public void setCategory(Category category) { 65 | this.category = category; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/domain/Purchase.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.domain; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.List; 5 | 6 | public class Purchase { 7 | private int purchaseId; 8 | private String clientId; 9 | private LocalDateTime date; 10 | private Character paymentMethod; 11 | private String comment; 12 | private Character state; 13 | private List items; 14 | 15 | public int getPurchaseId() { 16 | return purchaseId; 17 | } 18 | 19 | public void setPurchaseId(int purchaseId) { 20 | this.purchaseId = purchaseId; 21 | } 22 | 23 | public String getClientId() { 24 | return clientId; 25 | } 26 | 27 | public void setClientId(String clientId) { 28 | this.clientId = clientId; 29 | } 30 | 31 | public LocalDateTime getDate() { 32 | return date; 33 | } 34 | 35 | public void setDate(LocalDateTime date) { 36 | this.date = date; 37 | } 38 | 39 | public Character getPaymentMethod() { 40 | return paymentMethod; 41 | } 42 | 43 | public void setPaymentMethod(Character paymentMethod) { 44 | this.paymentMethod = paymentMethod; 45 | } 46 | 47 | public String getComment() { 48 | return comment; 49 | } 50 | 51 | public void setComment(String comment) { 52 | this.comment = comment; 53 | } 54 | 55 | public Character getState() { 56 | return state; 57 | } 58 | 59 | public void setState(Character state) { 60 | this.state = state; 61 | } 62 | 63 | public List getItems() { 64 | return items; 65 | } 66 | 67 | public void setItems(List items) { 68 | this.items = items; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/ComprasProducto.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | @Table(name = "compras_productos") 7 | public class ComprasProducto { 8 | @EmbeddedId 9 | private ComprasProductoPK id; 10 | 11 | private Integer cantidad; 12 | private Double total; 13 | private Boolean estado; 14 | 15 | @ManyToOne 16 | @MapsId("idCompra") 17 | @JoinColumn(name = "id_compra", insertable = false, updatable = false) 18 | private Compra compra; 19 | 20 | @ManyToOne 21 | @JoinColumn(name = "id_producto", insertable = false, updatable = false) 22 | private Producto producto; 23 | 24 | public ComprasProductoPK getId() { 25 | return id; 26 | } 27 | 28 | public void setId(ComprasProductoPK id) { 29 | this.id = id; 30 | } 31 | 32 | public Integer getCantidad() { 33 | return cantidad; 34 | } 35 | 36 | public void setCantidad(Integer cantidad) { 37 | this.cantidad = cantidad; 38 | } 39 | 40 | public Double getTotal() { 41 | return total; 42 | } 43 | 44 | public void setTotal(Double total) { 45 | this.total = total; 46 | } 47 | 48 | public Boolean getEstado() { 49 | return estado; 50 | } 51 | 52 | public void setEstado(Boolean estado) { 53 | this.estado = estado; 54 | } 55 | 56 | public Compra getCompra() { 57 | return compra; 58 | } 59 | 60 | public void setCompra(Compra compra) { 61 | this.compra = compra; 62 | } 63 | 64 | public Producto getProducto() { 65 | return producto; 66 | } 67 | 68 | public void setProducto(Producto producto) { 69 | this.producto = producto; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/Cliente.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.*; 4 | import java.util.List; 5 | 6 | @Entity 7 | @Table(name="clientes") 8 | public class Cliente { 9 | 10 | @Id 11 | private String id; 12 | private String nombre; 13 | private String apellidos; 14 | private Long celular; 15 | private String direccion; 16 | 17 | @Column(name="correo_electronico") 18 | private String correoElectronico; 19 | 20 | @OneToMany(mappedBy = "cliente") 21 | private List compras; 22 | 23 | public String getId() { 24 | return id; 25 | } 26 | 27 | public void setId(String id) { 28 | this.id = id; 29 | } 30 | 31 | public String getNombre() { 32 | return nombre; 33 | } 34 | 35 | public void setNombre(String nombre) { 36 | this.nombre = nombre; 37 | } 38 | 39 | public String getApellidos() { 40 | return apellidos; 41 | } 42 | 43 | public void setApellidos(String apellidos) { 44 | this.apellidos = apellidos; 45 | } 46 | 47 | public Long getCelular() { 48 | return celular; 49 | } 50 | 51 | public void setCelular(Long celular) { 52 | this.celular = celular; 53 | } 54 | 55 | public String getDireccion() { 56 | return direccion; 57 | } 58 | 59 | public void setDireccion(String direccion) { 60 | this.direccion = direccion; 61 | } 62 | 63 | public String getCorreoElectronico() { 64 | return correoElectronico; 65 | } 66 | 67 | public void setCorreoElectronico(String correoElectronico) { 68 | this.correoElectronico = correoElectronico; 69 | } 70 | 71 | public List getCompras() { 72 | return compras; 73 | } 74 | 75 | public void setCompras(List compras) { 76 | this.compras = compras; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.controller; 2 | 3 | import com.platzi.market.domain.dto.AuthenticationRequest; 4 | import com.platzi.market.domain.dto.AuthenticationResponse; 5 | import com.platzi.market.domain.service.PlatziUserDetailsService; 6 | import com.platzi.market.web.security.JWTUtil; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.security.authentication.AuthenticationManager; 11 | import org.springframework.security.authentication.BadCredentialsException; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.userdetails.UserDetails; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | @RestController 20 | @RequestMapping("/auth") 21 | public class AuthController { 22 | 23 | @Autowired 24 | private AuthenticationManager authenticationManager; 25 | 26 | @Autowired 27 | private PlatziUserDetailsService platziUserDetailsService; 28 | 29 | @Autowired 30 | private JWTUtil jwtUtil; 31 | 32 | @PostMapping("/authenticate") 33 | public ResponseEntity createToken(@RequestBody AuthenticationRequest request) { 34 | try { 35 | authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())); 36 | UserDetails userDetails = platziUserDetailsService.loadUserByUsername(request.getUsername()); 37 | String jwt = jwtUtil.generateToken(userDetails); 38 | 39 | return new ResponseEntity<>(new AuthenticationResponse(jwt), HttpStatus.OK); 40 | } catch (BadCredentialsException e) { 41 | return new ResponseEntity<>(HttpStatus.FORBIDDEN); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/ProductoRepository.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence; 2 | 3 | import com.platzi.market.domain.Product; 4 | import com.platzi.market.domain.repository.ProductRepository; 5 | import com.platzi.market.persistence.crud.ProductoCrudRepository; 6 | import com.platzi.market.persistence.entity.Producto; 7 | import com.platzi.market.persistence.mapper.ProductMapper; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Repository; 10 | 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | @Repository 15 | public class ProductoRepository implements ProductRepository { 16 | @Autowired 17 | private ProductoCrudRepository productoCrudRepository; 18 | 19 | @Autowired 20 | private ProductMapper mapper; 21 | 22 | @Override 23 | public List getAll() { 24 | List productos = (List) productoCrudRepository.findAll(); 25 | return mapper.toProducts(productos); 26 | } 27 | 28 | @Override 29 | public Optional> getByCategory(int categoryId) { 30 | List productos = productoCrudRepository.findByIdCategoriaOrderByNombreAsc(categoryId); 31 | return Optional.of(mapper.toProducts(productos)); 32 | } 33 | 34 | @Override 35 | public Optional> getScarseProducts(int quantity) { 36 | Optional> productos = productoCrudRepository.findByCantidadStockLessThanAndEstado(quantity, true); 37 | return productos.map(prods -> mapper.toProducts(prods)); 38 | } 39 | 40 | @Override 41 | public Optional getProduct(int productId) { 42 | return productoCrudRepository.findById(productId).map(producto -> mapper.toProduct(producto)); 43 | } 44 | 45 | @Override 46 | public Product save(Product product) { 47 | Producto producto = mapper.toProducto(product); 48 | return mapper.toProduct(productoCrudRepository.save(producto)); 49 | } 50 | 51 | @Override 52 | public void delete(int productId) { 53 | productoCrudRepository.deleteById(productId); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/Producto.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | @Table(name = "productos") 7 | public class Producto { 8 | 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.IDENTITY) 11 | @Column(name = "id_producto") 12 | private Integer idProducto; 13 | 14 | private String nombre; 15 | 16 | @Column(name = "id_categoria") 17 | private Integer idCategoria; 18 | 19 | @Column(name = "codigo_barras") 20 | private String codigoBarras; 21 | 22 | @Column(name = "precio_venta") 23 | private Double precioVenta; 24 | 25 | @Column(name = "cantidad_stock") 26 | private Integer cantidadStock; 27 | 28 | private Boolean estado; 29 | 30 | @ManyToOne 31 | @JoinColumn(name = "id_categoria", insertable = false, updatable = false) 32 | private Categoria categoria; 33 | 34 | public Integer getIdProducto() { 35 | return idProducto; 36 | } 37 | 38 | public String getNombre() { 39 | return nombre; 40 | } 41 | 42 | public void setNombre(String nombre) { 43 | this.nombre = nombre; 44 | } 45 | 46 | public Integer getIdCategoria() { 47 | return idCategoria; 48 | } 49 | 50 | public void setIdCategoria(Integer idCategoria) { 51 | this.idCategoria = idCategoria; 52 | } 53 | 54 | public String getCodigoBarras() { 55 | return codigoBarras; 56 | } 57 | 58 | public void setCodigoBarras(String codigoBarras) { 59 | this.codigoBarras = codigoBarras; 60 | } 61 | 62 | public Double getPrecioVenta() { 63 | return precioVenta; 64 | } 65 | 66 | public void setPrecioVenta(Double precioVenta) { 67 | this.precioVenta = precioVenta; 68 | } 69 | 70 | public Integer getCantidadStock() { 71 | return cantidadStock; 72 | } 73 | 74 | public void setCantidadStock(Integer cantidadStock) { 75 | this.cantidadStock = cantidadStock; 76 | } 77 | 78 | public Boolean getEstado() { 79 | return estado; 80 | } 81 | 82 | public void setEstado(Boolean estado) { 83 | this.estado = estado; 84 | } 85 | 86 | public Categoria getCategoria() { 87 | return categoria; 88 | } 89 | 90 | public void setCategoria(Categoria categoria) { 91 | this.categoria = categoria; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/persistence/entity/Compra.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.persistence.entity; 2 | 3 | import javax.persistence.*; 4 | import java.time.LocalDateTime; 5 | import java.util.List; 6 | 7 | @Entity 8 | @Table(name = "compras") 9 | public class Compra { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | @Column(name = "id_compra") 14 | private Integer idCompra; 15 | 16 | @Column(name = "id_cliente") 17 | private String idCliente; 18 | 19 | private LocalDateTime fecha; 20 | 21 | @Column(name = "medio_pago") 22 | private Character medioPago; 23 | 24 | private String comentario; 25 | private Character estado; 26 | 27 | @ManyToOne 28 | @JoinColumn(name = "id_cliente", insertable = false, updatable = false) 29 | private Cliente cliente; 30 | 31 | @OneToMany(mappedBy = "compra", cascade = {CascadeType.ALL}) 32 | private List productos; 33 | 34 | public Integer getIdCompra() { 35 | return idCompra; 36 | } 37 | 38 | public String getIdCliente() { 39 | return idCliente; 40 | } 41 | 42 | public void setIdCliente(String idCliente) { 43 | this.idCliente = idCliente; 44 | } 45 | 46 | public LocalDateTime getFecha() { 47 | return fecha; 48 | } 49 | 50 | public void setFecha(LocalDateTime fecha) { 51 | this.fecha = fecha; 52 | } 53 | 54 | public Character getMedioPago() { 55 | return medioPago; 56 | } 57 | 58 | public void setMedioPago(Character medioPago) { 59 | this.medioPago = medioPago; 60 | } 61 | 62 | public String getComentario() { 63 | return comentario; 64 | } 65 | 66 | public void setComentario(String comentario) { 67 | this.comentario = comentario; 68 | } 69 | 70 | public Character getEstado() { 71 | return estado; 72 | } 73 | 74 | public void setEstado(Character estado) { 75 | this.estado = estado; 76 | } 77 | 78 | public Cliente getCliente() { 79 | return cliente; 80 | } 81 | 82 | public void setCliente(Cliente cliente) { 83 | this.cliente = cliente; 84 | } 85 | 86 | public List getProductos() { 87 | return productos; 88 | } 89 | 90 | public void setProductos(List productos) { 91 | this.productos = productos; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/security/filter/JwtFilterRequest.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.security.filter; 2 | 3 | import com.platzi.market.domain.service.PlatziUserDetailsService; 4 | import com.platzi.market.web.security.JWTUtil; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.filter.OncePerRequestFilter; 12 | 13 | import javax.servlet.FilterChain; 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | @Component 20 | public class JwtFilterRequest extends OncePerRequestFilter { 21 | 22 | @Autowired 23 | private JWTUtil jwtUtil; 24 | 25 | @Autowired 26 | private PlatziUserDetailsService platziUserDetailsService; 27 | 28 | @Override 29 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 30 | String authorizationHeader = request.getHeader("Authorization"); 31 | 32 | if (authorizationHeader != null && authorizationHeader.startsWith("Bearer")) { 33 | String jwt = authorizationHeader.substring(7); 34 | String username = jwtUtil.extractUsername(jwt); 35 | 36 | if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { 37 | UserDetails userDetails = platziUserDetailsService.loadUserByUsername(username); 38 | 39 | if (jwtUtil.validateToken(jwt, userDetails)) { 40 | UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); 41 | authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); 42 | 43 | SecurityContextHolder.getContext().setAuthentication(authToken); 44 | } 45 | } 46 | } 47 | 48 | filterChain.doFilter(request, response); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/security/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.security; 2 | 3 | import com.platzi.market.domain.service.PlatziUserDetailsService; 4 | import com.platzi.market.web.security.filter.JwtFilterRequest; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.config.http.SessionCreationPolicy; 14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 | 16 | @EnableWebSecurity 17 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 18 | 19 | @Autowired 20 | private PlatziUserDetailsService platziUserDetailsService; 21 | 22 | @Autowired 23 | private JwtFilterRequest jwtFilterRequest; 24 | 25 | @Override 26 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 27 | auth.userDetailsService(platziUserDetailsService); 28 | } 29 | 30 | @Override 31 | protected void configure(HttpSecurity http) throws Exception { 32 | http.csrf().disable().authorizeRequests().antMatchers("/**/authenticate").permitAll() 33 | .anyRequest().authenticated().and().sessionManagement() 34 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS); 35 | 36 | http.addFilterBefore(jwtFilterRequest, UsernamePasswordAuthenticationFilter.class); 37 | } 38 | 39 | @Override 40 | public void configure(WebSecurity web) throws Exception { 41 | web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", 42 | "/swagger-resources/**", "/configuration/security", 43 | "/swagger-ui.html", "/webjars/**"); 44 | } 45 | 46 | @Override 47 | @Bean 48 | public AuthenticationManager authenticationManagerBean() throws Exception { 49 | return super.authenticationManagerBean(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/platzi/market/web/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.platzi.market.web.controller; 2 | 3 | import com.platzi.market.domain.Product; 4 | import com.platzi.market.domain.service.ProductService; 5 | import io.swagger.annotations.ApiOperation; 6 | import io.swagger.annotations.ApiParam; 7 | import io.swagger.annotations.ApiResponse; 8 | import io.swagger.annotations.ApiResponses; 9 | import io.swagger.annotations.Authorization; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.util.List; 16 | import java.util.function.Predicate; 17 | 18 | @RestController 19 | @RequestMapping("/products") 20 | public class ProductController { 21 | @Autowired 22 | private ProductService productService; 23 | 24 | @GetMapping("/all") 25 | @ApiOperation(value = "Get all supermarket products", authorizations = { @Authorization(value="JWT") }) 26 | @ApiResponse(code = 200, message = "OK") 27 | public ResponseEntity> getAll() { 28 | return new ResponseEntity<>(productService.getAll(), HttpStatus.OK); 29 | } 30 | 31 | @GetMapping("/{id}") 32 | @ApiOperation(value = "Search a product with an ID", authorizations = { @Authorization(value="JWT") }) 33 | @ApiResponses({ 34 | @ApiResponse(code = 200, message = "OK"), 35 | @ApiResponse(code = 404, message = "Product not found"), 36 | }) 37 | public ResponseEntity getProduct(@ApiParam(value = "The id of the product", required = true, example = "7") 38 | @PathVariable("id") int productId) { 39 | return productService.getProduct(productId) 40 | .map(product -> new ResponseEntity<>(product, HttpStatus.OK)) 41 | .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); 42 | } 43 | 44 | @GetMapping("/category/{categoryId}") 45 | public ResponseEntity> getByCategory(@PathVariable("categoryId") int categoryId) { 46 | return productService.getByCategory(categoryId).filter(Predicate.not(List::isEmpty)) 47 | .map(products -> new ResponseEntity<>(products, HttpStatus.OK)) 48 | .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); 49 | } 50 | 51 | @PostMapping("/save") 52 | public ResponseEntity save(@RequestBody Product product) { 53 | return new ResponseEntity<>(productService.save(product), HttpStatus.CREATED); 54 | } 55 | 56 | @DeleteMapping("/delete/{id}") 57 | public ResponseEntity delete(@PathVariable("id") int productId) { 58 | if (productService.delete(productId)) { 59 | return new ResponseEntity(HttpStatus.OK); 60 | } else { 61 | return new ResponseEntity(HttpStatus.NOT_FOUND); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /platzi-market.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "40c17c8d-0fca-4096-92a8-dad39f55bdb6", 4 | "name": "platzi-market", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Get Products", 10 | "request": { 11 | "method": "GET", 12 | "header": [ 13 | { 14 | "key": "Authorization", 15 | "value": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGVqYW5kcm8iLCJpYXQiOjE1OTU2MTgzODgsImV4cCI6MTU5NTY1NDM4OH0.MNDlElN415W872NJ80KrHJiG40BZHloCOOAV3x5999g", 16 | "type": "text", 17 | "disabled": true 18 | } 19 | ], 20 | "url": { 21 | "raw": "http://localhost:8090/platzi-market/api/products/all", 22 | "protocol": "http", 23 | "host": [ 24 | "localhost" 25 | ], 26 | "port": "8090", 27 | "path": [ 28 | "platzi-market", 29 | "api", 30 | "products", 31 | "all" 32 | ] 33 | } 34 | }, 35 | "response": [] 36 | }, 37 | { 38 | "name": "Get a product", 39 | "request": { 40 | "method": "GET", 41 | "header": [], 42 | "url": { 43 | "raw": "http://localhost:8090/platzi-market/api/products/10", 44 | "protocol": "http", 45 | "host": [ 46 | "localhost" 47 | ], 48 | "port": "8090", 49 | "path": [ 50 | "platzi-market", 51 | "api", 52 | "products", 53 | "10" 54 | ] 55 | } 56 | }, 57 | "response": [] 58 | }, 59 | { 60 | "name": "Get products by category", 61 | "request": { 62 | "method": "GET", 63 | "header": [], 64 | "url": { 65 | "raw": "http://localhost:8090/platzi-market/api/products/category/3", 66 | "protocol": "http", 67 | "host": [ 68 | "localhost" 69 | ], 70 | "port": "8090", 71 | "path": [ 72 | "platzi-market", 73 | "api", 74 | "products", 75 | "category", 76 | "3" 77 | ] 78 | } 79 | }, 80 | "response": [] 81 | }, 82 | { 83 | "name": "Create product", 84 | "request": { 85 | "method": "POST", 86 | "header": [], 87 | "body": { 88 | "mode": "raw", 89 | "raw": "{\n \"name\": \"Lechuga\",\n \"categoryId\": 1,\n \"price\": 2000.0,\n \"stock\": 60,\n \"active\": true\n }", 90 | "options": { 91 | "raw": { 92 | "language": "json" 93 | } 94 | } 95 | }, 96 | "url": { 97 | "raw": "http://localhost:8090/platzi-market/api/products/save", 98 | "protocol": "http", 99 | "host": [ 100 | "localhost" 101 | ], 102 | "port": "8090", 103 | "path": [ 104 | "platzi-market", 105 | "api", 106 | "products", 107 | "save" 108 | ] 109 | } 110 | }, 111 | "response": [] 112 | }, 113 | { 114 | "name": "Delete product", 115 | "request": { 116 | "method": "DELETE", 117 | "header": [], 118 | "url": { 119 | "raw": "http://localhost:8090/platzi-market/api/products/delete/51", 120 | "protocol": "http", 121 | "host": [ 122 | "localhost" 123 | ], 124 | "port": "8090", 125 | "path": [ 126 | "platzi-market", 127 | "api", 128 | "products", 129 | "delete", 130 | "51" 131 | ] 132 | } 133 | }, 134 | "response": [] 135 | }, 136 | { 137 | "name": "Get all purchases", 138 | "request": { 139 | "method": "GET", 140 | "header": [], 141 | "url": { 142 | "raw": "http://localhost:8090/platzi-market/api/purchases/all", 143 | "protocol": "http", 144 | "host": [ 145 | "localhost" 146 | ], 147 | "port": "8090", 148 | "path": [ 149 | "platzi-market", 150 | "api", 151 | "purchases", 152 | "all" 153 | ] 154 | } 155 | }, 156 | "response": [] 157 | }, 158 | { 159 | "name": "Get purchases by client", 160 | "request": { 161 | "method": "GET", 162 | "header": [], 163 | "url": { 164 | "raw": "http://localhost:8090/platzi-market/api/purchases/client/4546221", 165 | "protocol": "http", 166 | "host": [ 167 | "localhost" 168 | ], 169 | "port": "8090", 170 | "path": [ 171 | "platzi-market", 172 | "api", 173 | "purchases", 174 | "client", 175 | "4546221" 176 | ] 177 | } 178 | }, 179 | "response": [] 180 | }, 181 | { 182 | "name": "Save purchase", 183 | "request": { 184 | "method": "POST", 185 | "header": [], 186 | "body": { 187 | "mode": "raw", 188 | "raw": "{\n \"clientId\": \"4546221\",\n \"date\": \"2000-12-31T13:40:00\",\n \"paymentMethod\": \"E\",\n \"comment\": \"\",\n \"state\": \"P\",\n \"items\": [\n {\n \"productId\": 1,\n \"quantity\": 10,\n \"total\": 3000.0,\n \"active\": true\n },\n {\n \"productId\": 3,\n \"quantity\": 20,\n \"total\": 14000.0,\n \"active\": true\n }\n ]\n }", 189 | "options": { 190 | "raw": { 191 | "language": "json" 192 | } 193 | } 194 | }, 195 | "url": { 196 | "raw": "http://localhost:8090/platzi-market/api/purchases/save", 197 | "protocol": "http", 198 | "host": [ 199 | "localhost" 200 | ], 201 | "port": "8090", 202 | "path": [ 203 | "platzi-market", 204 | "api", 205 | "purchases", 206 | "save" 207 | ] 208 | } 209 | }, 210 | "response": [] 211 | }, 212 | { 213 | "name": "Authenticate", 214 | "request": { 215 | "method": "POST", 216 | "header": [], 217 | "body": { 218 | "mode": "raw", 219 | "raw": "{\n \"username\" : \"alejandro\",\n \"password\": \"platzi\"\n}", 220 | "options": { 221 | "raw": { 222 | "language": "json" 223 | } 224 | } 225 | }, 226 | "url": { 227 | "raw": "http://localhost:8090/platzi-market/api/auth/authenticate", 228 | "protocol": "http", 229 | "host": [ 230 | "localhost" 231 | ], 232 | "port": "8090", 233 | "path": [ 234 | "platzi-market", 235 | "api", 236 | "auth", 237 | "authenticate" 238 | ] 239 | } 240 | }, 241 | "response": [] 242 | }, 243 | { 244 | "name": "Authenticate in Heroku", 245 | "request": { 246 | "method": "POST", 247 | "header": [], 248 | "body": { 249 | "mode": "raw", 250 | "raw": "{\n \"username\" : \"alejandro\",\n \"password\": \"platzi\"\n}", 251 | "options": { 252 | "raw": { 253 | "language": "json" 254 | } 255 | } 256 | }, 257 | "url": { 258 | "raw": "http://platzimarket.herokuapp.com/platzi-market/api/auth/authenticate", 259 | "protocol": "http", 260 | "host": [ 261 | "platzimarket", 262 | "herokuapp", 263 | "com" 264 | ], 265 | "path": [ 266 | "platzi-market", 267 | "api", 268 | "auth", 269 | "authenticate" 270 | ] 271 | } 272 | }, 273 | "response": [] 274 | }, 275 | { 276 | "name": "Get all Products in Heroku", 277 | "request": { 278 | "method": "GET", 279 | "header": [], 280 | "url": { 281 | "raw": "http://platzimarket.herokuapp.com/platzi-market/api/products/all", 282 | "protocol": "http", 283 | "host": [ 284 | "platzimarket", 285 | "herokuapp", 286 | "com" 287 | ], 288 | "path": [ 289 | "platzi-market", 290 | "api", 291 | "products", 292 | "all" 293 | ] 294 | } 295 | }, 296 | "response": [] 297 | } 298 | ], 299 | "protocolProfileBehavior": {} 300 | } --------------------------------------------------------------------------------