Skip to content

Commit

Permalink
feat: added retreival of parttypeinformation for materials
Browse files Browse the repository at this point in the history
  • Loading branch information
eschrewe committed Mar 14, 2024
1 parent f30c142 commit 40965ea
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private void setupCustomerRole() throws JsonProcessingException {

MaterialPartnerRelation semiconductorPartnerRelation = new MaterialPartnerRelation(semiconductorMaterial,
supplierPartner, semiconductorMatNbrSupplier, true, false);
semiconductorPartnerRelation.setPartnerCXNumber(semiconductorMatNbrCatenaX);
// semiconductorPartnerRelation.setPartnerCXNumber(semiconductorMatNbrCatenaX);
mprService.create(semiconductorPartnerRelation);
semiconductorPartnerRelation = mprService.find(semiconductorMaterial, supplierPartner);
log.info("Found Relation: " + semiconductorPartnerRelation);
Expand Down Expand Up @@ -248,7 +248,7 @@ private void setupSupplierRole() {

MaterialPartnerRelation semiconductorPartnerRelation = new MaterialPartnerRelation(semiconductorMaterial,
customerPartner, semiconductorMatNbrCustomer, false, true);
semiconductorPartnerRelation.setPartnerCXNumber(semiconductorMatNbrCatenaX);
// semiconductorPartnerRelation.setPartnerCXNumber(semiconductorMatNbrCatenaX);
semiconductorPartnerRelation = mprService.create(semiconductorPartnerRelation);

log.info("Created Relation " + semiconductorPartnerRelation);
Expand Down Expand Up @@ -411,7 +411,7 @@ private Material getNewSemiconductorMaterialForSupplier() {
private Material getNewSemiconductorMaterialForCustomer() {
Material material = new Material();
material.setOwnMaterialNumber(semiconductorMatNbrCustomer);
material.setMaterialNumberCx(semiconductorMatNbrCatenaX);
// material.setMaterialNumberCx(semiconductorMatNbrCatenaX);
material.setMaterialFlag(true);
material.setName("Semiconductor");
return material;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public JsonNode createMaterialRegistrationRequestBody(MaterialPartnerRelation ma

var itemStockRequestSubmodelObject = createItemStockSubmodelObject();
submodelDescriptorsArray.add(itemStockRequestSubmodelObject);
log.info("Created body for material " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
log.debug("Created body for material " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
return body;
}

Expand Down Expand Up @@ -131,7 +131,7 @@ public JsonNode createProductRegistrationRequestBody(Material material, String p
var itemStockRequestSubmodelObject = createItemStockSubmodelObject();
submodelDescriptorsArray.add(itemStockRequestSubmodelObject);

log.info("Created body for product " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
log.debug("Created body for product " + material.getOwnMaterialNumber() + "\n" + body.toPrettyString());
return body;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ public boolean registerAssetsInitially() {
public boolean createPolicyAndContractDefForPartner(Partner partner) {
boolean result = createPolicyDefinitionForPartner(partner);
result &= createDtrContractDefinitionForPartner(partner);
result &= registerPartTypeAssetForPartner(partner);
result &= createPartTypeInfoContractDefForPartner(partner);
result &= createContractDefinitionForPartner(partner, DT_ApiMethodEnum.REQUEST);
result &= createContractDefinitionForPartner(partner, DT_ApiMethodEnum.STATUS_REQUEST);
return result & createContractDefinitionForPartner(partner, DT_ApiMethodEnum.RESPONSE);
Expand Down Expand Up @@ -186,6 +188,21 @@ private boolean createDtrContractDefinitionForPartner(Partner partner) {
}
}

private boolean createPartTypeInfoContractDefForPartner(Partner partner) {
var body = edcRequestBodyBuilder.buildPartTypeInfoContractDefinitionForPartner(partner);
try (var response = sendPostRequest(body, List.of("v2", "contractdefinitions"))) {
if (!response.isSuccessful()) {
log.warn("Contract definition registration failed for partner " + partner.getBpnl() + " and PartTypeInfo asset");
return false;
}
log.info("Contract definition successful for PartTypeAsset and partner " + partner.getBpnl());
return true;
} catch (Exception e) {
log.error("Contract definition registration failed for partner " + partner.getBpnl() + " and PartTypeInfo asset", e);
return false;
}
}


/**
* Registers a policy definition that allows only the given partner's
Expand Down Expand Up @@ -242,6 +259,7 @@ private boolean registerDtrAsset() {
if (response.body() != null) {
log.warn("Response: \n" + response.body().string());
}
return false;
}
return true;
} catch (Exception e) {
Expand All @@ -250,6 +268,24 @@ private boolean registerDtrAsset() {
}
}

private boolean registerPartTypeAssetForPartner(Partner partner) {
var body = edcRequestBodyBuilder.buildPartTypeInfoRegistrationBody(partner);
try (var response = sendPostRequest(body, List.of("v3", "assets"))) {
if (!response.isSuccessful()) {
log.warn("Asset registration failed for PartTypeAsset and partner " + partner.getBpnl());
if (response.body() != null) {
log.warn("Response: \n" + response.body().string());
}
return false;
}
log.info("Asset registration successful for PartTypeAsset and partner " + partner.getBpnl());
return true;
} catch (Exception e) {
log.error("Failed to register PartTypeAsset for partner " + partner.getBpnl());
return false;
}
}

/**
* Util method to register an API asset to your control plane.
*
Expand Down Expand Up @@ -277,11 +313,12 @@ private boolean registerApiAsset(DT_ApiMethodEnum apiMethod) {
/**
* Retrieve the response to an unfiltered catalog request from the partner
* with the given dspUrl
* @param dspUrl The dspUrl of your partner
* @return The response containing the full catalog, if successful
*
* @param dspUrl The dspUrl of your partner
* @return The response containing the full catalog, if successful
*/
public Response getCatalogResponse(String dspUrl) throws IOException {
return sendPostRequest(edcRequestBodyBuilder.buildBasicCatalogRequestBody(dspUrl, null), List.of("v2", "catalog", "request"));
return sendPostRequest(edcRequestBodyBuilder.buildBasicCatalogRequestBody(dspUrl, null), List.of("v2", "catalog", "request"));
}

/**
Expand Down Expand Up @@ -432,6 +469,113 @@ public Response postProxyPullRequest(String url, String authKey, String authCode
}
}

public Response getProxyPullRequest(String url, String authKey, String authCode, String[] pathParams) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
for (var pathSegment : pathParams) {
urlBuilder.addPathSegment(pathSegment);
}
try {
var request = new Request.Builder()
.get()
.url(urlBuilder.build())
.header(authKey, authCode)
.build();
return CLIENT.newCall(request).execute();
} catch (Exception e) {
log.error("ProxyPull GET Request failed ", e);
return null;
}
}

/**
* Tries to negotiate for the PartTypeInfo-Api with the given partner
* and also tries to initiate the transfer of the edr token to the given endpoint.
* <p>
* It will return a String array of length 4. The authKey is stored under index 0, the
* authCode under index 1, the endpoint under index 2 and the contractId under index 3.
*
* @param partner the partner
* @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive
*/
public String[] getContractForPartTypeInfoApi(Partner partner) {
try {
var responseNode = getCatalog(partner.getEdcUrl());
var catalogArray = responseNode.get("dcat:dataset");
// If there is exactly one asset, the catalogContent will be a JSON object.
// In all other cases catalogContent will be a JSON array.
// For the sake of uniformity we will embed a single object in an array.
if (catalogArray.isObject()) {
catalogArray = objectMapper.createArrayNode().add(catalogArray);
}
JsonNode targetCatalogEntry = null;
for (var entry : catalogArray) {
var dctTypeObject = entry.get("dct:type");
if (dctTypeObject != null) {
if (("https://w3id.org/catenax/taxonomy#UniqueIdPushConnectToParentNotification").equals(dctTypeObject.get("@id").asText())) {
if ("2.0".equals(entry.get("https://w3id.org/catenax/ontology/common#version").asText())) {
if (targetCatalogEntry == null) {
targetCatalogEntry = entry;
} else {
log.warn("Ambiguous catalog entries found! \n" + catalogArray.toPrettyString());
}
}
}
}
}
if (targetCatalogEntry == null) {
log.error("Could not find api asset for PartTypeInformation at partner " + partner.getBpnl() + " 's catalog");
return null;
}
String assetApiId = targetCatalogEntry.get("@id").asText();
JsonNode negotiationResponse = initiateNegotiation(partner, targetCatalogEntry);
String negotiationId = negotiationResponse.get("@id").asText();
// Await confirmation of contract and contractId
String contractId = null;
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
var responseObject = getNegotiationState(negotiationId);
if ("FINALIZED".equals(responseObject.get("edc:state").asText())) {
contractId = responseObject.get("edc:contractAgreementId").asText();
break;
}
}
if (contractId == null) {
var negotiationState = getNegotiationState(negotiationId);
log.warn("no contract id, last negotiation state: \n" + negotiationState.toPrettyString());
log.error("Failed to obtain " + assetApiId + " from " + partner.getEdcUrl());
return null;
}

// Initiate transfer of edr
var transferResp = initiateProxyPullTransfer(partner, contractId, assetApiId);
String transferId = transferResp.get("@id").asText();
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
transferResp = getTransferState(transferId);
if ("STARTED".equals(transferResp.get("edc:state").asText())) {
break;
}
}

// Await arrival of edr
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
EDR_Dto edr_Dto = edrService.findByTransferId(transferId);
if (edr_Dto != null) {
log.info("Successfully negotiated for " + assetApiId + " with " + partner.getEdcUrl());
return new String[]{edr_Dto.authKey(), edr_Dto.authCode(), edr_Dto.endpoint(), contractId};
}
}
log.warn("did not receive authCode");
log.error("Failed to obtain " + assetApiId + " from " + partner.getEdcUrl());
return null;

} catch (Exception e) {
log.error("Failed to get contract for PartTypeInfo api from " + partner.getBpnl(), e);
return null;
}
}

/**
* Tries to negotiate for the given api Method with the given partner
* and also tries to initiate the transfer of the edr token to the given endpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ public JsonNode buildDtrContractDefinitionForPartner(Partner partner) {
return body;
}

public JsonNode buildPartTypeInfoContractDefinitionForPartner(Partner partner) {
var body = getEdcContextObject();
body.put("@id", partner.getBpnl() +"_contractdefinition_for_PartTypeInfoAsset");
body.put("accessPolicyId", getBpnPolicyId(partner));
body.put("contractPolicyId", getBpnPolicyId(partner));
var assetsSelector = MAPPER.createObjectNode();
body.set("assetsSelector", assetsSelector);
assetsSelector.put("@type", "CriterionDto");
assetsSelector.put("operandLeft", EDC_NAMESPACE + "id");
assetsSelector.put("operator", "=");
assetsSelector.put("operandRight", getPartTypeInfoAssetId(partner));
return body;
}

/**
* This method helps to ensure that the buildContractDefinitionWithBpnRestrictedPolicy uses the
* same policy-id as the one that is created with the buildContractDefinitionWithBpnRestrictedPolicy
Expand Down Expand Up @@ -338,10 +352,42 @@ public JsonNode buildDtrRegistrationBody() {
return body;
}

public JsonNode buildPartTypeInfoRegistrationBody(Partner partner) {
var body = getAssetRegistrationContext();
body.put("@id", getPartTypeInfoAssetId(partner));
var propertiesObject = MAPPER.createObjectNode();
body.set("properties", propertiesObject);
var dctTypeObject = MAPPER.createObjectNode();
propertiesObject.set("dct:type", dctTypeObject);
dctTypeObject.put("@id", "cx-taxo:UniqueIdPushConnectToParentNotification");
propertiesObject.put("cx-common:version", "2.0");
propertiesObject.put("asset:prop:type", "foo.bar.PartTypeProp");
var dataAddress = MAPPER.createObjectNode();
String url = variablesService.getParttypeInformationServerendpoint();
if (!url.endsWith("/")) {
url += "/";
}
url += partner.getBpnl();
dataAddress.put("@type", "DataAddress");
dataAddress.put("proxyPath", "true");
dataAddress.put("proxyQueryParams", "false");
dataAddress.put("proxyMethod", "false");
dataAddress.put("type", "HttpData");
dataAddress.put("baseUrl", url);
dataAddress.put("authKey", "x-api-key");
dataAddress.put("authCode", variablesService.getApiKey());
body.set("dataAddress", dataAddress);
return body;
}

private String getDtrAssetId() {
return "DigitalTwinRegistryId@" + variablesService.getOwnBpnl();
}

private String getPartTypeInfoAssetId(Partner partner) {
return "PartTypeInformationApi_" + partner.getBpnl();
}

private ObjectNode getAssetRegistrationContext() {
var body = MAPPER.createObjectNode();
var context = MAPPER.createObjectNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(
// any request in spring context
(authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers("/stockView/**", "/partners/**", "/materials/**", "/materialpartnerrelations/**", "/item-stock/**", "/edrendpoint/**", "/edc/**").authenticated()
.requestMatchers("/stockView/**", "/partners/**", "/materials/**", "/materialpartnerrelations/**", "/item-stock/**", "/edrendpoint/**", "/edc/**", "/parttype/**").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 @@ -116,6 +116,9 @@ public class VariablesService {
*/
private String dtrUrl;

@Value("${puris.parttypeinformation.serverendpoint}")
private String parttypeInformationServerendpoint;

@Value("${puris.generatematerialcatenaxid}")
/**
* A flag that signals whether the MaterialService
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.eclipse.tractusx.puris.backend.masterdata.controller;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.tractusx.puris.backend.common.util.PatternStore;
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.adapter.PartTypeInformationSammMapper;
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.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.regex.Pattern;

@RestController
@RequestMapping("parttype")
@Slf4j
public class PartTypeInformationController {
static Pattern bpnlPattern = PatternStore.BPNL_PATTERN;
static Pattern materialNumberPattern = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN;

@Autowired
private PartnerService partnerService;
@Autowired
private MaterialService materialService;
@Autowired
private MaterialPartnerRelationService mprService;
@Autowired
private PartTypeInformationSammMapper sammMapper;

@GetMapping("/{bpnl}/{materialnumber}")
public ResponseEntity<?> getMapping(@PathVariable String bpnl, @PathVariable String materialnumber) {
if (!bpnlPattern.matcher(bpnl).matches() || !materialNumberPattern.matcher(materialnumber).matches()) {
return ResponseEntity.badRequest().build();
}
Partner partner = partnerService.findByBpnl(bpnl);
if (partner == null) {
return ResponseEntity.status(401).build();
}
log.info(bpnl + " requests part type information on " + materialnumber);
Material material = materialService.findByOwnMaterialNumber(materialnumber);
if (material == null || !material.isProductFlag()) {
return ResponseEntity.status(404).build();
}
var mpr = mprService.find(material, partner);
if (mpr == null || !mpr.isPartnerBuysMaterial()) {
return ResponseEntity.status(404).build();
}
var samm = sammMapper.productToSamm(material);
return ResponseEntity.ok(samm);
}
}
Loading

0 comments on commit 40965ea

Please sign in to comment.