Extract actual entity IDs from row links for service calls
When pseudoId differs from the internal productId (e.g., DEMO_VAR_GR_SM_COPY
vs 100204), models were using the display ID for service calls causing FK errors.
Now extracts the actual entity ID from the row's link URL and includes it in
the row data:
- compact: {id: 'DEMO_VAR_GR_SM_COPY', productId: '100204', link: '...'}
- aria: {ref: 'DEMO_VAR_GR_SM_COPY', productId: '100204', ...}
This is a generic solution that works for any entity type by parsing the
primary key parameter from the Edit link URL.
Fixes QA issue where models used pseudoId for applyProductFeatures causing
'record does not exist' referential integrity errors.
Showing
1 changed file
with
55 additions
and
17 deletions
| ... | @@ -975,20 +975,41 @@ def convertToAriaTree = { semanticState, targetScreenPath -> | ... | @@ -975,20 +975,41 @@ def convertToAriaTree = { semanticState, targetScreenPath -> |
| 975 | gridNode.children = [] | 975 | gridNode.children = [] |
| 976 | listData.each { row -> | 976 | listData.each { row -> |
| 977 | def rowNode = [role: "row"] | 977 | def rowNode = [role: "row"] |
| 978 | // Extract key identifying info - prefer specific IDs over generic productId | 978 | // Extract display ID - prefer human-readable pseudoId for display |
| 979 | // For features: productFeatureId, for assocs: toProductId, etc. | 979 | def displayId = row.pseudoId ?: row.toProductId ?: row.productFeatureId ?: |
| 980 | def id = row.pseudoId ?: row.toProductId ?: row.productFeatureId ?: | ||
| 981 | row.partyId ?: row.orderId ?: row.id | 980 | row.partyId ?: row.orderId ?: row.id |
| 982 | // Avoid using productId as ref when it's the same for all rows (e.g., feature list) | 981 | // Avoid using productId as ref when it's the same for all rows (e.g., feature list) |
| 983 | if (!id && row.productId) { | 982 | if (!displayId && row.productId) { |
| 984 | // Only use productId if there's no better identifier | 983 | // Only use productId if there's no better identifier |
| 985 | id = row.productId | 984 | displayId = row.productId |
| 986 | } | 985 | } |
| 987 | def name = row.combinedName ?: row.name ?: row.productName ?: | 986 | def name = row.combinedName ?: row.name ?: row.productName ?: |
| 988 | row.description ?: row.abbrev | 987 | row.description ?: row.abbrev |
| 989 | if (id) rowNode.ref = id | 988 | if (displayId) rowNode.ref = displayId |
| 990 | if (name) rowNode.name = name | 989 | if (name) rowNode.name = name |
| 991 | if (id && name && id != name) rowNode.description = id | 990 | if (displayId && name && displayId != name) rowNode.description = displayId |
| 991 | |||
| 992 | // Extract the primary key from the row's link URL (if any) | ||
| 993 | // This is the actual entity ID that services need, which may differ from pseudoId | ||
| 994 | // Match by displayId in path OR by link text (handles pseudoId != productId case) | ||
| 995 | def rowLink = data.links?.find { link -> | ||
| 996 | link.type == "navigation" && link.path?.contains("Edit") && ( | ||
| 997 | link.path?.contains(displayId?.toString()) || | ||
| 998 | link.text == displayId?.toString() || | ||
| 999 | link.text == name | ||
| 1000 | ) | ||
| 1001 | } | ||
| 1002 | if (rowLink?.path) { | ||
| 1003 | def matcher = rowLink.path =~ /(\w+Id)=([^&]+)/ | ||
| 1004 | if (matcher.find()) { | ||
| 1005 | def paramName = matcher.group(1) | ||
| 1006 | def paramValue = matcher.group(2) | ||
| 1007 | if (paramValue && paramValue != displayId?.toString()) { | ||
| 1008 | rowNode[paramName] = paramValue | ||
| 1009 | } | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | |||
| 992 | gridNode.children << rowNode | 1013 | gridNode.children << rowNode |
| 993 | } | 1014 | } |
| 994 | } | 1015 | } |
| ... | @@ -1211,17 +1232,16 @@ def convertToCompactFormat = { semanticState, targetScreenPath -> | ... | @@ -1211,17 +1232,16 @@ def convertToCompactFormat = { semanticState, targetScreenPath -> |
| 1211 | gridInfo.rows = listData.take(10).collect { row -> | 1232 | gridInfo.rows = listData.take(10).collect { row -> |
| 1212 | def rowInfo = [:] | 1233 | def rowInfo = [:] |
| 1213 | 1234 | ||
| 1214 | // Get identifying info - prefer specific IDs over generic productId | 1235 | // Get display ID - prefer human-readable pseudoId for display |
| 1215 | // For features: productFeatureId, for assocs: toProductId, etc. | 1236 | def displayId = row.pseudoId ?: row.toProductId ?: row.productFeatureId ?: |
| 1216 | def id = row.pseudoId ?: row.toProductId ?: row.productFeatureId ?: | ||
| 1217 | row.partyId ?: row.orderId ?: row.communicationEventId ?: row.id | 1237 | row.partyId ?: row.orderId ?: row.communicationEventId ?: row.id |
| 1218 | // Avoid using productId as id when it's the same for all rows (e.g., feature list) | 1238 | // Avoid using productId as displayId when it's the same for all rows (e.g., feature list) |
| 1219 | if (!id && row.productId) id = row.productId | 1239 | if (!displayId && row.productId) displayId = row.productId |
| 1220 | def name = row.combinedName ?: row.productName ?: row.organizationName ?: | 1240 | def name = row.combinedName ?: row.productName ?: row.organizationName ?: |
| 1221 | row.subject ?: row.name ?: row.description ?: row.abbrev | 1241 | row.subject ?: row.name ?: row.description ?: row.abbrev |
| 1222 | 1242 | ||
| 1223 | if (id) rowInfo.id = id | 1243 | if (displayId) rowInfo.id = displayId |
| 1224 | if (name && name != id) rowInfo.name = name | 1244 | if (name && name != displayId) rowInfo.name = name |
| 1225 | 1245 | ||
| 1226 | // Add key display values (2-3 additional fields beyond id/name) | 1246 | // Add key display values (2-3 additional fields beyond id/name) |
| 1227 | // Priority: status, type, date, amount, role, class fields | 1247 | // Priority: status, type, date, amount, role, class fields |
| ... | @@ -1263,14 +1283,32 @@ def convertToCompactFormat = { semanticState, targetScreenPath -> | ... | @@ -1263,14 +1283,32 @@ def convertToCompactFormat = { semanticState, targetScreenPath -> |
| 1263 | 1283 | ||
| 1264 | if (extraFields) rowInfo.data = extraFields | 1284 | if (extraFields) rowInfo.data = extraFields |
| 1265 | 1285 | ||
| 1266 | // Find link for this row | 1286 | // Find link for this row and extract the primary key for service calls |
| 1287 | // Match by displayId in path OR by link text (handles pseudoId != productId case) | ||
| 1267 | def rowLinks = data.links?.findAll { link -> | 1288 | def rowLinks = data.links?.findAll { link -> |
| 1268 | link.path?.contains(id?.toString()) && link.type == "navigation" | 1289 | link.type == "navigation" && ( |
| 1290 | link.path?.contains(displayId?.toString()) || | ||
| 1291 | link.text == displayId?.toString() || | ||
| 1292 | link.text == name | ||
| 1293 | ) | ||
| 1269 | } | 1294 | } |
| 1270 | if (rowLinks && rowLinks.size() > 0) { | 1295 | if (rowLinks && rowLinks.size() > 0) { |
| 1271 | // Pick the most relevant link (edit/view) | 1296 | // Pick the most relevant link (edit/view) |
| 1272 | def editLink = rowLinks.find { it.path?.contains("Edit") } | 1297 | def editLink = rowLinks.find { it.path?.contains("Edit") } |
| 1273 | rowInfo.link = (editLink ?: rowLinks[0]).path | 1298 | def linkPath = (editLink ?: rowLinks[0]).path |
| 1299 | rowInfo.link = linkPath | ||
| 1300 | |||
| 1301 | // Extract the primary key from the link URL (e.g., productId=100204) | ||
| 1302 | // This is the actual entity ID that services need, which may differ from pseudoId | ||
| 1303 | def matcher = linkPath =~ /(\w+Id)=([^&]+)/ | ||
| 1304 | if (matcher.find()) { | ||
| 1305 | def paramName = matcher.group(1) | ||
| 1306 | def paramValue = matcher.group(2) | ||
| 1307 | // Only include if it differs from the display ID (avoids redundancy) | ||
| 1308 | if (paramValue && paramValue != displayId?.toString()) { | ||
| 1309 | rowInfo[paramName] = paramValue | ||
| 1310 | } | ||
| 1311 | } | ||
| 1274 | } | 1312 | } |
| 1275 | 1313 | ||
| 1276 | rowInfo | 1314 | rowInfo | ... | ... |
-
Please register or sign in to post a comment