Skip to content

Commit

Permalink
Merge pull request #156 from catenax-ng/release_dt_access_api_part_2
Browse files Browse the repository at this point in the history
feat | Digital twin access rule api use in digital twin processing
  • Loading branch information
almadigabor authored May 13, 2024
2 parents 021035a + 6565674 commit d8a8b6e
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 46 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Supporting new submodel Singlelevelbomasplanned.
- Support EDC 7.
- Added new files for digital twin access rule support.
- Refactor code for pcf, dt access API, EDC 7.
- Refactor code for pcf, dt access API, EDC 7.
- Dt access api use in digital twin processing

### Fixed
- Remove garbage character from 'edc_request_template' path. Fixed [#147](https://github.com/eclipse-tractusx/managed-simple-data-exchanger-backend/issues/147).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/********************************************************************************
* Copyright (c) 2024 T-Systems International GmbH
* 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.sde.core.submodel.executor.step;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.tractusx.sde.common.configuration.properties.SDEConfigurationProperties;
import org.eclipse.tractusx.sde.common.constants.SubmoduleCommonColumnsConstant;
import org.eclipse.tractusx.sde.common.entities.PolicyModel;
import org.eclipse.tractusx.sde.common.exception.NoDataFoundException;
import org.eclipse.tractusx.sde.common.exception.ServiceException;
import org.eclipse.tractusx.sde.common.submodel.executor.DatabaseUsecaseStep;
import org.eclipse.tractusx.sde.common.submodel.executor.Step;
import org.eclipse.tractusx.sde.common.utils.PolicyOperationUtil;
import org.eclipse.tractusx.sde.digitaltwins.facilitator.DigitalTwinsFacilitator;
import org.eclipse.tractusx.sde.digitaltwins.facilitator.DigitalTwinsUtility;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class DigitalTwinAccessRuleFacilator extends Step {

private final DigitalTwinsFacilitator digitalTwinFacilitator;

private final DigitalTwinsUtility digitalTwinsUtility;

private final SDEConfigurationProperties sdeConfigProperties;

@Qualifier("DatabaseUsecaseHandler")
private final DatabaseUsecaseStep databaseUseCaseStep;

private String createRuleTemplate = """
{
"validFrom": "2020-01-02T03:04:05Z",
"validTo": "4999-01-02T03:04:05Z",
"description": "ACME policy within set validity period",
"policyType": "AAS",
"policy": {
"accessRules": [
{
"attribute": "bpn",
"operator": "eq",
"value": "%s"
},
{
"attribute": "mandatorySpecificAssetIds",
"operator": "includes",
"values": [ %s ]
},
{
"attribute": "visibleSpecificAssetIdNames",
"operator": "includes",
"values": [ %s ]
},
{
"attribute": "visibleSemanticIds",
"operator": "includes",
"values": [
{
"attribute": "modelUrn",
"operator": "eq",
"value": "%s"
}
]
}
]
}
}
""";

@SneakyThrows
public void createAccessRule(Integer rowIndex, ObjectNode jsonObject, Map<String, String> specificAssetIds,
PolicyModel policy, String sematicId) {
try {

deleteAllExistingDTAccessRulesIfExist(rowIndex, jsonObject);

List<String> accessBPNList = PolicyOperationUtil.getAccessBPNList(policy);
List<String> accessruleIds = new ArrayList<>();

for (String bpn : accessBPNList) {

String requestTemplate = String.format(createRuleTemplate, bpn,
digitalTwinsUtility.createAccessRuleMandatorySpecificAssetIds(specificAssetIds),
digitalTwinsUtility.createAccessRuleVisibleSpecificAssetIdNames(specificAssetIds), sematicId);

JsonNode requestBody = new ObjectMapper().readTree(requestTemplate);

JsonNode response = digitalTwinFacilitator
.createAccessControlsRule(sdeConfigProperties.getManufacturerId(), requestBody);
accessruleIds.add(response.get("id").asText());
}

jsonObject.put(SubmoduleCommonColumnsConstant.SHELL_ACCESS_RULE_IDS, StringUtils.join(accessruleIds, ","));

} catch (Exception e) {
log.error(String.format("Row: %s: DigitalTwin exception: unable to create digital twin access rule %s",
rowIndex, specificAssetIds.toString()) + ", because: " + e.getMessage());
throw new ServiceException(
String.format("Row: %s: DigitalTwin exception: unable to create digital twin access rule %s",
rowIndex, specificAssetIds.toString()));
}
}

private void deleteAllExistingDTAccessRulesIfExist(Integer rowIndex, ObjectNode jsonObject) {

String identifier = getIdentifier(jsonObject, getIdentifierOfModel());
JsonObject datinRow = null;
try {
databaseUseCaseStep.init(getSubmodelSchema());
datinRow = databaseUseCaseStep.readCreatedTwinsDetails(identifier);

if (datinRow != null && !datinRow.get(SubmoduleCommonColumnsConstant.SHELL_ACCESS_RULE_IDS).isJsonNull()) {

String accessRules = datinRow.get(SubmoduleCommonColumnsConstant.SHELL_ACCESS_RULE_IDS).getAsString();

Arrays.asList(accessRules.split(",")).stream()
.filter(StringUtils::isNotBlank)
.forEach(str -> {
try {
str = str.trim();
digitalTwinFacilitator.deleteAccessControlsRule(accessRules,
sdeConfigProperties.getManufacturerId());
} catch (Exception e) {
log.error(String.format(
"Row: %s: DigitalTwin: unable to delete existing DT access rule identifier %s, access rule id %s", rowIndex,
identifier, str));
}
});
}
} catch (NoDataFoundException e) {
log.debug(String.format("Row: %s: DigitalTwin: no existing digital twin access rule data found %s", rowIndex,
identifier));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.tractusx.sde.common.configuration.properties.SDEConfigurationProperties;
Expand All @@ -34,6 +35,7 @@
import org.eclipse.tractusx.sde.common.submodel.executor.Step;
import org.eclipse.tractusx.sde.common.utils.JsonObjectUtility;
import org.eclipse.tractusx.sde.common.utils.UUIdGenerator;
import org.eclipse.tractusx.sde.digitaltwins.entities.common.Endpoint;
import org.eclipse.tractusx.sde.digitaltwins.entities.request.CreateSubModelRequest;
import org.eclipse.tractusx.sde.digitaltwins.entities.request.ShellDescriptorRequest;
import org.eclipse.tractusx.sde.digitaltwins.entities.request.ShellLookupRequest;
Expand All @@ -52,9 +54,11 @@
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

@Service("DigitalTwinUseCaseHandler")
@Service("digitalTwinUseCaseHandler")
@RequiredArgsConstructor
public class DigitalTwinUseCaseHandler extends Step implements DigitalTwinUsecaseStep {

private static final String FORWARD_SLASH = "/";

private final DigitalTwinsFacilitator digitalTwinFacilitator;

Expand All @@ -63,7 +67,9 @@ public class DigitalTwinUseCaseHandler extends Step implements DigitalTwinUsecas
private final SDEConfigurationProperties sdeConfigProperties;

private final DigitalTwinLookUpInRegistry digitalTwinLookUpInRegistry;


private final DigitalTwinAccessRuleFacilator digitalTwinAccessRuleFacilator;

public void delete(Integer rowIndex, JsonObject jsonObject, String delProcessId, String refProcessId) {
String shellId = JsonObjectUtility.getValueFromJsonObject(jsonObject, SubmoduleCommonColumnsConstant.SHELL_ID);
String submodelId = JsonObjectUtility.getValueFromJsonObject(jsonObject,
Expand Down Expand Up @@ -102,6 +108,9 @@ public JsonNode run(Integer rowIndex, ObjectNode jsonObject, String processId, P
SubModelResponse foundSubmodel = findSubmoduleInShells(jsonObject, List.of(shellId));

checkAndCreateSubmodulIfNotExist(rowIndex, jsonObject, shellId, aasDescriptorRequest, foundSubmodel);

digitalTwinAccessRuleFacilator.init(getSubmodelSchema());
digitalTwinAccessRuleFacilator.createAccessRule(rowIndex, jsonObject, specificAssetIds, policy, getsemanticIdOfModel());

} catch (Exception e) {
throw new CsvHandlerUseCaseException(rowIndex, ": DigitalTwins: " + e.getMessage());
Expand Down Expand Up @@ -157,25 +166,46 @@ private String createShell(Map<String, String> specificAssetIds, ShellDescriptor
public JsonNode checkAndCreateSubmodulIfNotExist(Integer rowIndex, ObjectNode jsonObject, String shellId,
ShellDescriptorRequest aasDescriptorRequest, SubModelResponse foundSubmodel) {

String identification = findIdentificationForSubmodule(rowIndex, jsonObject);
Map<String, String> identification = findIdentificationForSubmodule(rowIndex, jsonObject);

String path = getSubmoduleUriPathOfSubmodule();

String submodelRequestidentifier= identification.get("submodelRequestidentifier");
String submodelIdentifier = identification.get("submodelIdentifier");

if (StringUtils.isNotBlank(path)) {
path = FORWARD_SLASH + getNameOfModel() + FORWARD_SLASH + path + FORWARD_SLASH
+ submodelRequestidentifier;
}

CreateSubModelRequest createSubModelRequest = digitalTwinsUtility.getCreateSubModelRequest(shellId,
getsemanticIdOfModel(), getIdShortOfModel(), submodelIdentifier, path,
getSubmodelShortDescriptionOfModel());

if (foundSubmodel == null) {

logDebug(String.format("No submodels for '%s'", shellId));

CreateSubModelRequest createSubModelRequest = digitalTwinsUtility.getCreateSubModelRequest(shellId,
getsemanticIdOfModel(), getIdShortOfModel(), identification, getNameOfModel(),
getSubmoduleUriPathOfSubmodule(), getSubmodelShortDescriptionOfModel());

digitalTwinFacilitator.updateShellDetails(shellId, aasDescriptorRequest, createSubModelRequest);
jsonObject.put(SubmoduleCommonColumnsConstant.SUBMODULE_ID, createSubModelRequest.getId());

} else {
// There is no need to send submodel because of nothing to change in it so
// sending null of it
jsonObject.put(SubmoduleCommonColumnsConstant.SUBMODULE_ID, foundSubmodel.getId());
digitalTwinFacilitator.updateShellDetails(shellId, aasDescriptorRequest, null);
logDebug("Complete Digital Twins Update Update Digital Twins");
boolean isSubmodelRequestidentifierSame = false;
if(foundSubmodel.getEndpoints()!=null) {
isSubmodelRequestidentifierSame = foundSubmodel.getEndpoints().stream()
.map(Endpoint::getProtocolInformation)
.anyMatch(ele-> ele.getEndpointAddress().contains(submodelRequestidentifier));
}

if(!(foundSubmodel.getId().equals(submodelIdentifier)) || !isSubmodelRequestidentifierSame ) {
logDebug(String.format("Found submodel but need to update submodels for '%s'", shellId));
digitalTwinFacilitator.updateShellDetails(shellId, aasDescriptorRequest, createSubModelRequest);
jsonObject.put(SubmoduleCommonColumnsConstant.SUBMODULE_ID, createSubModelRequest.getId());
} else {
jsonObject.put(SubmoduleCommonColumnsConstant.SUBMODULE_ID, foundSubmodel.getId());
digitalTwinFacilitator.updateShellDetails(shellId, aasDescriptorRequest, null);
logDebug("Complete Digital Twins Update Update Digital Twins");
}
}

return jsonObject;
Expand Down Expand Up @@ -205,7 +235,9 @@ private SubModelResponse findSubmoduleInShells(ObjectNode jsonObject, List<Strin
List<JsonElement> allGlobalFiled = jArray.asList().stream()
.filter(ele -> {
JsonElement refElement = ele.getAsJsonObject().get("ref");
return refElement!= null && refElement.getAsString().equals("shellGlobalAssetId");
return refElement != null && refElement.isJsonPrimitive()
&& refElement.getAsJsonPrimitive().isString()
&& refElement.getAsJsonPrimitive().getAsString().equals("shellGlobalAssetId");
})
.toList();
if (!allGlobalFiled.isEmpty()) {
Expand All @@ -220,10 +252,13 @@ private SubModelResponse findSubmoduleInShells(ObjectNode jsonObject, List<Strin
}

@SneakyThrows
private String findIdentificationForSubmodule(Integer rowIndex, ObjectNode jsonObject) {

String identification = null;
private Map<String, String> findIdentificationForSubmodule(Integer rowIndex, ObjectNode jsonObject) {
String submodelIdentifier =null;
String identificationField = extractExactFieldName(getIdentifierOfModel());

List<String> databaseIdentifierFields = getDatabaseIdentifierSpecsOfModel();

JsonArray jArray = checkIsAutoPopulatedfieldsSubmodel();
List<JsonElement> allAutoPopulateField = null;

Expand All @@ -238,19 +273,28 @@ private String findIdentificationForSubmodule(Integer rowIndex, ObjectNode jsonO

for (JsonElement jsonElement : allAutoPopulateField) {
JsonObject asJsonObject = jsonElement.getAsJsonObject().get("ref").getAsJsonObject();
String shemaIdentificationKey = jsonElement.getAsJsonObject().get("key").getAsString();
String shemaIdentificationKey = extractExactFieldName(jsonElement.getAsJsonObject().get("key").getAsString());

String identificationLocal = findIdentificationForSubmodule(rowIndex, jsonObject, asJsonObject);
jsonObject.put(shemaIdentificationKey, identificationLocal);

if (identificationField.equals(shemaIdentificationKey)) {
identification = identificationLocal;
submodelIdentifier = identificationLocal;
}
}
} else {
identification = UUIdGenerator.getUrnUuid();
}

if(submodelIdentifier == null) {
submodelIdentifier = UUIdGenerator.getUrnUuid();
}

return identification;

String submodelRequestidentifier = getDatabaseIdentifierValues(jsonObject, databaseIdentifierFields);

Map<String, String> output= new TreeMap<>();
output.put("submodelIdentifier", submodelIdentifier);
output.put("submodelRequestidentifier", submodelRequestidentifier);

return output;
}

private String findIdentificationForSubmodule(Integer rowIndex, ObjectNode jsonObject, JsonObject asJsonObject) {
Expand Down Expand Up @@ -333,4 +377,4 @@ public ShellDescriptorRequest getShellDescriptorRequest(String nameAtManufacture
specificAssetIds, policy);
}

}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit d8a8b6e

Please sign in to comment.