Skip to content
Open
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
18 changes: 8 additions & 10 deletions .github/workflows/continuous-delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2

- name: Modify application.yml
run: |
sed -i "s|^ *active:.*| active: default|" src/main/resources/application.yml
sed -i "s|^ *url:.*| url: ${{ secrets.DATASOURCE_URL }}|" src/main/resources/application.yml
sed -i "s|^ *username:.*| username: ${{ secrets.DATASOURCE_USERNAME }}|" src/main/resources/application.yml
sed -i "s|^ *password:.*| password: ${{ secrets.DATASOURCE_PASSWORD }}|" src/main/resources/application.yml
cat src/main/resources/application.yml

- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '17'

- name: Build with Maven
run: mvn clean install
env:
PGHOST: ${{ secrets.PGHOST }}
PGPORT: ${{ secrets.PGPORT }}
PGDATABASE: ${{ secrets.PGDATABASE }}
PGUSER: ${{ secrets.PGUSER }}
PGPASSWORD: ${{ secrets.PGPASSWORD }}
ISSUER_URI: ${{ secrets.ISSUER_URI }}
run: mvn clean install -Dspring.profiles.active=prd

- name: Build Docker image
run: |
Expand All @@ -49,7 +48,6 @@ jobs:
PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
HOST_NAME: ${{ secrets.EC2_HOST }}
USER_NAME: ${{ secrets.EC2_USERNAME }}

run: |
echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOST_NAME} '
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
jobs:
build:
runs-on: ubuntu-latest
env:
ISSUER_URI: https://test-issuer-uri
steps:
- name: Checkout project
uses: actions/checkout@v3
Expand All @@ -20,4 +22,4 @@ jobs:
java-version: '17'

- name: Build & Test
run: mvn clean install
run: mvn clean install -Dspring.profiles.active=dev
26 changes: 20 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,35 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
Expand All @@ -50,10 +63,7 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -64,6 +74,11 @@
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand All @@ -82,5 +97,4 @@
</plugin>
</plugins>
</build>

</project>
27 changes: 27 additions & 0 deletions src/main/java/com/gilberto/logistockapi/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.gilberto.logistockapi.config;

import com.gilberto.logistockapi.security.JwtConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.oauth2ResourceServer(oauth2 ->
oauth2.jwt(jwt ->
jwt.jwtAuthenticationConverter(new JwtConverter())
)
)
.build();
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/gilberto/logistockapi/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gilberto.logistockapi.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*");
}
}

Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package com.gilberto.logistockapi.controllers;

import com.gilberto.logistockapi.exceptions.ProductAlreadyRegisteredException;
import com.gilberto.logistockapi.exceptions.ProductNotFoundException;
import com.gilberto.logistockapi.exceptions.ProductStockExceededException;
import com.gilberto.logistockapi.exceptions.ProductStockUnderThanZeroException;
import com.gilberto.logistockapi.models.dto.SummaryProduct;
import com.gilberto.logistockapi.models.dto.request.ProductFilter;
import com.gilberto.logistockapi.models.dto.request.ProductForm;
import com.gilberto.logistockapi.models.dto.request.ProductUpdateForm;
import com.gilberto.logistockapi.models.dto.request.QuantityForm;
import com.gilberto.logistockapi.models.dto.response.ProductDTO;
import com.gilberto.logistockapi.services.IProductService;
import jakarta.validation.Valid;

import java.net.URI;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -24,76 +23,70 @@
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/product")
@RequestMapping("/api/v1/products")
public class ProductController {

private final IProductService productService;

public ProductController(@Autowired IProductService productService) {
this.productService = productService;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<ProductDTO> create(@RequestBody @Valid ProductForm productForm)
throws ProductAlreadyRegisteredException {
return ResponseEntity.created(URI.create(""))
.body(this.productService.create(productForm));
}

@GetMapping
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<List<ProductDTO>> listAll(@Valid ProductFilter filter) {
return ResponseEntity.ok(this.productService.listAll(filter));
}

@GetMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<ProductDTO> findById(@PathVariable Long id)
throws ProductNotFoundException {
return ResponseEntity.ok(this.productService.findById(id));
}

@GetMapping("/barcode/{barcode}")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<ProductDTO> findByBarCode(@PathVariable String barcode)
throws ProductNotFoundException {
return ResponseEntity.ok(this.productService.findByBarCode(barcode));
}

@PutMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<ProductDTO> updateById(@PathVariable Long id,
@RequestBody @Valid ProductUpdateForm updateForm)
throws ProductNotFoundException {
return ResponseEntity.ok(this.productService.updateById(id, updateForm));
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public ResponseEntity<Object> deleteById(@PathVariable Long id) throws ProductNotFoundException {
this.productService.delete(id);
return ResponseEntity.noContent().build();
}

@PatchMapping("/{id}/increase")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<ProductDTO> increaseStock(@PathVariable Long id,
@RequestBody @Valid QuantityForm quantityForm)
throws ProductNotFoundException, ProductStockExceededException {
return ResponseEntity.ok(this.productService.increaseStock(id, quantityForm));
}

@PatchMapping("/{id}/decrease")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<ProductDTO> decreaseStock(@PathVariable Long id,
@RequestBody @Valid QuantityForm quantityForm)
throws ProductStockUnderThanZeroException, ProductNotFoundException {
return ResponseEntity.ok(this.productService.decreaseStock(id, quantityForm));
}


private final IProductService productService;

@Autowired
public ProductController(IProductService productService) {
this.productService = productService;
}

@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<ProductDTO> create(@RequestBody @Valid ProductForm productForm) {
return ResponseEntity.created(URI.create(""))
.body(this.productService.create(productForm));
}

@GetMapping
@PreAuthorize("hasRole('USER')")
public ResponseEntity<Page<SummaryProduct>> listAll(@Valid ProductFilter filter) {
return ResponseEntity.ok(this.productService.listAll(filter));
}

@GetMapping("/{id}")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ProductDTO> findById(@PathVariable Long id) {
return ResponseEntity.ok(this.productService.findById(id));
}

@GetMapping("/barcode/{barcode}")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ProductDTO> findByCode(@PathVariable String code) {
return ResponseEntity.ok(this.productService.findByCode(code));
}

@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<ProductDTO> updateById(@PathVariable Long id,
@RequestBody @Valid ProductUpdateForm updateForm) {
return ResponseEntity.ok(this.productService.updateById(id, updateForm));
}

@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Object> deleteById(@PathVariable Long id) {
this.productService.delete(id);
return ResponseEntity.noContent().build();
}

@PatchMapping("/{id}/increase")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ProductDTO> increaseStock(@PathVariable Long id,
@RequestBody @Valid QuantityForm quantityForm) {
return ResponseEntity.ok(this.productService.increaseStock(id, quantityForm));
}

@PatchMapping("/{id}/decrease")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<ProductDTO> decreaseStock(@PathVariable Long id,
@RequestBody @Valid QuantityForm quantityForm) {
return ResponseEntity.ok(this.productService.decreaseStock(id, quantityForm));
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.gilberto.logistockapi.exceptions.handler;
package com.gilberto.logistockapi.controllers.handler;

import com.gilberto.logistockapi.exceptions.HttpException;
import com.gilberto.logistockapi.models.dto.response.ErrorDTO;
Expand All @@ -9,11 +9,11 @@

@RestControllerAdvice
public class HttpResponseHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(HttpException.class)
public ResponseEntity<ErrorDTO> handlerHttpException(HttpException exception) {
return ResponseEntity.status(exception.getStatus())
.body(new ErrorDTO(exception.getStatus().value(), exception.getMessage()));
}

@ExceptionHandler(HttpException.class)
public ResponseEntity<ErrorDTO> handlerHttpException(HttpException exception) {
return ResponseEntity.status(exception.getStatus())
.body(new ErrorDTO(exception.getStatus().value(), exception.getMessage()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import org.springframework.http.HttpStatus;

@Getter
public sealed class HttpException extends Exception permits ProductAlreadyRegisteredException,
ProductNotFoundException, ProductStockExceededException, ProductStockUnderThanZeroException {
public sealed class HttpException extends RuntimeException
permits
ProductAlreadyRegisteredException,
ProductNotFoundException,
ProductStockExceededException,
ProductStockUnderThanZeroException {

private HttpStatus status;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

public interface IProductMapper {

Product toProduct(ProductForm productForm);
Product toEntity(ProductForm productForm);

ProductDTO toProductDTO(Product product);
ProductDTO toDTO(Product product);

}
Loading