diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..868293e --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,22 @@ +name: Build Project +on: + pull_request: + branches: [ "main", "develop", "release" ] + +jobs: + Build: + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Build with Maven + env: + CONFIG_IP: ${{ secrets.DEV_INTEG_HOST }} + run: mvn clean install diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index f700652..8a79107 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -1,41 +1,16 @@ name: Deploy Develop on: - push: - branches: ["develop"] pull_request: - branches: ["develop"] + branches: + - develop + types: + - closed workflow_dispatch: jobs: - Build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Maven - run: sudo apt-get install -y maven - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: "21" - distribution: "temurin" - cache: maven - - name: Build with Maven - env: - DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} - DATASOURCE_URL: ${{ secrets.NEON_DATASOURCE_URL }}/${{ secrets.DB_NAME }} - DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }} - GOOGLE_ADMIN_CONFIG_TYPE: ${{ secrets.GOOGLE_ADMIN_CONFIG_TYPE }} - GOOGLE_ADMIN_CONFIG_PROJECT_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_PROJECT_ID }} - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY: ${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY }} - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID }} - GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL }} - GOOGLE_ADMIN_CONFIG_CLIENT_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_ID }} - GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL }} - run: mvn clean install - - deploy: + Deploy: name: Deploy on Develop + if: ${{ github.event.pull_request.merged == true }} runs-on: self-hosted - needs: Build steps: - name: executing remote ssh commands using ssh key uses: appleboy/ssh-action@master @@ -53,16 +28,8 @@ jobs: echo "Building Docker image..." docker build -t ${{ github.event.repository.name }} . echo "Creating .env file..." - echo "DATASOURCE_URL=${{ secrets.DEV_DATASOURCE_URL }}/${{ secrets.DB_NAME }}" >> .env - echo "DATASOURCE_USERNAME=${{ secrets.DATASOURCE_USERNAME }}" >> .env - echo "DATASOURCE_PASSWORD=${{ secrets.DATASOURCE_PASSWORD }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_TYPE=${{ secrets.GOOGLE_ADMIN_CONFIG_TYPE }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PROJECT_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_PROJECT_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PRIVATE_KEY=${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_CLIENT_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_X509_CERT_URL=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL }}" >> .env + echo "PROFILE=dev" >> .env + echo "CONFIG_IP=10.43.101.114" >> .env docker rm -f ${{ github.event.repository.name }} docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }} echo "Docker container running..." diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 0000000..efa963e --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,33 @@ +name: Deploy PROD +on: + push: + tags: + - '*' + workflow_dispatch: +jobs: + Deploy: + name: Deploy on PROD + runs-on: self-hosted + steps: + - name: executing remote ssh commands using ssh key + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.PROD_G2_HOST }} + username: ${{ secrets.HOSTS_USERNAME }} + key: ${{ secrets.PROD_G2_SSH_KEY }} + port: ${{ secrets.SSH_PORT }} + script: | + cd AllConnected/${{ github.event.repository.name }} + echo "Fetching latest code..." + git fetch + git checkout main + git pull + echo "Building Docker image..." + docker build -t ${{ github.event.repository.name }} . + echo "Creating .env file..." + echo "PROFILE=prod2" >> .env + echo "CONFIG_IP=10.43.101.72" >> .env + docker rm -f ${{ github.event.repository.name }} + docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }} + echo "Docker container running..." + rm .env diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 7612477..bf5ff75 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -1,41 +1,16 @@ name: Deploy QA on: - push: - branches: ["release"] pull_request: - branches: ["release"] + branches: + - release + types: + - closed workflow_dispatch: jobs: - Build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Maven - run: sudo apt-get install -y maven - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: "21" - distribution: "temurin" - cache: maven - - name: Build with Maven - env: - DATASOURCE_PASSWORD: ${{ secrets.DATASOURCE_PASSWORD }} - DATASOURCE_URL: ${{ secrets.NEON_DATASOURCE_URL }}/${{ secrets.DB_NAME }} - DATASOURCE_USERNAME: ${{ secrets.DATASOURCE_USERNAME }} - GOOGLE_ADMIN_CONFIG_TYPE: ${{ secrets.GOOGLE_ADMIN_CONFIG_TYPE }} - GOOGLE_ADMIN_CONFIG_PROJECT_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_PROJECT_ID }} - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY: ${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY }} - GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID }} - GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL }} - GOOGLE_ADMIN_CONFIG_CLIENT_ID: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_ID }} - GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL: ${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL }} - run: mvn clean install - Deploy: name: Deploy on QA + if: ${{ github.event.pull_request.merged == true }} runs-on: self-hosted - needs: Build steps: - name: executing remote ssh commands using ssh key uses: appleboy/ssh-action@master @@ -53,17 +28,9 @@ jobs: echo "Building Docker image..." docker build -t ${{ github.event.repository.name }} . echo "Creating .env file..." - echo "DATASOURCE_URL=${{ secrets.QA_DATASOURCE_G2_URL }}/${{ secrets.DB_NAME }}" >> .env - echo "DATASOURCE_USERNAME=${{ secrets.DATASOURCE_USERNAME }}" >> .env - echo "DATASOURCE_PASSWORD=${{ secrets.DATASOURCE_PASSWORD }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_TYPE=${{ secrets.GOOGLE_ADMIN_CONFIG_TYPE }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PROJECT_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_PROJECT_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PRIVATE_KEY=${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_PRIVATE_KEY_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_EMAIL }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_CLIENT_ID=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_ID }}" >> .env - echo "GOOGLE_ADMIN_CONFIG_X509_CERT_URL=${{ secrets.GOOGLE_ADMIN_CONFIG_CLIENT_X509_CERT_URL }}" >> .env + echo "PROFILE=qa2" >> .env + echo "CONFIG_IP=10.43.100.223" >> .env docker rm -f ${{ github.event.repository.name }} docker run --name ${{ github.event.repository.name }} --network all_connected -d -p ${{ secrets.SERVICE_PORT }}:8080 --env-file .env ${{ github.event.repository.name }} echo "Docker container running..." - rm .env \ No newline at end of file + rm .env diff --git a/pom.xml b/pom.xml index 0f4fc77..0dd9cb7 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,14 @@ org.springframework.cloud spring-cloud-starter-netflix-eureka-client - + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + org.springframework.boot spring-boot-devtools diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/config/WebConfig.java b/src/main/java/co/allconnected/fussiontech/productsservice/config/WebConfig.java deleted file mode 100644 index 8375cc0..0000000 --- a/src/main/java/co/allconnected/fussiontech/productsservice/config/WebConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package co.allconnected.fussiontech.productsservice.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("/**") - .allowedOriginPatterns("*") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(true); - } -} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/controllers/LabelsController.java b/src/main/java/co/allconnected/fussiontech/productsservice/controllers/LabelsController.java new file mode 100644 index 0000000..3ee4e60 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/controllers/LabelsController.java @@ -0,0 +1,78 @@ +package co.allconnected.fussiontech.productsservice.controllers; + +import co.allconnected.fussiontech.productsservice.dtos.LabelDTO; +import co.allconnected.fussiontech.productsservice.dtos.Response; +import co.allconnected.fussiontech.productsservice.services.LabelService; +import co.allconnected.fussiontech.productsservice.utils.OperationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/labels") +public class LabelsController { + private final LabelService labelService; + + @Autowired + public LabelsController(LabelService labelService) { + this.labelService = labelService; + } + + @PostMapping + public ResponseEntity createLabel(@RequestParam String name) { + try { + if (name == null || name.trim().isEmpty()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); // Manejo del error si 'name' es nulo o vacĂ­o + } + LabelDTO labelDTO = labelService.createLabel(name); + return ResponseEntity.status(HttpStatus.CREATED).body(labelDTO); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + } + @PutMapping("/{id}") + public ResponseEntity updateLabel(@PathVariable String id, @RequestParam String name) { + try { + return ResponseEntity.status(HttpStatus.OK).body(labelService.updateLabel(id, name)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @GetMapping("/{id}") + public ResponseEntity getLabel(@PathVariable String id) { + try { + return ResponseEntity.status(HttpStatus.OK).body(labelService.getLabel(id)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + + @GetMapping() + public ResponseEntity getLabels() { + try { + LabelDTO[] listLabelsDTO = labelService.getLabels(); + return ResponseEntity.status(HttpStatus.OK).body(listLabelsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteLabel(@PathVariable String id) { + try { + labelService.deleteLabel(id); + return ResponseEntity.status(HttpStatus.OK).body(new Response(HttpStatus.OK.value(), "Label deleted")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/controllers/ProductsController.java b/src/main/java/co/allconnected/fussiontech/productsservice/controllers/ProductsController.java new file mode 100644 index 0000000..9c846eb --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/controllers/ProductsController.java @@ -0,0 +1,243 @@ +package co.allconnected.fussiontech.productsservice.controllers; + +import co.allconnected.fussiontech.productsservice.dtos.*; +import co.allconnected.fussiontech.productsservice.services.ProductService; +import co.allconnected.fussiontech.productsservice.utils.OperationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/api/v1/products") +public class ProductsController { + private final ProductService productService; + + @Autowired + public ProductsController(ProductService productService) { + this.productService = productService; + } + /* + CRUD PRODUCTS + */ + @PostMapping + public ResponseEntity createProduct(@ModelAttribute ProductCreateDTO product, @RequestParam(value = "photo", required = false) MultipartFile photo) { + try { + System.out.println(product.idBusiness()); + ProductDTO productDTO = productService.createProduct(product, photo); + return ResponseEntity.status(HttpStatus.CREATED).body(productDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @PutMapping("/{id}") + public ResponseEntity updateProduct(@PathVariable String id, @ModelAttribute ProductCreateDTO product, @RequestParam(value = "photo", required = false) MultipartFile photo) { + try { + return ResponseEntity.status(HttpStatus.OK).body(productService.updateProduct(id, product, photo)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @GetMapping("/{id}") + public ResponseEntity getProduct(@PathVariable String id) { + try { + return ResponseEntity.status(HttpStatus.OK).body(productService.getProduct(id)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @GetMapping("/businesses/{id_business}") + public ResponseEntity getProductsByBusiness(@PathVariable String id_business) { + try { + ProductDTO[] listProductsDTO = productService.getProductsByBusiness(id_business); + return ResponseEntity.status(HttpStatus.OK).body(listProductsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + @GetMapping + public ResponseEntity getProducts() { + try { + ProductDTO[] listProductsDTO = productService.getProducts(); + return ResponseEntity.status(HttpStatus.OK).body(listProductsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteProduct(@PathVariable String id) { + try { + productService.deleteProduct(id); + return ResponseEntity.status(HttpStatus.OK).body(new Response(HttpStatus.OK.value(), "Product deleted")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + /* + OPERATIONS PRODUCTS - LABELS + */ + @PostMapping("/{id_product}/labels/{id_label}") + public ResponseEntity addLabel(@PathVariable String id_product, @PathVariable String id_label) { + try { + productService.assignLabelToProduct(id_product, id_label); + return ResponseEntity.status(HttpStatus.OK) + .body(new Response(HttpStatus.OK.value(), "Label assigned to product successfully.")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @DeleteMapping("/{id_product}/labels/{id_label}/delete") + public ResponseEntity deleteLabel(@PathVariable String id_product, @PathVariable String id_label) { + try { + productService.deleteLabelFromProduct(id_product, id_label); + return ResponseEntity.status(HttpStatus.OK) + .body(new Response(HttpStatus.OK.value(), "Label deleted from product successfully.")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + /* + OPERATIONS PRODUCTS - REPORTS + */ + @PostMapping("/{id_product}/report") + public ResponseEntity addReport(@PathVariable String id_product, @RequestBody ReportedProductCreateDTO reportedProductCreateDTO){ + try { + ReportedProductDTO reportedProductDTO = productService.reportProduct(id_product, reportedProductCreateDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(reportedProductDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @PutMapping("/{id_product}/report") + public ResponseEntity updateReport(@PathVariable String id_product, @RequestBody ReportedProductCreateDTO reportedProductCreateDTO){ + try { + ReportedProductDTO reportedProductDTO = productService.updateProductReport(id_product, reportedProductCreateDTO); + return ResponseEntity.status(HttpStatus.OK).body(reportedProductDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @DeleteMapping("/{id_product}/report") + public ResponseEntity deleteReport(@PathVariable String id_product){ + try { + productService.deleteReport(id_product); + return ResponseEntity.status(HttpStatus.OK).body(new Response(HttpStatus.OK.value(), "Report deleted")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @GetMapping("/{id_product}/report") + public ResponseEntity getReport(@PathVariable String id_product){ + try { + return ResponseEntity.status(HttpStatus.OK).body(productService.getReport(id_product)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + @GetMapping("/reports") + public ResponseEntity getReports(){ + try { + ReportedProductDTO[] listReportsDTO = productService.getReports(); + if (listReportsDTO.length == 0) + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new Response(HttpStatus.NOT_FOUND.value(), "No reports found")); + return ResponseEntity.status(HttpStatus.OK).body(listReportsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + /* + RATING OPERATIONS + */ + @PostMapping("/rating/{id_product}") + public ResponseEntity addRating(@PathVariable String id_product, @RequestBody RatingCreateDTO ratingCreateDTO){ + try { + /* + System.out.println(ratingCreateDTO.userId()); + System.out.println(ratingCreateDTO.productId()); + System.out.println(ratingCreateDTO.rating()); + + */ + RatingDTO ratingDTO = productService.rateProduct(id_product, ratingCreateDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(ratingDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } + + @GetMapping("/rating") + public ResponseEntity getRatings(){ + try { + RatingDTO[] listRatingsDTO = productService.getAllRating(); + return ResponseEntity.status(HttpStatus.OK).body(listRatingsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + + @GetMapping("/rating/{id_product}/average") + public ResponseEntity getRating(@PathVariable String id_product){ + try { + float rating = productService.getAverageRating(id_product); + return ResponseEntity.status(HttpStatus.OK).body(new Response(HttpStatus.OK.value(), "Average rating: " + rating)); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + + @GetMapping("/rating/{id_product}") + public ResponseEntity getRatingsByProduct(@PathVariable String id_product){ + try { + RatingDTO[] listRatingsDTO = productService.getRatingByProduct(id_product); + return ResponseEntity.status(HttpStatus.OK).body(listRatingsDTO); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage())); + } + } + @DeleteMapping("/rating/{id_rating}") + public ResponseEntity deleteRating (@PathVariable String id_rating){ + try { + productService.deleteRating(id_rating); + return ResponseEntity.status(HttpStatus.OK).body(new Response(HttpStatus.OK.value(), "Rating deleted")); + } catch (OperationException e) { + return ResponseEntity.status(e.getCode()).body(new Response(e.getCode(), e.getMessage())); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Unexpected error occurred: " + e.getMessage())); + } + } +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/controllers/delete.txt b/src/main/java/co/allconnected/fussiontech/productsservice/controllers/delete.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/LabelDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/LabelDTO.java new file mode 100644 index 0000000..9d41bf2 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/LabelDTO.java @@ -0,0 +1,29 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +import co.allconnected.fussiontech.productsservice.model.Label; +import co.allconnected.fussiontech.productsservice.model.Product; +import lombok.Getter; +import lombok.Setter; +import java.io.Serializable; + +@Getter +@Setter + +public class LabelDTO implements Serializable { + Integer id; + String label; + String [] products; + + public LabelDTO(){ + + } + + public LabelDTO(Label label){ + this.id = label.getId(); + this.label = label.getLabel(); + this.products = label.getProducts().stream().map(Product::getName).toArray(String[]::new); + } + + + +} \ No newline at end of file diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductCreateDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductCreateDTO.java new file mode 100644 index 0000000..07dfcb9 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductCreateDTO.java @@ -0,0 +1,6 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +import java.util.UUID; + +public record ProductCreateDTO (UUID idBusiness, String name, String description, Integer stock, Double price, String status) { +} \ No newline at end of file diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductDTO.java new file mode 100644 index 0000000..9856223 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ProductDTO.java @@ -0,0 +1,33 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +import co.allconnected.fussiontech.productsservice.model.Label; +import co.allconnected.fussiontech.productsservice.model.Product; +import lombok.Getter; +import lombok.Setter; +import java.io.Serializable; + +@Getter +@Setter +public class ProductDTO implements Serializable { + private Integer id; + private String idBusiness; + private String name; + private String description; + private String photoUrl; + private Integer stock; + private Double price; + private String status; + private String [] labels; + + public ProductDTO(Product product){ + this.id = product.getId(); + this.idBusiness = product.getIdBusiness().toString(); + this.name = product.getName(); + this.description = product.getDescription(); + this.photoUrl = product.getPhotoUrl(); + this.stock = product.getStock(); + this.price = product.getPrice(); + this.status = product.getStatus(); + this.labels = product.getLabels().stream().map(Label::getLabel).toArray(String[]::new); + } +} \ No newline at end of file diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingCreateDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingCreateDTO.java new file mode 100644 index 0000000..d38e72c --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingCreateDTO.java @@ -0,0 +1,4 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +public record RatingCreateDTO(Integer productId, String userId, Integer rating, String comment) { +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingDTO.java new file mode 100644 index 0000000..bb44e5c --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/RatingDTO.java @@ -0,0 +1,29 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +import co.allconnected.fussiontech.productsservice.model.Rating; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import java.io.Serializable; +import java.time.Instant; + +@Getter +@Setter +@NoArgsConstructor +public class RatingDTO implements Serializable { + private Integer idRating; + private Integer productId; + private String userId; + private Integer rating; + private String comment; + private Instant date; + + public RatingDTO(Rating rating){ + this.idRating = rating.getId(); + this.productId = rating.getIdProduct().getId(); + this.userId = rating.getIdUser(); + this.rating = rating.getRating(); + this.comment = rating.getComment(); + this.date = rating.getDate(); + } +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductCreateDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductCreateDTO.java new file mode 100644 index 0000000..0011ea8 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductCreateDTO.java @@ -0,0 +1,4 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +public record ReportedProductCreateDTO (String reason, String description) { +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductDTO.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductDTO.java new file mode 100644 index 0000000..1d39780 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/ReportedProductDTO.java @@ -0,0 +1,24 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +import co.allconnected.fussiontech.productsservice.model.ReportedProduct; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.time.Instant; + +@Getter +@Setter +public class ReportedProductDTO implements Serializable { + private Integer productId; + private String reason; + private String description; + private Instant reportDate; + + public ReportedProductDTO (ReportedProduct reportedProduct){ + this.productId = reportedProduct.getProduct().getId(); + this.reason = reportedProduct.getReason(); + this.description = reportedProduct.getDescription(); + this.reportDate = reportedProduct.getReportDate(); + } +} \ No newline at end of file diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/Response.java b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/Response.java new file mode 100644 index 0000000..ac7a347 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/Response.java @@ -0,0 +1,4 @@ +package co.allconnected.fussiontech.productsservice.dtos; + +public record Response(int code, String message) { +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/dtos/delete.txt b/src/main/java/co/allconnected/fussiontech/productsservice/dtos/delete.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/model/Label.java b/src/main/java/co/allconnected/fussiontech/productsservice/model/Label.java new file mode 100644 index 0000000..2d3fa62 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/model/Label.java @@ -0,0 +1,35 @@ +package co.allconnected.fussiontech.productsservice.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.LinkedHashSet; +import java.util.Set; + +@Getter +@Setter +@Entity +@NoArgsConstructor +@Table(name = "label", schema = "all_connected_products") +public class Label { + public Label(String label) { + this.label = label; + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id_label", nullable = false) + private Integer id; + + @Column(name = "label", nullable = false, length = 45) + private String label; + + @ManyToMany + @JoinTable(name = "product_label", + schema = "all_connected_products", // Agrega el esquema explĂ­citamente + joinColumns = @JoinColumn(name = "id_label"), + inverseJoinColumns = @JoinColumn(name = "id_announcement")) + private Set products = new LinkedHashSet<>(); +} diff --git a/src/main/java/co/allconnected/fussiontech/productsservice/model/Product.java b/src/main/java/co/allconnected/fussiontech/productsservice/model/Product.java new file mode 100644 index 0000000..44ba4c7 --- /dev/null +++ b/src/main/java/co/allconnected/fussiontech/productsservice/model/Product.java @@ -0,0 +1,58 @@ +package co.allconnected.fussiontech.productsservice.model; + +import co.allconnected.fussiontech.productsservice.dtos.ProductCreateDTO; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + +@Getter +@Setter +@Entity +@NoArgsConstructor +@Table(name = "product", schema = "all_connected_products") +public class Product { + public Product (ProductCreateDTO dto) { + this.idBusiness = dto.idBusiness(); + this.name = dto.name(); + this.description = dto.description(); + this.stock = dto.stock(); + this.price = dto.price(); + this.status = dto.status(); + } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id_product", nullable = false) + private Integer id; + + @Column(name = "id_business", nullable = false) + private UUID idBusiness; + + @Column(name = "name", nullable = false, length = 45) + private String name; + + @Column(name = "description", nullable = false, length = 280) + private String description; + + @Column(name = "photo_url", length = 700) + private String photoUrl; + + @Column(name = "stock", nullable = false) + private Integer stock; + + @Column(name = "price", nullable = false) + private Double price; + + @Column(name = "status", nullable = false, length = Integer.MAX_VALUE) + private String status; + + @ManyToMany(mappedBy = "products") + private Set