Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priyanka amarnani related party mapping processor[5xx] #3259

Open
wants to merge 13 commits into
base: 5.x.x
Choose a base branch
from
Open
17 changes: 17 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# _Mapping Update - Related party role mapper_

_Background_

The Party Role mapping issue involved the incorrect transfer of FpML's relatedParty structure into CDM, particularly in cases where multiple relatedParty elements exist within the same partyTradeInformation block. The mapping process was only capturing the first relatedParty role found, which led to incorrect associations between party references and roles. Furthermore, if the role of the first relatedParty was not found in PartyRoleEnum, another role was incorrectly assigned, causing mismatches and inaccuracies in the data mapping.


_What is being released?_

- We are introducing a new RelatedPartyRoleMappingProcessor that addresses the limitations of the previous implementation. This processor evaluates all relatedParty elements within a partyTradeInformation block instead of just mapping the first one. It ensures that each relatedParty is independently assessed, verifies its role against the PartyRoleEnum list, and assigns the correct role and reference accordingly. Additionally, if a role is not found in PartyRoleEnum, the processor omits that reference rather than assigning an incorrect role to the relatedParty.

_Review directions_

In Rosetta, select the Textual Browser and inspect each of the changes identified above.

Changes can be reviewed in PR: #3259

# _CDM Model - TaxonomySourceEnum_

_Background_
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package cdm.event.common.processor;

import cdm.base.staticdata.party.PartyRole;
import cdm.base.staticdata.party.PartyRoleEnum;
import cdm.base.staticdata.party.metafields.ReferenceWithMetaParty;
import cdm.event.common.Trade;
import com.regnosys.rosetta.common.translation.*;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.path.RosettaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.subPath;
import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.updateMappingSuccess;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;

/**
* Created by Tradeheader, SL
*
* @author gopazoTH
* @since 18/09/2024
*/

@SuppressWarnings("unused")
public class RelatedPartyRoleMappingProcessor extends MappingProcessor {

private static final Logger LOGGER = LoggerFactory.getLogger(RelatedPartyRoleMappingProcessor.class);
private final SynonymToEnumMap synonymToEnumMap;

public RelatedPartyRoleMappingProcessor(RosettaPath modelPath, List<Path> synonymPaths, MappingContext context) {
super(modelPath, synonymPaths, context);
this.synonymToEnumMap = context.getSynonymToEnumMap();
}

@Override
public void map(Path synonymPath, List<? extends RosettaModelObjectBuilder> builder, RosettaModelObjectBuilder parent) {

Collection<PartyRoleMapping> partyRoleMappings = getMappings().stream()
// find all partyTradeInformation.relatedParty mappings
.filter(m -> synonymPath.getParent().nameStartMatches(m.getXmlPath()))
// group by relatedParty path index
.collect(Collectors.groupingBy(this::getKey, collectingAndThen(toList(), this::toPartyRoleMapping)))
.values();

partyRoleMappings.forEach(m -> {
Trade.TradeBuilder tradeBuilder = (Trade.TradeBuilder) parent;

// Check if m.role is non-null before calling fromDisplayName
if (m != null && m.role != null) {
// Normalize the XML value to match the enum constant (convert to uppercase and replace spaces/underscores)
PartyRoleEnum roleEnum = synonymToEnumMap.getEnumValue(PartyRoleEnum.class, m.role);

// Create the PartyRoleBuilder and add the necessary values
PartyRole.PartyRoleBuilder partyRoleBuilder = PartyRole.builder()
.setRole(roleEnum);

// Check if m.partyReference is non-null before setting it
if (m.partyReference != null) {
partyRoleBuilder.setPartyReference(ReferenceWithMetaParty.builder()
.setExternalReference(m.partyReference));
}

// Check if m.ownershipPartyReference is non-null before setting it
if (m.ownershipPartyReference != null) {
partyRoleBuilder.setOwnershipPartyReference(ReferenceWithMetaParty.builder()
.setExternalReference(m.ownershipPartyReference));
}

// Add the PartyRole to the trade
tradeBuilder.addPartyRole(partyRoleBuilder);

// Update mappings if they are non-null
if (m.partyReferenceMapping != null && m.partyReferenceModelPath != null) {
updateMappingSuccess(m.partyReferenceMapping, m.partyReferenceModelPath);
}
if (m.roleMapping != null && m.roleModelPath != null) {
updateMappingSuccess(m.roleMapping, m.roleModelPath);
}
}
});
}

/**
* Group by path index, e.g. collect all mappings under relatedParty(index)
* (nonpublicExecutionReport.trade.tradeHeader.partyTradeInformation(0).relatedParty(10))
*/
private String getKey(Mapping m) {
int partyTradeInformationIndex = getPathIndex(m, "partyTradeInformation");
int relatedPartyIndex = getPathIndex(m, "relatedParty");
return partyTradeInformationIndex + "-" + relatedPartyIndex;
}

private int getPathIndex(Mapping m, String elementName) {
return subPath(elementName, m.getXmlPath())
.map(Path::getLastElement)
.map(Path.PathElement::forceGetIndex)
.orElse(-1);
}

private PartyRoleMapping toPartyRoleMapping(List<Mapping> mappings) {
try {
if (!mappings.isEmpty()) {
// Get the xmlPath from the first element in the mappings list
String xmlPath = mappings.get(0).getXmlPath().toString();

// Extract the index value from partyTradeInformation(x) in the xmlPath
Matcher matcher = Pattern.compile(".*partyTradeInformation\\((\\d+)\\).*").matcher(xmlPath);

if (matcher.find()) {
String index = matcher.group(1); // This captures the index (x) from partyTradeInformation(x)

// Dynamically build the pattern using the extracted index
Pattern dynamicPattern = Pattern.compile(".*partyTradeInformation\\(" + index + "\\)\\.partyReference.*");

// Apply the dynamic pattern to filter mappings
Mapping ownershipPartyReferenceMapping = getMapping(
getMappings().stream()
.filter(m -> m.getXmlValue() != null && dynamicPattern.matcher(m.getXmlPath().toString()).matches())
.collect(Collectors.toList()),
Path.parse("partyReference.href")
);

// Proceed with your existing mapping logic
RosettaPath ownershipPartyReferenceModelPath = getModelPath().newSubPath("ownershipPartyReference").newSubPath("externalReference");
Mapping partyReferenceMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("partyReference.href"));
RosettaPath partyReferenceModelPath = getModelPath().newSubPath("partyReference").newSubPath("externalReference");
Mapping roleMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("role"));
RosettaPath roleModelPath = getModelPath().newSubPath("role");

// Build and return the PartyRoleMapping if the mappings are valid
if (partyReferenceMapping != null && partyReferenceMapping.getXmlValue() != null
&& roleMapping != null && roleMapping.getXmlValue() != null) {
return new PartyRoleMapping(
String.valueOf(ownershipPartyReferenceMapping.getXmlValue()),
ownershipPartyReferenceMapping,
ownershipPartyReferenceModelPath,
String.valueOf(partyReferenceMapping.getXmlValue()),
partyReferenceMapping,
partyReferenceModelPath,
String.valueOf(roleMapping.getXmlValue()),
roleMapping,
roleModelPath
);
}
}
}
} catch (Exception e) {
LOGGER.error("Failed to build party role mapping from mappings {}", mappings, e);
}
return null;
}


private Mapping getMapping(List<Mapping> mappings, Path endsWith) {
return mappings.stream().filter(m -> m.getXmlPath().endsWith(endsWith)).findFirst().orElse(null);
}

private static class PartyRoleMapping {

private final String ownershipPartyReference;
private final Mapping ownershipPartyReferenceMapping;
private final RosettaPath ownershipPartyReferenceModelPath;
private final String partyReference;
private final Mapping partyReferenceMapping;
private final RosettaPath partyReferenceModelPath;
private final String role;
private final Mapping roleMapping;
private final RosettaPath roleModelPath;

public PartyRoleMapping(String ownershipPartyReference, Mapping ownershipPartyReferenceMapping, RosettaPath ownershipPartyReferenceModelPath, String partyReference, Mapping partyReferenceMapping, RosettaPath partyReferenceModelPath, String role, Mapping roleMapping, RosettaPath roleModelPath) {
this.ownershipPartyReference = ownershipPartyReference;
this.ownershipPartyReferenceMapping = ownershipPartyReferenceMapping;
this.ownershipPartyReferenceModelPath = ownershipPartyReferenceModelPath;
this.partyReference = partyReference;
this.partyReferenceMapping = partyReferenceMapping;
this.partyReferenceModelPath = partyReferenceModelPath;
this.role = role;
this.roleMapping = roleMapping;
this.roleModelPath = roleModelPath;
}
}
}
Loading