From 781ef99e869da0888a216a21ca7a82eb43c8bbe8 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Tue, 28 May 2024 17:00:59 +0200 Subject: [PATCH 1/3] feat: initial commit --- .../DataInjectionCommandLineRunner.java | 19 +++ .../common/security/SecurityConfig.java | 15 ++- .../backend/common/util/VariablesService.java | 8 ++ .../controller/ErpAdapterController.java | 68 +++++++++++ .../domain/model/ErpAdapterRequest.java | 57 +++++++++ .../ErpAdapterRequestRepository.java | 31 +++++ .../service/ErpAdapterRequestService.java | 57 +++++++++ .../service/ItemStockErpAdapterService.java | 112 ++++++++++++++++++ .../logic/adapter/ItemStockSammMapper.java | 60 ++++++++++ 9 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java index c9126c35..0de37b95 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java @@ -25,6 +25,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.util.VariablesService; +import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erp_adapter.logic.service.ErpAdapterRequestService; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.MaterialPartnerRelation; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; @@ -37,6 +39,7 @@ import org.eclipse.tractusx.puris.backend.stock.domain.model.ReportedMaterialItemStock; import org.eclipse.tractusx.puris.backend.stock.domain.model.ReportedProductItemStock; import org.eclipse.tractusx.puris.backend.common.domain.model.measurement.ItemUnitEnumeration; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; import org.eclipse.tractusx.puris.backend.stock.logic.service.MaterialItemStockService; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductItemStockService; import org.eclipse.tractusx.puris.backend.stock.logic.service.ReportedMaterialItemStockService; @@ -76,6 +79,9 @@ public class DataInjectionCommandLineRunner implements CommandLineRunner { @Autowired private VariablesService variablesService; + @Autowired + private ErpAdapterRequestService erpAdapterRequestService; + private ObjectMapper objectMapper; private final String semiconductorMatNbrCustomer = "MNR-7307-AU340474.002"; @@ -241,6 +247,19 @@ private void setupCustomerRole() throws JsonProcessingException { .locationBpna(supplierPartner.getSites().first().getAddresses().first().getBpna()) .build(); reportedProductItemStockService.create(reportedProductItemStock); + + // TODO: remove mock + ErpAdapterRequest mockRequest = ErpAdapterRequest + .builder() + .partnerBpnl(supplierPartner.getBpnl()) + .requestDate(new Date(System.currentTimeMillis()-3*60*60*1000)) + .ownMaterialNumber(semiconductorMaterial.getOwnMaterialNumber()) + .directionCharacteristic(DirectionCharacteristic.INBOUND) + .requestType("itemstock") + .sammVersion("2.0") + .build(); + mockRequest = erpAdapterRequestService.create(mockRequest); + log.info("Created mocked ErpAdapterRequest: \n" + mockRequest); } /** diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java index 6bdc8372..9299895c 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java @@ -83,7 +83,20 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests( // any request in spring context (authorizeHttpRequests) -> authorizeHttpRequests - .requestMatchers("/stockView/**", "/partners/**", "/materials/**", "/materialpartnerrelations/**", "/item-stock/**", "/production/**", "/delivery/**", "/demand/**", "/planned-production/**", "/material-demand/**", "/delivery-information/**", "/edc/**", "/parttypeinformation/**") + .requestMatchers("/stockView/**", + "/partners/**", + "/materials/**", + "/materialpartnerrelations/**", + "/item-stock/**", + "/production/**", + "/delivery/**", + "/demand/**", + "/planned-production/**", + "/material-demand/**", + "/delivery-information/**", + "/edc/**", + "/erp_adapter/**", + "/parttypeinformation/**") .authenticated() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/health/**").permitAll() .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll() diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java index ff2b0a9c..bcc64055 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java @@ -56,6 +56,14 @@ public class VariablesService { */ private String edrEndpoint; + + /** + * The endpoint of your ERP adapter + * (currently just mock!) + */ +// @Value("${puris.erpadapterurl") + private String erpAdapterUrl ="http:foo.com/my_erp_endpoint"; + @Value("${puris.baseurl}" + "catena/item-stock/request") /** * The url under which this application's request endpoint can diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java new file mode 100644 index 00000000..983bd3a9 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erp_adapter.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.extern.slf4j.Slf4j; + +import org.eclipse.tractusx.puris.backend.erp_adapter.logic.service.ItemStockErpAdapterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.UUID; + +@RestController +@RequestMapping("erp_adapter") +@Slf4j +public class ErpAdapterController { + + @Autowired + private ItemStockErpAdapterService itemStockErpAdapterService; + + @PutMapping + public ResponseEntity putMethod( + @RequestParam("request_id") UUID requestId, + @RequestParam("bpnl") String partnerBpnl, + @RequestParam("response_type") String responseType, + @RequestParam("samm_version") String sammVersion, + @RequestParam(value = "response_timestamp") + @Parameter(example = "2024-05-28T15:00:00") + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") Date responseTimestamp, + @RequestBody JsonNode requestBody + ) { + Dto dto = new Dto(requestId, partnerBpnl, responseType, sammVersion, responseTimestamp, requestBody); + switch (responseType.toLowerCase()) { + case "itemstock" -> itemStockErpAdapterService.receiveItemStockUpdate(dto); + default -> { + return ResponseEntity.status(501).body("Unsupported response type: " + responseType); + } + } + + return ResponseEntity.ok("ok"); + } + + public record Dto(UUID requestId, String partnerBpnl, String responseType, String sammVersion, + Date responseTimeStamp, JsonNode responseBody ){} +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java new file mode 100644 index 00000000..f48bb147 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erp_adapter.domain.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.*; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; + +import java.util.Date; +import java.util.UUID; + +@Entity +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class ErpAdapterRequest { + @GeneratedValue + @Id + private UUID id; + + private String partnerBpnl; + + private String requestType; + + private String sammVersion; + + private Date requestDate; + + private Date responseReceivedDate; + + private String ownMaterialNumber; + + private DirectionCharacteristic directionCharacteristic; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java new file mode 100644 index 00000000..510f0334 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erp_adapter.domain.repository; + +import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface ErpAdapterRequestRepository extends JpaRepository { +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java new file mode 100644 index 00000000..a8de48e5 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erp_adapter.logic.service; + +import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erp_adapter.domain.repository.ErpAdapterRequestRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public class ErpAdapterRequestService { + + @Autowired + private ErpAdapterRequestRepository repository; + + public ErpAdapterRequest create(ErpAdapterRequest erpAdapterRequest) { + if (erpAdapterRequest.getId() != null && repository.existsById(erpAdapterRequest.getId())) { + return null; + } + return repository.save(erpAdapterRequest); + } + + public ErpAdapterRequest get(UUID id) { + return repository.findById(id).orElse(null); + } + + public ErpAdapterRequest update(ErpAdapterRequest erpAdapterRequest) { + if (repository.existsById(erpAdapterRequest.getId())) { + return repository.save(erpAdapterRequest); + } + return null; + } + + public void delete(UUID id) { + repository.deleteById(id); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java new file mode 100644 index 00000000..ad48141c --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erp_adapter.logic.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.puris.backend.erp_adapter.controller.ErpAdapterController; +import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.stock.domain.model.MaterialItemStock; +import org.eclipse.tractusx.puris.backend.stock.domain.model.ProductItemStock; +import org.eclipse.tractusx.puris.backend.stock.logic.adapter.ItemStockSammMapper; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.ItemStockSamm; +import org.eclipse.tractusx.puris.backend.stock.logic.service.MaterialItemStockService; +import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductItemStockService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +@Service +@Slf4j +public class ItemStockErpAdapterService { + + private ObjectMapper mapper = new ObjectMapper(); + + @Autowired + private ErpAdapterRequestService erpAdapterRequestService; + + @Autowired + private MaterialPartnerRelationService mprService; + + @Autowired + private PartnerService partnerService; + + @Autowired + private MaterialService materialService; + + @Autowired + private ItemStockSammMapper sammMapper; + + @Autowired + private MaterialItemStockService materialItemStockService; + @Autowired + private ProductItemStockService productItemStockService; + + + public void receiveItemStockUpdate(ErpAdapterController.Dto dto) { + try { + ItemStockSamm samm = mapper.treeToValue(dto.responseBody(), ItemStockSamm.class); + Partner partner = partnerService.findByBpnl(dto.partnerBpnl()); + ErpAdapterRequest request = erpAdapterRequestService.get(dto.requestId()); + Material material = materialService.findByOwnMaterialNumber(request.getOwnMaterialNumber()); + + switch (samm.getDirection()) { + case INBOUND -> { + var mpr = mprService.find(material, partner); + if (mpr == null || !mpr.isPartnerSuppliesMaterial()) { + log.error("Partner {} is not registered as supplier for {}", partner.getBpnl(), material.getOwnMaterialNumber()); + return; + } + List materialItemStockList = sammMapper.erpSammToMaterialItemStock(samm, partner, material); + materialItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> materialItemStockService.delete(stock.getUuid())); + materialItemStockList.forEach(stock -> materialItemStockService.create(stock)); + log.info("Inserted {} MaterialItemStocks for {} and {}", materialItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); + request.setResponseReceivedDate(new Date()); + erpAdapterRequestService.update(request); + } + case OUTBOUND -> { + var mpr = mprService.find(material, partner); + if (mpr == null || !mpr.isPartnerBuysMaterial()) { + log.error("Partner {} is not registered as customer for {}", partner.getBpnl(), material.getOwnMaterialNumber()); + return; + } + List productItemStockList = sammMapper.erpSammToProductItemStock(samm, partner, material); + productItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> productItemStockService.delete(stock.getUuid())); + productItemStockList.forEach(stock -> productItemStockService.create(stock)); + log.info("Inserted {} ProductItemStocks for {} and {}", productItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); + request.setResponseReceivedDate(new Date()); + erpAdapterRequestService.update(request); + } + default -> throw new IllegalArgumentException("Invalid direction: " + samm.getDirection()); + } + + } catch (Exception e) { + log.error("Error while receiving erp itemstock update", e); + } + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java index d8ea0471..d7563927 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/adapter/ItemStockSammMapper.java @@ -105,6 +105,66 @@ private ItemStockSamm listToItemStockSamm(List itemStocks, private record PositionsMappingHelper(String customerOrderId, String supplierOrderId, String customerOrderPositionId) { } + public List erpSammToMaterialItemStock(ItemStockSamm samm, Partner partner, Material material) { + ArrayList materialItemStocks = new ArrayList<>(); + for (var position : samm.getPositions()) { + String supplierOrderId = null, customerOrderPositionId = null, customerOrderId = null; + if (position.getOrderPositionReference() != null) { + supplierOrderId = position.getOrderPositionReference().getSupplierOrderId(); + customerOrderId = position.getOrderPositionReference().getCustomerOrderId(); + customerOrderPositionId = position.getOrderPositionReference().getCustomerOrderPositionId(); + } + for (var allocatedStock : position.getAllocatedStocks()) { + var builder = MaterialItemStock.builder(); + var itemStock = builder + .partner(partner) + .material(material) + .isBlocked(allocatedStock.getIsBlocked()) + .locationBpna(allocatedStock.getStockLocationBPNA()) + .locationBpns(allocatedStock.getStockLocationBPNS()) + .lastUpdatedOnDateTime(allocatedStock.getLastUpdatedOnDateTime()) + .customerOrderId(customerOrderId) + .supplierOrderId(supplierOrderId) + .customerOrderPositionId(customerOrderPositionId) + .measurementUnit(allocatedStock.getQuantityOnAllocatedStock().getUnit()) + .quantity(allocatedStock.getQuantityOnAllocatedStock().getValue()) + .build(); + materialItemStocks.add(itemStock); + } + } + return materialItemStocks; + } + + public List erpSammToProductItemStock(ItemStockSamm samm, Partner partner, Material material) { + ArrayList productItemStocks = new ArrayList<>(); + for (var position : samm.getPositions()) { + String supplierOrderId = null, customerOrderPositionId = null, customerOrderId = null; + if (position.getOrderPositionReference() != null) { + supplierOrderId = position.getOrderPositionReference().getSupplierOrderId(); + customerOrderId = position.getOrderPositionReference().getCustomerOrderId(); + customerOrderPositionId = position.getOrderPositionReference().getCustomerOrderPositionId(); + } + for (var allocatedStock : position.getAllocatedStocks()) { + var builder = ProductItemStock.builder(); + var itemStock = builder + .partner(partner) + .material(material) + .isBlocked(allocatedStock.getIsBlocked()) + .locationBpna(allocatedStock.getStockLocationBPNA()) + .locationBpns(allocatedStock.getStockLocationBPNS()) + .lastUpdatedOnDateTime(allocatedStock.getLastUpdatedOnDateTime()) + .customerOrderId(customerOrderId) + .supplierOrderId(supplierOrderId) + .customerOrderPositionId(customerOrderPositionId) + .measurementUnit(allocatedStock.getQuantityOnAllocatedStock().getUnit()) + .quantity(allocatedStock.getQuantityOnAllocatedStock().getValue()) + .build(); + productItemStocks.add(itemStock); + } + } + return productItemStocks; + } + public List itemStockSammToReportedProductItemStock(ItemStockSamm samm, Partner partner) { From edf68db1cdf95fe97036aad6edb571048c34cb7a Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Wed, 29 May 2024 11:00:45 +0200 Subject: [PATCH 2/3] fix: review issues --- .../DataInjectionCommandLineRunner.java | 8 +- .../common/security/SecurityConfig.java | 2 +- .../controller/ErpAdapterController.java | 68 ------------- .../controller/ErpAdapterController.java | 97 +++++++++++++++++++ .../domain/model/ErpAdapterRequest.java | 14 ++- .../ErpAdapterRequestRepository.java | 4 +- .../service/ErpAdapterRequestService.java | 10 +- .../service/ItemStockErpAdapterService.java | 58 ++++++++--- 8 files changed, 170 insertions(+), 91 deletions(-) delete mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java rename backend/src/main/java/org/eclipse/tractusx/puris/backend/{erp_adapter => erpadapter}/domain/model/ErpAdapterRequest.java (72%) rename backend/src/main/java/org/eclipse/tractusx/puris/backend/{erp_adapter => erpadapter}/domain/repository/ErpAdapterRequestRepository.java (86%) rename backend/src/main/java/org/eclipse/tractusx/puris/backend/{erp_adapter => erpadapter}/logic/service/ErpAdapterRequestService.java (79%) rename backend/src/main/java/org/eclipse/tractusx/puris/backend/{erp_adapter => erpadapter}/logic/service/ItemStockErpAdapterService.java (65%) diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java index 0de37b95..b84e8c3d 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java @@ -25,8 +25,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.util.VariablesService; -import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; -import org.eclipse.tractusx.puris.backend.erp_adapter.logic.service.ErpAdapterRequestService; +import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erpadapter.logic.service.ErpAdapterRequestService; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.MaterialPartnerRelation; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; @@ -255,11 +255,11 @@ private void setupCustomerRole() throws JsonProcessingException { .requestDate(new Date(System.currentTimeMillis()-3*60*60*1000)) .ownMaterialNumber(semiconductorMaterial.getOwnMaterialNumber()) .directionCharacteristic(DirectionCharacteristic.INBOUND) - .requestType("itemstock") + .requestType("ItemStock") .sammVersion("2.0") .build(); mockRequest = erpAdapterRequestService.create(mockRequest); - log.info("Created mocked ErpAdapterRequest: \n" + mockRequest); + log.info("Created mocked ErpAdapterRequest: \n{}", mockRequest); } /** diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java index 9299895c..d9e2cfca 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java @@ -95,7 +95,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "/material-demand/**", "/delivery-information/**", "/edc/**", - "/erp_adapter/**", + "/erp-adapter/**", "/parttypeinformation/**") .authenticated() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/health/**").permitAll() diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java deleted file mode 100644 index 983bd3a9..00000000 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/controller/ErpAdapterController.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2024 Volkswagen AG - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.tractusx.puris.backend.erp_adapter.controller; - -import com.fasterxml.jackson.databind.JsonNode; -import io.swagger.v3.oas.annotations.Parameter; -import lombok.extern.slf4j.Slf4j; - -import org.eclipse.tractusx.puris.backend.erp_adapter.logic.service.ItemStockErpAdapterService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Date; -import java.util.UUID; - -@RestController -@RequestMapping("erp_adapter") -@Slf4j -public class ErpAdapterController { - - @Autowired - private ItemStockErpAdapterService itemStockErpAdapterService; - - @PutMapping - public ResponseEntity putMethod( - @RequestParam("request_id") UUID requestId, - @RequestParam("bpnl") String partnerBpnl, - @RequestParam("response_type") String responseType, - @RequestParam("samm_version") String sammVersion, - @RequestParam(value = "response_timestamp") - @Parameter(example = "2024-05-28T15:00:00") - @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") Date responseTimestamp, - @RequestBody JsonNode requestBody - ) { - Dto dto = new Dto(requestId, partnerBpnl, responseType, sammVersion, responseTimestamp, requestBody); - switch (responseType.toLowerCase()) { - case "itemstock" -> itemStockErpAdapterService.receiveItemStockUpdate(dto); - default -> { - return ResponseEntity.status(501).body("Unsupported response type: " + responseType); - } - } - - return ResponseEntity.ok("ok"); - } - - public record Dto(UUID requestId, String partnerBpnl, String responseType, String sammVersion, - Date responseTimeStamp, JsonNode responseBody ){} -} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java new file mode 100644 index 00000000..0512c617 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Volkswagen AG + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.puris.backend.erpadapter.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.puris.backend.erpadapter.logic.service.ItemStockErpAdapterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.UUID; + +import static org.eclipse.tractusx.puris.backend.common.util.PatternStore.BPNL_PATTERN; +import static org.eclipse.tractusx.puris.backend.common.util.PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN; + +@RestController +@RequestMapping("erp-adapter") +@Slf4j +public class ErpAdapterController { + + @Autowired + private ItemStockErpAdapterService itemStockErpAdapterService; + + @Operation(description = "This endpoint accepts responses from the ERP adapter. Incoming messages are expected to " + + "carry a SAMM of the previously requested type. \n\nPlease note that this version currently accepts multiple responses " + + "addressing the same request-id for testing purposes. However, in the near future, this will be enforced strictly. " + + "I.e. only the first response for a given request-id will be accepted. All later responses addressing the same request-id" + + " will be rejected (status code 409)\n\n" + + "Currently supported: \n\n" + + "| response-type | samm-version |\n" + + "|---------------|--------------|\n" + + "| ItemStock | 2.0 |" + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "accepted"), + @ApiResponse(responseCode = "400", description = "bad request"), + @ApiResponse(responseCode = "404", description = "unknown request-id"), + @ApiResponse(responseCode = "409", description = "repeated answer for request-id"), + @ApiResponse(responseCode = "500", description = "internal server error"), + @ApiResponse(responseCode = "501", description = "unsupported response-type") + }) + @PutMapping + public ResponseEntity putMethod( + @RequestParam("request-id") UUID requestId, + @RequestParam("bpnl") String partnerBpnl, + @RequestParam("response-type") String responseType, + @RequestParam("samm-version") String sammVersion, + @RequestParam(value = "response-timestamp") + @Parameter(example = "2024-05-28T15:00:00") + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") Date responseTimestamp, + @RequestBody JsonNode requestBody + ) { + boolean valid = BPNL_PATTERN.matcher(partnerBpnl).matches(); + valid = valid && NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN.matcher(responseType).matches(); + valid = valid && NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN.matcher(sammVersion).matches(); + if (!valid) { + return ResponseEntity.badRequest().build(); + } + Dto dto = new Dto(requestId, partnerBpnl, responseType, sammVersion, responseTimestamp, requestBody); + int responseCode = 501; + switch (responseType) { + case "ItemStock" -> responseCode = itemStockErpAdapterService.receiveItemStockUpdate(dto); + default -> { + return ResponseEntity.status(responseCode).body("Unsupported response type: " + responseType); + } + } + return ResponseEntity.status(responseCode).build(); + } + + public record Dto(UUID requestId, String partnerBpnl, String responseType, String sammVersion, + Date responseTimeStamp, JsonNode body){} +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/model/ErpAdapterRequest.java similarity index 72% rename from backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java rename to backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/model/ErpAdapterRequest.java index f48bb147..b1341ea5 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/model/ErpAdapterRequest.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/model/ErpAdapterRequest.java @@ -18,12 +18,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.puris.backend.erp_adapter.domain.model; +package org.eclipse.tractusx.puris.backend.erpadapter.domain.model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import lombok.*; +import org.eclipse.tractusx.puris.backend.common.util.PatternStore; import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; import java.util.Date; @@ -41,16 +44,25 @@ public class ErpAdapterRequest { @Id private UUID id; + @Pattern(regexp = PatternStore.BPNL_STRING) + @NotNull private String partnerBpnl; + @Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING) + @NotNull private String requestType; + @Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING) + @NotNull private String sammVersion; + @NotNull private Date requestDate; private Date responseReceivedDate; + @Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING) + @NotNull private String ownMaterialNumber; private DirectionCharacteristic directionCharacteristic; diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/repository/ErpAdapterRequestRepository.java similarity index 86% rename from backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java rename to backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/repository/ErpAdapterRequestRepository.java index 510f0334..0b290475 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/domain/repository/ErpAdapterRequestRepository.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/domain/repository/ErpAdapterRequestRepository.java @@ -18,9 +18,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.puris.backend.erp_adapter.domain.repository; +package org.eclipse.tractusx.puris.backend.erpadapter.domain.repository; -import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ErpAdapterRequestService.java similarity index 79% rename from backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java rename to backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ErpAdapterRequestService.java index a8de48e5..60088da3 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ErpAdapterRequestService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ErpAdapterRequestService.java @@ -18,10 +18,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.puris.backend.erp_adapter.logic.service; +package org.eclipse.tractusx.puris.backend.erpadapter.logic.service; -import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; -import org.eclipse.tractusx.puris.backend.erp_adapter.domain.repository.ErpAdapterRequestRepository; +import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erpadapter.domain.repository.ErpAdapterRequestRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -41,7 +41,9 @@ public ErpAdapterRequest create(ErpAdapterRequest erpAdapterRequest) { } public ErpAdapterRequest get(UUID id) { - return repository.findById(id).orElse(null); + // TODO: Remove when mock is removed + return repository.findById(id).orElse(repository.findAll().getFirst()); +// return repository.findById(id).orElse(null); } public ErpAdapterRequest update(ErpAdapterRequest erpAdapterRequest) { diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java similarity index 65% rename from backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java rename to backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java index ad48141c..707bbb22 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erp_adapter/logic/service/ItemStockErpAdapterService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java @@ -18,12 +18,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.eclipse.tractusx.puris.backend.erp_adapter.logic.service; +package org.eclipse.tractusx.puris.backend.erpadapter.logic.service; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import org.eclipse.tractusx.puris.backend.erp_adapter.controller.ErpAdapterController; -import org.eclipse.tractusx.puris.backend.erp_adapter.domain.model.ErpAdapterRequest; +import org.eclipse.tractusx.puris.backend.erpadapter.controller.ErpAdapterController; +import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; @@ -38,7 +38,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Date; import java.util.List; @Service @@ -67,12 +66,46 @@ public class ItemStockErpAdapterService { @Autowired private ProductItemStockService productItemStockService; + private final static String SUPPORTEDSAMMVERSION = "2.0"; - public void receiveItemStockUpdate(ErpAdapterController.Dto dto) { + + /** + * This method handles a response for an ItemStock Request from the ERP Adapter. + * It's return value is the status code that is being sent back to the ERP Adapter. + * + * @param dto contains the parameters and the body of the response message + * @return the appropriate HTTP response code + */ + public int receiveItemStockUpdate(ErpAdapterController.Dto dto) { try { - ItemStockSamm samm = mapper.treeToValue(dto.responseBody(), ItemStockSamm.class); - Partner partner = partnerService.findByBpnl(dto.partnerBpnl()); + ItemStockSamm samm = mapper.treeToValue(dto.body(), ItemStockSamm.class); ErpAdapterRequest request = erpAdapterRequestService.get(dto.requestId()); + if (request == null) { + log.error("Unknown request-id {}", dto.requestId()); + return 404; + } + // TODO: uncomment the following block when removing mock request, also edit swagger description in ErpAdapterController +// if (request.getResponseReceivedDate() != null) { +// log.error("Received duplicate response for messageId {}", request.getId()); +// return 409; +// } + if (!request.getPartnerBpnl().equals(dto.partnerBpnl())) { + log.error("BPNL mismatch! request BPNL: {}, message BPNL: {}", + request.getPartnerBpnl(), dto.partnerBpnl()); + return 400; + } + if (!request.getDirectionCharacteristic().equals(samm.getDirection())) { + log.error("Direction mismatch! request direction: {}, message direction: {}", + request.getDirectionCharacteristic(), samm.getDirection()); + return 400; + } + if (!SUPPORTEDSAMMVERSION.equals(dto.sammVersion()) || !SUPPORTEDSAMMVERSION.equals(request.getSammVersion())) { + log.error("Unsupported Samm Version! Supported: " + SUPPORTEDSAMMVERSION + ", request: {}, message: {}", + request.getSammVersion(), dto.sammVersion()); + return 400; + } + + Partner partner = partnerService.findByBpnl(request.getPartnerBpnl()); Material material = materialService.findByOwnMaterialNumber(request.getOwnMaterialNumber()); switch (samm.getDirection()) { @@ -80,33 +113,36 @@ public void receiveItemStockUpdate(ErpAdapterController.Dto dto) { var mpr = mprService.find(material, partner); if (mpr == null || !mpr.isPartnerSuppliesMaterial()) { log.error("Partner {} is not registered as supplier for {}", partner.getBpnl(), material.getOwnMaterialNumber()); - return; + return 400; } List materialItemStockList = sammMapper.erpSammToMaterialItemStock(samm, partner, material); materialItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> materialItemStockService.delete(stock.getUuid())); materialItemStockList.forEach(stock -> materialItemStockService.create(stock)); log.info("Inserted {} MaterialItemStocks for {} and {}", materialItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); - request.setResponseReceivedDate(new Date()); + request.setResponseReceivedDate(dto.responseTimeStamp()); erpAdapterRequestService.update(request); + return 201; } case OUTBOUND -> { var mpr = mprService.find(material, partner); if (mpr == null || !mpr.isPartnerBuysMaterial()) { log.error("Partner {} is not registered as customer for {}", partner.getBpnl(), material.getOwnMaterialNumber()); - return; + return 400; } List productItemStockList = sammMapper.erpSammToProductItemStock(samm, partner, material); productItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> productItemStockService.delete(stock.getUuid())); productItemStockList.forEach(stock -> productItemStockService.create(stock)); log.info("Inserted {} ProductItemStocks for {} and {}", productItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); - request.setResponseReceivedDate(new Date()); + request.setResponseReceivedDate(dto.responseTimeStamp()); erpAdapterRequestService.update(request); + return 201; } default -> throw new IllegalArgumentException("Invalid direction: " + samm.getDirection()); } } catch (Exception e) { log.error("Error while receiving erp itemstock update", e); + return 500; } } } From 3434ff28e2836900177b1f4591f0eed142eb2521 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Wed, 29 May 2024 15:28:44 +0200 Subject: [PATCH 3/3] fix: added validations and a sample for swagger --- .../backend/common/util/VariablesService.java | 8 --- .../controller/ErpAdapterController.java | 69 +++++++++++++++++++ .../service/ItemStockErpAdapterService.java | 26 ++++++- 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java index bcc64055..ff2b0a9c 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java @@ -56,14 +56,6 @@ public class VariablesService { */ private String edrEndpoint; - - /** - * The endpoint of your ERP adapter - * (currently just mock!) - */ -// @Value("${puris.erpadapterurl") - private String erpAdapterUrl ="http:foo.com/my_erp_endpoint"; - @Value("${puris.baseurl}" + "catena/item-stock/request") /** * The url under which this application's request endpoint can diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java index 0512c617..3214296e 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/controller/ErpAdapterController.java @@ -23,6 +23,8 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; @@ -73,6 +75,9 @@ public ResponseEntity putMethod( @RequestParam(value = "response-timestamp") @Parameter(example = "2024-05-28T15:00:00") @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") Date responseTimestamp, + @io.swagger.v3.oas.annotations.parameters.RequestBody(content = {@Content(examples = { + @ExampleObject(itemStock20Sample) + })}) @RequestBody JsonNode requestBody ) { boolean valid = BPNL_PATTERN.matcher(partnerBpnl).matches(); @@ -94,4 +99,68 @@ public ResponseEntity putMethod( public record Dto(UUID requestId, String partnerBpnl, String responseType, String sammVersion, Date responseTimeStamp, JsonNode body){} + + private final static String itemStock20Sample = "{\n" + + " \"materialGlobalAssetId\": null,\n" + + " \"positions\": [\n" + + " {\n" + + " \"orderPositionReference\": {\n" + + " \"supplierOrderId\": \"M-Nbr-4711\",\n" + + " \"customerOrderId\": \"C-Nbr-4711\",\n" + + " \"customerOrderPositionId\": \"PositionId-01\"\n" + + " },\n" + + " \"allocatedStocks\": [\n" + + " {\n" + + " \"isBlocked\": false,\n" + + " \"stockLocationBPNA\": \"BPNA4444444444AA\",\n" + + " \"lastUpdatedOnDateTime\": \"2023-04-28T14:23:00.123456+14:00\",\n" + + " \"quantityOnAllocatedStock\": {\n" + + " \"value\": 22.0,\n" + + " \"unit\": \"unit:piece\"\n" + + " },\n" + + " \"stockLocationBPNS\": \"BPNS4444444444XX\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"orderPositionReference\": {\n" + + " \"supplierOrderId\": \"M-Nbr-4711\",\n" + + " \"customerOrderId\": \"C-Nbr-4711\",\n" + + " \"customerOrderPositionId\": \"PositionId-03\"\n" + + " },\n" + + " \"allocatedStocks\": [\n" + + " {\n" + + " \"isBlocked\": false,\n" + + " \"stockLocationBPNA\": \"BPNA4444444444AA\",\n" + + " \"lastUpdatedOnDateTime\": \"2023-04-28T14:23:00.123456+14:00\",\n" + + " \"quantityOnAllocatedStock\": {\n" + + " \"value\": 66.0,\n" + + " \"unit\": \"unit:piece\"\n" + + " },\n" + + " \"stockLocationBPNS\": \"BPNS4444444444XX\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"orderPositionReference\": {\n" + + " \"supplierOrderId\": \"M-Nbr-4711\",\n" + + " \"customerOrderId\": \"C-Nbr-4711\",\n" + + " \"customerOrderPositionId\": \"PositionId-02\"\n" + + " },\n" + + " \"allocatedStocks\": [\n" + + " {\n" + + " \"isBlocked\": true,\n" + + " \"stockLocationBPNA\": \"BPNA4444444444AA\",\n" + + " \"lastUpdatedOnDateTime\": \"2023-04-28T14:23:00.123456+14:00\",\n" + + " \"quantityOnAllocatedStock\": {\n" + + " \"value\": 44.0,\n" + + " \"unit\": \"unit:piece\"\n" + + " },\n" + + " \"stockLocationBPNS\": \"BPNS4444444444XX\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"direction\": \"INBOUND\"\n" + + "}"; } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java index 707bbb22..c8bebeba 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/erpadapter/logic/service/ItemStockErpAdapterService.java @@ -20,6 +20,7 @@ package org.eclipse.tractusx.puris.backend.erpadapter.logic.service; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.erpadapter.controller.ErpAdapterController; @@ -116,6 +117,12 @@ public int receiveItemStockUpdate(ErpAdapterController.Dto dto) { return 400; } List materialItemStockList = sammMapper.erpSammToMaterialItemStock(samm, partner, material); + int initialSize = materialItemStockList.size(); + materialItemStockList.removeIf(stock -> !materialItemStockService.validate(stock)); + int removed = initialSize - materialItemStockList.size(); + if (removed > 0) { + log.warn("Removed {} out of {} MaterialItemStocks because of failing validation.", removed, initialSize); + } materialItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> materialItemStockService.delete(stock.getUuid())); materialItemStockList.forEach(stock -> materialItemStockService.create(stock)); log.info("Inserted {} MaterialItemStocks for {} and {}", materialItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); @@ -130,6 +137,12 @@ public int receiveItemStockUpdate(ErpAdapterController.Dto dto) { return 400; } List productItemStockList = sammMapper.erpSammToProductItemStock(samm, partner, material); + int initialSize = productItemStockList.size(); + productItemStockList.removeIf(stock -> !productItemStockService.validate(stock)); + int removed = initialSize - productItemStockList.size(); + if (removed > 0) { + log.warn("Removed {} out of {} ProductItemStocks because of failing validation.", removed, initialSize); + } productItemStockService.findByPartnerAndMaterial(partner, material).forEach(stock -> productItemStockService.delete(stock.getUuid())); productItemStockList.forEach(stock -> productItemStockService.create(stock)); log.info("Inserted {} ProductItemStocks for {} and {}", productItemStockList.size(), material.getOwnMaterialNumber(), partner.getBpnl()); @@ -137,12 +150,19 @@ public int receiveItemStockUpdate(ErpAdapterController.Dto dto) { erpAdapterRequestService.update(request); return 201; } - default -> throw new IllegalArgumentException("Invalid direction: " + samm.getDirection()); } } catch (Exception e) { - log.error("Error while receiving erp itemstock update", e); - return 500; + if (e instanceof IllegalArgumentException || e instanceof JsonProcessingException) { + // treeToValue method has thrown this exception + log.error("Error parsing request body: \n{}", dto.body().toPrettyString(), e); + return 400; + } else { + log.error("Error while receiving erp itemstock update", e); + return 500; + } } + log.error("Unexpected error while receiving erp itemstock update"); + return 500; } }