diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index d7ee2ac8..4066cbf6 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -24,7 +24,7 @@ maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR G maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/jakarta.persistence/jakarta.persistence-api/3.1.0, EPL-2.0 OR BSD-3-Clause AND (EPL-2.0 OR BSD-3-Clause AND BSD-3-Clause), approved, #7696 maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7697 -maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.12.22, Apache-2.0, approved, #1810 maven/mavencentral/net.bytebuddy/byte-buddy/1.12.22, Apache-2.0 AND BSD-3-Clause, approved, #1811 diff --git a/backend/DEPENDENCIES b/backend/DEPENDENCIES index d7ee2ac8..4066cbf6 100644 --- a/backend/DEPENDENCIES +++ b/backend/DEPENDENCIES @@ -24,7 +24,7 @@ maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR G maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/jakarta.persistence/jakarta.persistence-api/3.1.0, EPL-2.0 OR BSD-3-Clause AND (EPL-2.0 OR BSD-3-Clause AND BSD-3-Clause), approved, #7696 maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7697 -maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.12.22, Apache-2.0, approved, #1810 maven/mavencentral/net.bytebuddy/byte-buddy/1.12.22, Apache-2.0 AND BSD-3-Clause, approved, #1811 diff --git a/backend/docs/dev.md b/backend/docs/dev.md new file mode 100644 index 00000000..2d5c296d --- /dev/null +++ b/backend/docs/dev.md @@ -0,0 +1,12 @@ +## Keeping dependencies-files up to date +### Backend + +Navigate to the ./backend folder and run: +``` +mvn org.eclipse.dash:license-tool-plugin:license-check +cp DEPENDENCIES ../DEPENDENCIES_BACKEND +``` +The first line runs the maven license tool with the parameters specified in the +./backend/pom.xml and produces a DEPENDENCIES file in the ./backend folder. +Then this file gets copied to the PURIS-project root folder under the name DEPENDENCIES_BACKEND. +Both files should be updated prior to any pull request. 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 d510f5c2..0dade664 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 @@ -327,12 +327,12 @@ private Partner createAndGetSupplierPartner() { private Partner createAndGetNonScenarioCustomer() { Partner nonScenarioCustomer = new Partner( "Non-Scenario Customer", - "(None Provided!)>", + "http://nonscenario-customer.com/api/v1/ids", "BPNL2222222222RR", - "BPNS2222222222XZ", - "Zentraleinkaufsabteilung", - "BPNA2222222222HH", - "54.321N, 8.7654E" + "BPNA2222222222XZ", + "Fichtenweg 23", + "65432 Waldhausen", + "Germany" ); nonScenarioCustomer = partnerService.create(nonScenarioCustomer); log.info(String.format("Created non-scenario customer partner: %s", nonScenarioCustomer)); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialController.java new file mode 100644 index 00000000..af809c92 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialController.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.controller; + +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 org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.logic.dto.MaterialEntityDto; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("materials") +public class MaterialController { + + @Autowired + private MaterialService materialService; + private ModelMapper modelMapper = new ModelMapper(); + + @PostMapping + @CrossOrigin + @Operation(description = "Creates a new Material entity with the data given in the request body. As a bare minimum, " + + "it must contain a new, unique ownMaterialNumber.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully created a new Material entity."), + @ApiResponse(responseCode = "400", description = "Malformed request body."), + @ApiResponse(responseCode = "409", description = "Material with the given ownMaterialNumber already exists."), + @ApiResponse(responseCode = "500", description = "Internal Server error.") + }) + public ResponseEntity createMaterial(@RequestBody MaterialEntityDto materialDto) { + if (materialDto.getOwnMaterialNumber() == null || materialDto.getOwnMaterialNumber().isEmpty()) { + // Cannot create material without ownMaterialNumber + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + if (materialService.findByOwnMaterialNumber(materialDto.getOwnMaterialNumber()) != null) { + // Cannot create material, ownMaterialNumber is already assigned + return new ResponseEntity<>(HttpStatusCode.valueOf(409)); + } + Material createdMaterial; + try { + createdMaterial = modelMapper.map(materialDto, Material.class); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + + createdMaterial = materialService.create(createdMaterial); + if (createdMaterial == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + @PutMapping + @CrossOrigin + @Operation(description = "Updates an existing Material entity with the data given in the request body.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Update was accepted."), + @ApiResponse(responseCode = "400", description = "Malformed request body."), + @ApiResponse(responseCode = "404", description = "No existing Material Entity found, no update was performed."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity updateMaterial(@RequestBody MaterialEntityDto materialDto) { + if (materialDto.getOwnMaterialNumber() == null || materialDto.getOwnMaterialNumber().isEmpty()) { + // Cannot update material without ownMaterialNumber + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + Material existingMaterial = materialService.findByOwnMaterialNumber(materialDto.getOwnMaterialNumber()); + if (existingMaterial == null) { + // Cannot update non-existent Material + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + Material updatedMaterial; + try { + updatedMaterial = modelMapper.map(materialDto, Material.class); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + updatedMaterial = materialService.update(updatedMaterial); + if (updatedMaterial == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + @GetMapping + @CrossOrigin + @Operation(description = "Returns the requested Material dto, specified by the given ownMaterialNumber.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Returns the requested Material."), + @ApiResponse(responseCode = "404", description = "Requested Material was not found.") + }) + public ResponseEntity getMaterial(@Parameter(name = "ownMaterialNumber", + description = "The Material Number that is used in your own company to identify the Material.", + example = "MNR-7307-AU340474.002") @RequestParam String ownMaterialNumber) { + Material foundMaterial = materialService.findByOwnMaterialNumber(ownMaterialNumber); + if (foundMaterial == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + + MaterialEntityDto dto = modelMapper.map(foundMaterial, MaterialEntityDto.class); + return new ResponseEntity<>(dto, HttpStatusCode.valueOf(200)); + } + + @CrossOrigin + @GetMapping("/all") + @Operation(description = "Returns a list of all Materials and Products.") + public ResponseEntity> listMaterials() { + return new ResponseEntity<>(materialService.findAll(). + stream().map(x -> modelMapper.map(x, MaterialEntityDto.class)).collect(Collectors.toList()), + HttpStatusCode.valueOf(200)); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialPartnerRelationsController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialPartnerRelationsController.java new file mode 100644 index 00000000..9b3c19db --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/MaterialPartnerRelationsController.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.controller; + +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 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; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@RestController +@RequestMapping("materialpartnerrelations") +public class MaterialPartnerRelationsController { + + + @Autowired + private MaterialService materialService; + + @Autowired + private PartnerService partnerService; + + @Autowired + private MaterialPartnerRelationService mprService; + + @PostMapping + @CrossOrigin + @Operation(description = "Creates a new MaterialPartnerRelation with the given parameter data. " + + "Please note that this is only possible, if the designated Material " + + "and Partner entities have already been created before this request. ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Successfully created a new MaterialPartnerRelationEntity."), + @ApiResponse(responseCode = "400", description = "Material and/or Partner do not exist."), + @ApiResponse(responseCode = "409", description = "Relation for given Material and Partner does already exist."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity createMaterialPartnerRelation( + @Parameter(description = "The Material Number that is used in your own company to identify the Material.", + example = "MNR-7307-AU340474.002") @RequestParam String ownMaterialNumber, + @Parameter(description = "The unique BPNL that was assigned to that Partner.", + example = "BPNL2222222222RR") @RequestParam() String partnerBpnl, + @Parameter(description = "The Material Number that this Partner is using in his own company to identify the Material.", + example = "MNR-8101-ID146955.001") @RequestParam String partnerMaterialNumber, + @Parameter(description = "This boolean flag indicates whether this Partner is a potential supplier of the given Material.", + example = "true") @RequestParam boolean partnerSupplies, + @Parameter(description = "This boolean flag indicates whether this Partner is a potential customer of this Material.", + example = "true") @RequestParam boolean partnerBuys) { + Material material = materialService.findByOwnMaterialNumber(ownMaterialNumber); + if (material == null || partnerBpnl == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + Partner partner = null; + if (partnerBpnl != null) { + partner = partnerService.findByBpnl(partnerBpnl); + } + if (partner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + + if (mprService.find(material, partner) != null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(409)); + } + MaterialPartnerRelation newMpr = new MaterialPartnerRelation(material, partner, partnerMaterialNumber, partnerSupplies, partnerBuys); + + newMpr = mprService.create(newMpr); + if (newMpr == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + @PutMapping + @CrossOrigin + @Operation(description = "Updates an existing MaterialPartnerRelation. You have to specify the ownMaterialNumber and " + + "the partnerBpnl. The other three parameters are genuinely optional. Provide them only if you want to change their values. ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Update was accepted."), + @ApiResponse(responseCode = "404", description = "No existing entity was found."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity updateMaterialPartnerRelation( + @Parameter(description = "The Material Number that is used in your own company to identify the Material.", + example = "MNR-7307-AU340474.002") @RequestParam String ownMaterialNumber, + @Parameter(description = "The unique BPNL that was assigned to that Partner.", + example = "BPNL2222222222RR") @RequestParam() String partnerBpnl, + @Parameter(description = "The Material Number that this Partner is using in his own company to identify the Material.", + example = "MNR-8101-ID146955.001") @RequestParam(required = false) String partnerMaterialNumber, + @Parameter(description = "This boolean flag indicates whether this Partner is a potential supplier of the given Material.", + example = "true") @RequestParam(required = false) Boolean partnerSupplies, + @Parameter(description = "This boolean flag indicates whether this Partner is a potential customer of this Material.", + example = "true") @RequestParam(required = false) Boolean partnerBuys) { + MaterialPartnerRelation existingRelation = null; + Partner partner = partnerService.findByBpnl(partnerBpnl); + Material material = materialService.findByOwnMaterialNumber(ownMaterialNumber); + if (partner != null && material != null) { + existingRelation = mprService.find(material, partner); + } + if (existingRelation == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + if (partnerSupplies != null) { + existingRelation.setPartnerSuppliesMaterial(partnerSupplies); + } + if (partnerBuys != null) { + existingRelation.setPartnerBuysMaterial(partnerBuys); + } + if (partnerMaterialNumber != null) { + existingRelation.setPartnerMaterialNumber(partnerMaterialNumber); + } + existingRelation = mprService.update(existingRelation); + if (existingRelation == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/PartnerController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/PartnerController.java new file mode 100644 index 00000000..a00989a7 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/PartnerController.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.*; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.*; +import org.eclipse.tractusx.puris.backend.masterdata.logic.dto.*; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.modelmapper.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("partners") +public class PartnerController { + + @Autowired + private PartnerService partnerService; + private ModelMapper modelMapper = new ModelMapper(); + + @CrossOrigin + @PostMapping + @Operation(description = "Creates a new Partner entity with the data given in the request body. Please note that no " + + "UUID can be assigned to a Partner that wasn't created before. So the request body must not contain a UUID.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Partner created successfully."), + @ApiResponse(responseCode = "400", description = "Request body was malformed, didn't meet the minimum constraints or wrongfully contained a UUID."), + @ApiResponse(responseCode = "409", description = "The BPNL specified in the request body is already assigned. ") + }) + public ResponseEntity createPartner(@RequestBody PartnerDto partnerDto) { + modelMapper.getConfiguration().setPropertyCondition(Conditions.isNotNull()); + // Any given UUID is wrong by default since we're creating a new Partner entity + if (partnerDto.getUuid() != null || partnerDto.getBpnl() == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + + // Check whether the given BPNL is already assigned + Partner checkExistingPartner = partnerService.findByBpnl(partnerDto.getBpnl()); + if (checkExistingPartner != null) { + // Cannot create Partner because BPNL is already assigned + return new ResponseEntity<>(HttpStatusCode.valueOf(409)); + } + + Partner partnerEntity; + try { + partnerEntity = modelMapper.map(partnerDto, Partner.class); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + + partnerEntity = partnerService.create(partnerEntity); + if (partnerEntity == null) { + // Creation failed due to unfulfilled constraints + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + @PutMapping("putAddress") + @CrossOrigin + @Operation(description = "Updates an existing Partner by adding a new Address. If that Partner already has " + + "an Address with the BPNA given in the request body, that existing Address will be overwritten. ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Update accepted."), + @ApiResponse(responseCode = "400", description = "Invalid Address data."), + @ApiResponse(responseCode = "404", description = "Partner not found."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity addAddress( + @Parameter(description = "The unique BPNL that was assigned to that Partner.", + example = "BPNL2222222222RR") @RequestParam() String partnerBpnl, + @RequestBody AddressDto address) { + Partner existingPartner = partnerService.findByBpnl(partnerBpnl); + if (existingPartner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + Address newAddress; + try { + newAddress = modelMapper.map(address, Address.class); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + // Remove operation in case there is an update of an existing address + existingPartner.getAddresses().remove(newAddress); + + existingPartner.getAddresses().add(newAddress); + existingPartner = partnerService.update(existingPartner); + if (existingPartner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + @PutMapping("putSite") + @CrossOrigin + @Operation(description = "Updates an existing Partner by adding a new Site. If that Partner already has " + + "a Site with the BPNS given in the request body, that existing Site will be overwritten. ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Update accepted."), + @ApiResponse(responseCode = "400", description = "Invalid Address data."), + @ApiResponse(responseCode = "404", description = "Partner not found."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity addSite( + @Parameter(description = "The unique BPNL that was assigned to that Partner.", + example = "BPNL2222222222RR") @RequestParam() String partnerBpnl, + @RequestBody SiteDto site) { + Partner existingPartner = partnerService.findByBpnl(partnerBpnl); + if (existingPartner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + Site newSite; + try { + newSite = modelMapper.map(site, Site.class); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + // Remove operation in case there is an update of an existing site + existingPartner.getSites().remove(newSite); + + existingPartner.getSites().add(newSite); + existingPartner = partnerService.update(existingPartner); + if (existingPartner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(400)); + } + + return new ResponseEntity<>(HttpStatusCode.valueOf(200)); + } + + + @CrossOrigin + @GetMapping + @Operation(description = "Returns the requested PartnerDto.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Found Partner, returning it in response body."), + @ApiResponse(responseCode = "404", description = "Requested Partner not found."), + @ApiResponse(responseCode = "500", description = "Internal Server Error.") + }) + public ResponseEntity getPartner( + @Parameter(description = "The unique BPNL that was assigned to that Partner.", + example = "BPNL2222222222RR") @RequestParam() String partnerBpnl) { + Partner partner = partnerService.findByBpnl(partnerBpnl); + if (partner == null) { + return new ResponseEntity<>(HttpStatusCode.valueOf(404)); + } + try { + PartnerDto partnerDto = modelMapper.map(partner, PartnerDto.class); + return new ResponseEntity<>(partnerDto, HttpStatusCode.valueOf(200)); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatusCode.valueOf(500)); + } + } + + @CrossOrigin + @GetMapping("/all") + @Operation(description = "Returns a list of all Partners. ") + public ResponseEntity> listPartners() { + return new ResponseEntity<>(partnerService.findAll(). + stream().map(partner -> modelMapper.map(partner, PartnerDto.class)).collect(Collectors.toList()), + HttpStatusCode.valueOf(200)); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/AddressDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/AddressDto.java new file mode 100644 index 00000000..b06bfd35 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/AddressDto.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.masterdata.logic.dto; + +import lombok.*; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class AddressDto implements Comparable { + + private String bpna; + private String streetAndNumber; + private String zipCodeAndCity; + private String country; + + @Override + public int compareTo(AddressDto o) { + return bpna.compareTo(o.bpna); + } + + @Override + public int hashCode() { + return bpna.hashCode(); + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof AddressDto) { + return bpna.equals(((AddressDto) other).bpna); + } + return false; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialDto.java index 34c15d12..b45cf117 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialDto.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialDto.java @@ -32,6 +32,9 @@ @AllArgsConstructor @ToString @NoArgsConstructor +/** + * This Dto class is being used within the ProductStockSammDto. + */ public class MaterialDto implements Serializable { private UUID uuid; diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialEntityDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialEntityDto.java new file mode 100644 index 00000000..1b4b5f6f --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/MaterialEntityDto.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.masterdata.logic.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class MaterialEntityDto { + + private boolean materialFlag; + private boolean productFlag; + private String ownMaterialNumber; + private String materialNumberCx; + private String name; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/PartnerDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/PartnerDto.java index a5a03b84..58bc2627 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/PartnerDto.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/PartnerDto.java @@ -21,13 +21,12 @@ */ package org.eclipse.tractusx.puris.backend.masterdata.logic.dto; -import jakarta.annotation.Nullable; import lombok.*; -import org.eclipse.tractusx.puris.backend.stock.logic.dto.PartnerProductStockDto; -import org.eclipse.tractusx.puris.backend.stock.logic.dto.ProductStockDto; import java.io.Serializable; -import java.util.*; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; @Getter @Setter @@ -41,6 +40,8 @@ public class PartnerDto implements Serializable { private String edcUrl; private String bpnl; - private String siteBpns; + + private SortedSet addresses = new TreeSet<>(); + private SortedSet sites = new TreeSet<>(); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/SiteDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/SiteDto.java new file mode 100644 index 00000000..e1bba57f --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/dto/SiteDto.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. + * (represented by Fraunhofer ISST) + * Copyright (c) 2023 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.masterdata.logic.dto; + +import lombok.*; + +import java.util.HashSet; +import java.util.Set; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class SiteDto implements Comparable { + private String bpns; + private String name; + private Set addresses = new HashSet<>(); + + @Override + public int compareTo(SiteDto o) { + return bpns.compareTo(o.bpns); + } + + @Override + public int hashCode() { + return bpns.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof SiteDto) { + return bpns.equals(((SiteDto) other).bpns); + } + return false; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialService.java index faa179f5..96afb531 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialService.java @@ -43,6 +43,8 @@ public interface MaterialService { Material findByMaterialNumberCx(String materialNumberCx); + List findAll(); + // Material findMaterialByMaterialNumberCustomer(String materialNumberCustomer); // // Material findProductByMaterialNumberCustomer(String materialNumberCustomer); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialServiceImpl.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialServiceImpl.java index 6ade631f..08d1fb09 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialServiceImpl.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/MaterialServiceImpl.java @@ -90,4 +90,9 @@ public Material findByMaterialNumberCx(String materialNumberCx) { } + @Override + public List findAll() { + return materialRepository.findAll(); + } + } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerService.java index d2e84e51..c05ecac5 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerService.java @@ -36,6 +36,8 @@ public interface PartnerService { List findAllSupplierPartnersForMaterialId(String ownMaterialNumber); + List findAll(); + Partner update(Partner partner); Partner findByBpnl(String bpnl); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java index 5e069e6c..3134bb98 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java @@ -29,9 +29,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.net.URL; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.regex.Pattern; @Service @Slf4j @@ -49,21 +51,74 @@ public class PartnerServiceImpl implements PartnerService { @Autowired private VariablesService variablesService; + private Pattern bpnlPattern = Pattern.compile("^BPNL[0-9]{10}[A-Z]{2}$"); + private Pattern bpnsPattern = Pattern.compile("^BPNS[0-9]{10}[A-Z]{2}$"); + private Pattern bpnaPattern = Pattern.compile("^BPNA[0-9]{10}[A-Z]{2}$"); @Override public Partner create(Partner partner) { + if (!testConstraints(partner)) { + log.error("Could not create Partner " + partner.getBpnl() + " because of constraint violation"); + return null; + } if (partner.getUuid() == null) { return partnerRepository.save(partner); } - var searchResult = partnerRepository.findById(partner.getUuid()); if (searchResult.isEmpty()) { - return partnerRepository.save(partner); + if (partnerRepository.findFirstByBpnl(partner.getBpnl()).isEmpty()) { + return partnerRepository.save(partner); + } } log.error("Could not create Partner " + partner.getBpnl() + " because it already existed before"); return null; } + + private boolean testConstraints(Partner partner) { + // Each Partner needs a BPNL, a name, an edcUrl and a BPNA or a BPNS (containing a BPNA) + boolean validData = bpnlPattern.matcher(partner.getBpnl()).matches(); + if (!validData) { + log.error("Invalid BPNL: " + partner.getBpnl()); + } + int addressCount = 0; + try { + new URL(partner.getEdcUrl()).toURI(); + } catch (Exception e) { + validData = false; + log.error("Invalid EDC URL: " + partner.getEdcUrl()); + } + boolean nameExists = partner.getName() != null && !partner.getName().isEmpty(); + if (!nameExists) { + log.error("Missing name of partner"); + } + validData = validData && nameExists; + for (var site : partner.getSites()) { + boolean validBpns = bpnsPattern.matcher(site.getBpns()).matches(); + if (!validBpns) { + log.error("Invalid BPNS: " + site.getBpns()); + } + validData = validData && validBpns; + for (var address : site.getAddresses()) { + boolean validBpna = bpnaPattern.matcher(address.getBpna()).matches(); + if (!validBpna) { + log.error("Invalid BPNA: " + address.getBpna()); + } + validData = validData && validBpna; + addressCount++; + } + } + for (var address : partner.getAddresses()) { + boolean validBpna = bpnaPattern.matcher(address.getBpna()).matches(); + validData = validData && validBpna; + addressCount++; + } + if (addressCount<1) { + log.error("No BPNA given for Partner " + partner.getBpnl()); + } + return validData && addressCount > 0; + } + @Override public Partner findByUuid(UUID partnerUuid) { return partnerRepository.findById(partnerUuid).orElse(null); @@ -83,8 +138,18 @@ public List findAllSupplierPartnersForMaterialId(String ownMaterialNumb return List.of(); } + @Override + public List findAll() { + return partnerRepository.findAll(); + } + @Override public Partner update(Partner partner) { + if (!testConstraints(partner)) { + log.error("Could not update Partner " + partner.getBpnl() + " because of constraint violation"); + return null; + } + Optional existingPartner = partnerRepository.findById(partner.getUuid()); if (existingPartner.isPresent()) { diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/FrontendMaterialDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/FrontendMaterialDto.java index 89a55009..1dbf11fe 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/FrontendMaterialDto.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/dto/FrontendMaterialDto.java @@ -23,11 +23,13 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @AllArgsConstructor +@NoArgsConstructor public class FrontendMaterialDto { String ownMaterialNumber; String description;