Skip to content

Commit

Permalink
feat: implemented delivery information edc flow (eclipse-tractusx#355)
Browse files Browse the repository at this point in the history
* feat: implemented delivery information edc flow

* chore: updated frontend dependencies

* feat: implemented logic for delivery creation based on incoterm

* fix: CodeQl findings
  • Loading branch information
ReneSchroederLJ authored May 14, 2024
1 parent d80f9f3 commit c7eded8
Show file tree
Hide file tree
Showing 19 changed files with 1,109 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public JsonNode createMaterialRegistrationRequestBody(MaterialPartnerRelation ma

submodelDescriptorsArray.add(createSubmodelObject(SubmodelType.ITEM_STOCK.URN_SEMANTIC_ID, href + DirectionCharacteristic.INBOUND + "/", variablesService.getItemStockSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(SubmodelType.DEMAND.URN_SEMANTIC_ID, href, variablesService.getDemandSubmodelApiAssetId()));
submodelDescriptorsArray.add(createSubmodelObject(SubmodelType.DELIVERY.URN_SEMANTIC_ID, href, variablesService.getDeliverySubmodelApiAssetId()));

log.debug("Created body for material " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
return body;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ 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/**", "/edrendpoint/**", "/edc/**", "/parttypeinformation/**")
.requestMatchers("/stockView/**", "/partners/**", "/materials/**", "/materialpartnerrelations/**", "/item-stock/**", "/production/**", "/delivery/**", "/demand/**", "/planned-production/**", "/material-demand/**", "/delivery-information/**", "/edrendpoint/**", "/edc/**", "/parttypeinformation/**")
.authenticated()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/health/**").permitAll()
.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,38 @@
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.management.openmbean.KeyAlreadyExistsException;

import org.eclipse.tractusx.puris.backend.delivery.domain.model.Delivery;
import org.eclipse.tractusx.puris.backend.common.util.PatternStore;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.OwnDelivery;
import org.eclipse.tractusx.puris.backend.delivery.domain.model.ReportedDelivery;
import org.eclipse.tractusx.puris.backend.delivery.logic.dto.DeliveryDto;
import org.eclipse.tractusx.puris.backend.delivery.logic.service.DeliveryRequestApiService;
import org.eclipse.tractusx.puris.backend.delivery.logic.service.OwnDeliveryService;
import org.eclipse.tractusx.puris.backend.delivery.logic.service.ReportedDeliveryService;
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.dto.PartnerDto;
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.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -67,25 +76,43 @@ public class DeliveryController {
@Autowired
private ReportedDeliveryService reportedDeliveryService;

@Autowired
private DeliveryRequestApiService deliveryRequestApiService;

@Autowired
private MaterialService materialService;

@Autowired
private PartnerService partnerService;

@Autowired
private MaterialPartnerRelationService mprService;

@Autowired
private ModelMapper modelMapper;

@Autowired
private Validator validator;

private final Pattern materialPattern = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN;

@Autowired
private ExecutorService executorService;

@GetMapping()
@ResponseBody
@Operation(summary = "Get all planned deliveries for the given Material",
description = "Get all planned deliveries for the given material number. Optionally the delivery can be filtered by its partner bpnl.")
public List<DeliveryDto> getAllDeliveries(String materialNumber, Optional<String> bpnl) {
return ownDeliveryService.findAllByFilters(Optional.of(materialNumber), bpnl)
description = "Get all planned deliveries for the given material number. Optionally a bpns and partner bpnl can be provided to filter the deliveries further.")
public List<DeliveryDto> getAllDeliveries(String ownMaterialNumber, Optional<String> bpns, Optional<String> bpnl) {
Material material = materialService.findByOwnMaterialNumber(ownMaterialNumber);
if (material == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Material does not exist.");
}
var reportedDeliveries = reportedDeliveryService.findAllByFilters(Optional.of(ownMaterialNumber), bpns, bpnl)
.stream().map(this::convertToDto).collect(Collectors.toList());
var ownDeliveries = ownDeliveryService.findAllByFilters(Optional.of(ownMaterialNumber), bpns, bpnl)
.stream().map(this::convertToDto).collect(Collectors.toList());
return List.of(reportedDeliveries, ownDeliveries).stream().flatMap(List::stream).toList();
}

@PostMapping()
Expand Down Expand Up @@ -157,13 +184,35 @@ public void deleteDelivery(@PathVariable UUID id) {
ownDeliveryService.delete(id);
}

@GetMapping("reported")
@GetMapping("reported/refresh")
@ResponseBody
@Operation(summary = "Get all deliveries of partners for a material",
description = "Get all deliveries of partners for a material number. Optionally the partners can be filtered by their bpnl.")
public List<DeliveryDto> getAllDeliveriesForPartner(String materialNumber, Optional<String> bpnl) {
return reportedDeliveryService.findAllByFilters(Optional.of(materialNumber), bpnl)
.stream().map(this::convertToDto).collect(Collectors.toList());
@Operation(
summary = "Refreshes all reported deliveries",
description = "Refreshes all reported deliveries from the delivery request API."
)
public ResponseEntity<List<PartnerDto>> refreshReportedDeliveries(@RequestParam String ownMaterialNumber) {
if (!materialPattern.matcher(ownMaterialNumber).matches()) {
return new ResponseEntity<>(HttpStatusCode.valueOf(400));
}
Material materialEntity = materialService.findByOwnMaterialNumber(ownMaterialNumber);
if (materialEntity == null) {
return new ResponseEntity<>(HttpStatusCode.valueOf(404));
}

List<Partner> partners;
if (materialEntity.isMaterialFlag()) {
partners = mprService.findAllSuppliersForOwnMaterialNumber(ownMaterialNumber);
} else {
partners = mprService.findAllCustomersForOwnMaterialNumber(ownMaterialNumber);
}
for (Partner partner : partners) {
executorService.submit(() ->
deliveryRequestApiService.doReportedDeliveryRequest(partner, materialEntity));
}

return ResponseEntity.ok(partners.stream()
.map(partner -> modelMapper.map(partner, PartnerDto.class))
.toList());
}

private OwnDelivery convertToEntity(DeliveryDto dto) {
Expand All @@ -183,13 +232,19 @@ private OwnDelivery convertToEntity(DeliveryDto dto) {
return entity;
}

private DeliveryDto convertToDto(Delivery entity) {
private DeliveryDto convertToDto(OwnDelivery entity) {
DeliveryDto dto = modelMapper.map(entity, DeliveryDto.class);

dto.setOwnMaterialNumber(entity.getMaterial().getOwnMaterialNumber());
dto.setPartnerBpnl(entity.getPartner().getBpnl());
dto.setReported(false);
return dto;
}

private DeliveryDto convertToDto(ReportedDelivery entity) {
DeliveryDto dto = modelMapper.map(entity, DeliveryDto.class);
dto.setOwnMaterialNumber(entity.getMaterial().getOwnMaterialNumber());
dto.setPartnerBpnl(entity.getPartner().getBpnl());

dto.setReported(true);
return dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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.delivery.controller;

import io.swagger.v3.oas.annotations.Operation;
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.common.util.PatternStore;
import org.eclipse.tractusx.puris.backend.delivery.logic.dto.deliverysamm.DeliveryInformation;
import org.eclipse.tractusx.puris.backend.delivery.logic.service.DeliveryRequestApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.regex.Pattern;

@RestController
@RequestMapping("delivery-information")
@Slf4j
/**
* This class offers the endpoint for requesting the PlannedProduction Submodel 2.0.0
*/
public class DeliveryRequestApiController {

@Autowired
private DeliveryRequestApiService deliveryRequestApiSrvice;

private final Pattern bpnlPattern = PatternStore.BPNL_PATTERN;

private final Pattern urnPattern = PatternStore.URN_OR_UUID_PATTERN;


@Operation(summary = "This endpoint receives the Delivery Information Submodel 2.0.0 requests")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Ok"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "500", description = "Internal Server Error"),
@ApiResponse(responseCode = "501", description = "Unsupported representation")
})
@GetMapping("request/{materialNumberCx}/{representation}")
public ResponseEntity<DeliveryInformation> getDeliveryMapping(
@RequestHeader("edc-bpn") String bpnl,
@PathVariable String materialNumberCx,
@PathVariable String representation
) {
if (!bpnlPattern.matcher(bpnl).matches() || !urnPattern.matcher(materialNumberCx).matches()) {
log.warn("Rejecting request at Delivery Information Submodel request 2.0.0 endpoint");
return ResponseEntity.badRequest().build();
}

if (!"$value".equals(representation)) {
log.warn("Rejecting request at Delivery Information Submodel request 2.0.0 endpoint, missing '$value' in request");
if (!PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN.matcher(representation).matches()) {
representation = "<REPLACED_INVALID_REPRESENTATION>";
}
log.warn("Received " + representation + " from " + bpnl);
return ResponseEntity.status(501).build();
}
var samm = deliveryRequestApiSrvice.handleDeliverySubmodelRequest(bpnl, materialNumberCx);
if (samm == null) {
return ResponseEntity.status(500).build();
}
return ResponseEntity.ok(samm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.delivery.domain.model;

public enum DeliveryResponsibilityEnumeration {
CUSTOMER, SUPPLIER, PARTIAL
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,33 @@ public enum IncotermEnumeration {
public String getValue() {
return value;
}

public DeliveryResponsibilityEnumeration getResponsibility() {
switch (this) {
case EXW:
return DeliveryResponsibilityEnumeration.CUSTOMER;
case FCA:
return DeliveryResponsibilityEnumeration.PARTIAL;
case FAS:
return DeliveryResponsibilityEnumeration.PARTIAL;
case FOB:
return DeliveryResponsibilityEnumeration.PARTIAL;
case CFR:
return DeliveryResponsibilityEnumeration.PARTIAL;
case CIF:
return DeliveryResponsibilityEnumeration.PARTIAL;
case DAP:
return DeliveryResponsibilityEnumeration.SUPPLIER;
case DPU:
return DeliveryResponsibilityEnumeration.SUPPLIER;
case CPT:
return DeliveryResponsibilityEnumeration.SUPPLIER;
case CIP:
return DeliveryResponsibilityEnumeration.SUPPLIER;
case DDP:
return DeliveryResponsibilityEnumeration.SUPPLIER;
default:
throw new IllegalArgumentException("Unknown Incoterm");
}
}
}
Loading

0 comments on commit c7eded8

Please sign in to comment.