You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ossie.apache.org by jb...@apache.org on 2026/10/16 09:11:08 UTC
(ossie) branch main updated: fix(salesforce): rebuild each relationship's join criteria independently (#593)
This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ossie.git
The following commit(s) were added to refs/heads/main by this push:
new 464f3968 fix(salesforce): rebuild each relationship's join criteria independently (#593)
464f3968 is described below
commit 464f3968a91150ab2dec091ab2b4c62c3ea598e9
Author: RyutoYoda❄️ <98...@users.noreply.github.com>
AuthorDate: Fri Oct 16 18:11:00 2026 +0900
fix(salesforce): rebuild each relationship's join criteria independently (#593)
---
.../converter/RelationshipMappingHandler.java | 14 ++--
.../apache/ossie/MetricExportIntegrationTest.java | 9 +-
.../ossie/OssieToSalesforceConverterTest.java | 97 ++++++++++++++++++++++
3 files changed, 110 insertions(+), 10 deletions(-)
diff --git a/converters/salesforce/src/main/java/org/apache/ossie/converter/RelationshipMappingHandler.java b/converters/salesforce/src/main/java/org/apache/ossie/converter/RelationshipMappingHandler.java
index 08ce6fd1..07c2c829 100644
--- a/converters/salesforce/src/main/java/org/apache/ossie/converter/RelationshipMappingHandler.java
+++ b/converters/salesforce/src/main/java/org/apache/ossie/converter/RelationshipMappingHandler.java
@@ -94,10 +94,12 @@ public class RelationshipMappingHandler implements PipelineStep {
outputData.putAll(mappedData);
- // Apply manual mappings (criteria)
+ // Apply manual mappings (criteria). The Salesforce relationships were mapped from
+ // the filtered list, so criteria have to be rebuilt from that same list: pairing
+ // them with the unfiltered one shifts every relationship that follows a dropped one.
List<Object> sfRelationships = getList(outputData, SEMANTIC_RELATIONSHIPS);
if (sfRelationships != null) {
- reconstructCriteria(ossieRelationships, sfRelationships);
+ reconstructCriteria(validRelationships, sfRelationships);
}
customExtensionHandler.restoreCustomExtensionsAtLevel(outputData, sourceData, Level.RELATIONSHIPS);
@@ -216,19 +218,21 @@ public class RelationshipMappingHandler implements PipelineStep {
Object fromColumnsObj = ossieRel.get(FROM_COLUMNS);
Object toColumnsObj = ossieRel.get(TO_COLUMNS);
+ // Each relationship is rebuilt on its own: a relationship whose join columns
+ // cannot be paired gets no criteria, and the ones that follow still get theirs.
if (fromColumnsObj == null || toColumnsObj == null) {
- return;
+ continue;
}
List<Object> fromColumns = asList(fromColumnsObj);
List<Object> toColumns = asList(toColumnsObj);
if (fromColumns.isEmpty() || toColumns.isEmpty()) {
- return;
+ continue;
}
if (fromColumns.size() != toColumns.size()) {
- return;
+ continue;
}
List<Map<String, Object>> criteriaArray = new ArrayList<>();
diff --git a/converters/salesforce/src/test/java/org/apache/ossie/MetricExportIntegrationTest.java b/converters/salesforce/src/test/java/org/apache/ossie/MetricExportIntegrationTest.java
index 4c496d99..418acef4 100644
--- a/converters/salesforce/src/test/java/org/apache/ossie/MetricExportIntegrationTest.java
+++ b/converters/salesforce/src/test/java/org/apache/ossie/MetricExportIntegrationTest.java
@@ -346,7 +346,7 @@ class MetricExportIntegrationTest {
}
@Test
- void metricCannotUseJoinCriteriaCorruptedByInheritedRelationshipFiltering() throws Exception {
+ void metricKeepsItsJoinCriteriaWhenAnEarlierRelationshipIsFiltered() throws Exception {
Map<String, Object> source = model("sales", List.of(metric("combined", "ANSI_SQL",
"SUM(orders.profit) + SUM(returns.profit)")));
Map<String, Object> returns = new LinkedHashMap<>(items(source, "datasets").get(0));
@@ -358,12 +358,11 @@ class MetricExportIntegrationTest {
source.put("relationships", List.of(valid));
assertTrue(measurements(convertOne(source)).get(0).get("expression").toString().contains("[returns].[profit]"));
+ // A relationship ahead of it is dropped for naming a column that does not exist. The
+ // surviving relationship keeps its own join columns, so the metric resolves unchanged.
source.put("relationships", List.of(Map.of("name", "removed_first", "from", "orders", "to", "returns",
"from_columns", List.of("missing"), "to_columns", List.of("customer_id")), valid));
- var error = assertThrows(ConversionException.class, () -> convertOne(source));
- assertTrue(error.getMessage().contains("combined"), error.getMessage());
- assertTrue(error.getMessage().contains("orders_returns"), error.getMessage());
- assertTrue(error.getMessage().contains("join key correspondence"), error.getMessage());
+ assertTrue(measurements(convertOne(source)).get(0).get("expression").toString().contains("[returns].[profit]"));
}
private Map<String, Object> convertOne(Map<String, Object> model) throws IOException {
diff --git a/converters/salesforce/src/test/java/org/apache/ossie/OssieToSalesforceConverterTest.java b/converters/salesforce/src/test/java/org/apache/ossie/OssieToSalesforceConverterTest.java
index e9938834..90a19dc3 100644
--- a/converters/salesforce/src/test/java/org/apache/ossie/OssieToSalesforceConverterTest.java
+++ b/converters/salesforce/src/test/java/org/apache/ossie/OssieToSalesforceConverterTest.java
@@ -251,6 +251,103 @@ class OssieToSalesforceConverterTest {
assertTrue(hasValidOrdersProducts, "Orders_Products should be included");
}
+ /**
+ * Builds a model whose first relationship is dropped by the validator, because its
+ * join column is not a field of the entity it names, while the second one is valid.
+ * Export has to carry the second relationship's own endpoints and columns across.
+ */
+ private static String ossieModelWithAFilteredRelationshipFirst() {
+ return "version: 0.2.0.dev0\n"
+ + "name: Imported_Model\n"
+ + "datasets:\n"
+ + dataset("Customers", "customer_id")
+ + dataset("Orders", "product_id")
+ + dataset("Products", "product_id")
+ + "relationships:\n"
+ + relationship("Customers_Orders", "Customers", "Orders", "missing_column")
+ + relationship("Orders_Products", "Orders", "Products", "product_id");
+ }
+
+ private static String dataset(String name, String... columns) {
+ StringBuilder yaml = new StringBuilder("- name: " + name + "\n"
+ + " source: " + name + "__dll\n"
+ + " fields:\n");
+ for (String column : columns) {
+ yaml.append(" - name: ").append(column).append("\n")
+ .append(" datatype: String\n")
+ .append(" dimension: {}\n")
+ .append(" expression:\n")
+ .append(" dialects:\n")
+ .append(" - dialect: ANSI_SQL\n")
+ .append(" expression: ").append(column).append("\n");
+ }
+ return yaml.toString();
+ }
+
+ private static String relationship(String name, String from, String to, String column) {
+ return "- name: " + name + "\n"
+ + " from: " + from + "\n"
+ + " to: " + to + "\n"
+ + " from_columns: [" + column + "]\n"
+ + " to_columns: [" + column + "]\n";
+ }
+
+ @Test
+ void testRelationshipAfterAFilteredOneKeepsItsOwnEndpointsAndCriteria() throws Exception {
+ List<String> results = converter.convert(ossieModelWithAFilteredRelationshipFirst());
+ Map<String, Object> sfModel = jsonMapper.readValue(results.get(0), new TypeReference<Map<String, Object>>() {});
+
+ List<Map<String, Object>> relationships = (List<Map<String, Object>>) sfModel.get("semanticRelationships");
+ assertNotNull(relationships);
+ assertEquals(1, relationships.size(), "Only the valid relationship should be exported");
+
+ Map<String, Object> exported = relationships.get(0);
+ assertEquals("Orders_Products", exported.get("apiName"));
+ assertEquals("Orders", exported.get("leftSemanticDefinitionApiName"));
+ assertEquals("Products", exported.get("rightSemanticDefinitionApiName"));
+
+ List<Map<String, Object>> criteria = (List<Map<String, Object>>) exported.get("criteria");
+ assertNotNull(criteria);
+ assertEquals(1, criteria.size());
+ assertEquals("product_id", criteria.get(0).get("leftSemanticFieldApiName"));
+ assertEquals("product_id", criteria.get(0).get("rightSemanticFieldApiName"));
+ }
+
+ @Test
+ void testRelationshipWithUnpairableJoinColumnsLeavesTheNextOneIntact() throws Exception {
+ // The first relationship names two join columns on one side and one on the other, so
+ // there is no criterion to build for it. The second one still gets its own criteria.
+ String model = "version: 0.2.0.dev0\n"
+ + "name: Imported_Model\n"
+ + "datasets:\n"
+ + dataset("Customers", "customer_id", "tenant_id")
+ + dataset("Orders", "customer_id", "product_id")
+ + dataset("Products", "product_id")
+ + "relationships:\n"
+ + "- name: Customers_Orders\n"
+ + " from: Customers\n"
+ + " to: Orders\n"
+ + " from_columns: [customer_id, tenant_id]\n"
+ + " to_columns: [customer_id]\n"
+ + relationship("Orders_Products", "Orders", "Products", "product_id");
+
+ List<String> results = converter.convert(model);
+ Map<String, Object> sfModel = jsonMapper.readValue(results.get(0), new TypeReference<Map<String, Object>>() {});
+
+ List<Map<String, Object>> relationships = (List<Map<String, Object>>) sfModel.get("semanticRelationships");
+ assertNotNull(relationships);
+ Map<String, Object> exported = relationships.stream()
+ .filter(relationship -> "Orders_Products".equals(relationship.get("apiName")))
+ .findFirst()
+ .orElseThrow();
+
+ List<Map<String, Object>> criteria = (List<Map<String, Object>>) exported.get("criteria");
+ assertNotNull(criteria, "The relationship after an unpairable one keeps its criteria");
+ assertEquals(1, criteria.size());
+ assertEquals("product_id", criteria.get(0).get("leftSemanticFieldApiName"));
+ assertEquals("product_id", criteria.get(0).get("rightSemanticFieldApiName"));
+ }
+
@Test
void testAModelWhoseRelationshipsAreAllFilteredExportsNone() throws Exception {
// Dropping the last valid relationship used to leave the relationship mappings