Fix FTL macro errors and stabilize semantic state extraction.
- Resolve NonHashException in macros by avoiding .add() on sequences. - Fix 'Maps with null keys' JSON error by ensuring string keys. - Stabilize form macros with null-safe checks. - Update McpServices and CustomScreenTestImpl for better semantic data handling.
Showing
4 changed files
with
35 additions
and
40 deletions
| ... | @@ -79,9 +79,7 @@ | ... | @@ -79,9 +79,7 @@ |
| 79 | 79 | ||
| 80 | [${linkText}](${slashPath})<#t> | 80 | [${linkText}](${slashPath})<#t> |
| 81 | <#if mcpSemanticData??> | 81 | <#if mcpSemanticData??> |
| 82 | <#if !mcpSemanticData.links??><#assign dummy = mcpSemanticData.put("links", [])></#if> | 82 | <#assign dummy = ec.resource.expression("mcpSemanticData.links.add([text: '" + (linkText!"")?js_string + "', path: '" + (slashPath!"")?js_string + "', type: 'navigation'])", "")!> |
| 83 | <#assign linkInfo = {"text": linkText, "path": slashPath, "type": "navigation"}> | ||
| 84 | <#assign dummy = mcpSemanticData.links.add(linkInfo)> | ||
| 85 | </#if> | 83 | </#if> |
| 86 | </#if> | 84 | </#if> |
| 87 | </#macro> | 85 | </#macro> |
| ... | @@ -105,15 +103,10 @@ | ... | @@ -105,15 +103,10 @@ |
| 105 | <#assign formMap = ec.resource.expression(mapName, "")!> | 103 | <#assign formMap = ec.resource.expression(mapName, "")!> |
| 106 | 104 | ||
| 107 | <#if mcpSemanticData??> | 105 | <#if mcpSemanticData??> |
| 108 | <#if !mcpSemanticData.formMetadata??><#assign dummy = mcpSemanticData.put("formMetadata", {})></#if> | 106 | <#assign formName = (.node["@name"]!"")?string> |
| 109 | |||
| 110 | <#assign formMeta = {"name": (.node["@name"]!""), "map": mapName}> | ||
| 111 | <#assign fieldMetaList = []> | 107 | <#assign fieldMetaList = []> |
| 112 | 108 | <#assign dummy = ec.resource.expression("if (mcpSemanticData.formMetadata == null) mcpSemanticData.formMetadata = [:]; mcpSemanticData.formMetadata.put('" + formName?js_string + "', [name: '" + formName?js_string + "', map: '" + (mapName!"")?js_string + "'])", "")!> | |
| 113 | <#assign dummy = mcpSemanticData.formMetadata.put(.node["@name"], formMeta)> | ||
| 114 | </#if> | 109 | </#if> |
| 115 | |||
| 116 | <#if mcpSemanticData?? && formMap?has_content><#assign dummy = mcpSemanticData.put(.node["@name"], formMap)></#if> | ||
| 117 | <#t>${sri.pushSingleFormMapContext(mapName)} | 110 | <#t>${sri.pushSingleFormMapContext(mapName)} |
| 118 | <#list formNode["field"] as fieldNode> | 111 | <#list formNode["field"] as fieldNode> |
| 119 | <#assign fieldSubNode = ""> | 112 | <#assign fieldSubNode = ""> |
| ... | @@ -125,13 +118,13 @@ | ... | @@ -125,13 +118,13 @@ |
| 125 | <#if mcpSemanticData??> | 118 | <#if mcpSemanticData??> |
| 126 | <#assign fieldMeta = {"name": (fieldNode["@name"]!""), "title": (title!), "required": (fieldNode["@required"]! == "true")}> | 119 | <#assign fieldMeta = {"name": (fieldNode["@name"]!""), "title": (title!), "required": (fieldNode["@required"]! == "true")}> |
| 127 | 120 | ||
| 128 | <#if fieldSubNode["text-line"]?has_content><#assign dummy = fieldMeta.put("type", "text")></#if> | 121 | <#if fieldSubNode["text-line"]?has_content><#assign fieldMeta = fieldMeta + {"type": "text"}></#if> |
| 129 | <#if fieldSubNode["text-area"]?has_content><#assign dummy = fieldMeta.put("type", "textarea")></#if> | 122 | <#if fieldSubNode["text-area"]?has_content><#assign fieldMeta = fieldMeta + {"type": "textarea"}></#if> |
| 130 | <#if fieldSubNode["drop-down"]?has_content><#assign dummy = fieldMeta.put("type", "dropdown")></#if> | 123 | <#if fieldSubNode["drop-down"]?has_content><#assign fieldMeta = fieldMeta + {"type": "dropdown"}></#if> |
| 131 | <#if fieldSubNode["check"]?has_content><#assign dummy = fieldMeta.put("type", "checkbox")></#if> | 124 | <#if fieldSubNode["check"]?has_content><#assign fieldMeta = fieldMeta + {"type": "checkbox"}></#if> |
| 132 | <#if fieldSubNode["date-find"]?has_content><#assign dummy = fieldMeta.put("type", "date")></#if> | 125 | <#if fieldSubNode["date-find"]?has_content><#assign fieldMeta = fieldMeta + {"type": "date"}></#if> |
| 133 | 126 | ||
| 134 | <#assign dummy = fieldMetaList.add(fieldMeta)> | 127 | <#assign fieldMetaList = fieldMetaList + [fieldMeta]> |
| 135 | </#if> | 128 | </#if> |
| 136 | 129 | ||
| 137 | * **${title}**: <#recurse fieldSubNode> | 130 | * **${title}**: <#recurse fieldSubNode> |
| ... | @@ -139,7 +132,9 @@ | ... | @@ -139,7 +132,9 @@ |
| 139 | </#list> | 132 | </#list> |
| 140 | 133 | ||
| 141 | <#if mcpSemanticData?? && fieldMetaList?has_content> | 134 | <#if mcpSemanticData?? && fieldMetaList?has_content> |
| 142 | <#assign dummy = mcpSemanticData.formMetadata[.node["@name"]!].put("fields", fieldMetaList)> | 135 | <#assign formName = (.node["@name"]!"")?string> |
| 136 | <#assign dummy = ec.context.put("tempFieldMetaList", fieldMetaList)!> | ||
| 137 | <#assign dummy = ec.resource.expression("def formMeta = mcpSemanticData.formMetadata?.get('" + formName?js_string + "'); if (formMeta != null) formMeta.put('fields', tempFieldMetaList)", "")!> | ||
| 143 | </#if> | 138 | </#if> |
| 144 | 139 | ||
| 145 | <#t>${sri.popContext()} | 140 | <#t>${sri.popContext()} |
| ... | @@ -154,24 +149,17 @@ | ... | @@ -154,24 +149,17 @@ |
| 154 | <#assign totalItems = listObject?size> | 149 | <#assign totalItems = listObject?size> |
| 155 | 150 | ||
| 156 | <#if mcpSemanticData?? && listObject?has_content> | 151 | <#if mcpSemanticData?? && listObject?has_content> |
| 157 | <#assign truncatedList = listObject> | 152 | <#assign formName = (.node["@name"]!"")?string> |
| 158 | <#assign dummy = mcpSemanticData.put(.node["@name"], truncatedList)> | 153 | <#assign displayedItems = (totalItems > 50)?then(50, totalItems)> |
| 159 | 154 | <#assign isTruncated = (totalItems > 50)> | |
| 160 | <#if !mcpSemanticData.listMetadata??><#assign dummy = mcpSemanticData.put("listMetadata", {})></#if> | ||
| 161 | |||
| 162 | <#assign columnNames = []> | 155 | <#assign columnNames = []> |
| 163 | <#list formListColumnList as columnFieldList> | 156 | <#list formListColumnList as columnFieldList> |
| 164 | <#assign fieldNode = columnFieldList[0]> | 157 | <#assign fieldNode = columnFieldList[0]> |
| 165 | <#assign dummy = columnNames.add(fieldNode["@name"]!"")> | 158 | <#assign columnNames = columnNames + [fieldNode["@name"]!""]> |
| 166 | </#list> | 159 | </#list> |
| 167 | 160 | <#assign dummy = ec.context.put("tempListObject", listObject)!> | |
| 168 | <#assign dummy = mcpSemanticData.listMetadata.put(.node["@name"]!"", { | 161 | <#assign dummy = ec.context.put("tempColumnNames", columnNames)!> |
| 169 | "name": .node["@name"]!"", | 162 | <#assign dummy = ec.resource.expression("mcpSemanticData.put('" + formName?js_string + "', tempListObject); if (mcpSemanticData.listMetadata == null) mcpSemanticData.listMetadata = [:]; mcpSemanticData.listMetadata.put('" + formName?js_string + "', [name: '" + formName?js_string + "', totalItems: " + totalItems + ", displayedItems: " + displayedItems + ", truncated: " + isTruncated?string + ", columns: tempColumnNames])", "")!> |
| 170 | "totalItems": totalItems, | ||
| 171 | "displayedItems": (totalItems > 50)?then(50, totalItems), | ||
| 172 | "truncated": (totalItems > 50), | ||
| 173 | "columns": columnNames | ||
| 174 | })> | ||
| 175 | </#if> | 163 | </#if> |
| 176 | 164 | ||
| 177 | <#-- Header Row --> | 165 | <#-- Header Row --> |
| ... | @@ -227,8 +215,8 @@ | ... | @@ -227,8 +215,8 @@ |
| 227 | <#-- ================== Form Field Widgets ==================== --> | 215 | <#-- ================== Form Field Widgets ==================== --> |
| 228 | <#macro "check"> | 216 | <#macro "check"> |
| 229 | <#assign options = sri.getFieldOptions(.node)!> | 217 | <#assign options = sri.getFieldOptions(.node)!> |
| 230 | <#assign currentValue = sri.getFieldValueString(.node)> | 218 | <#assign currentValue = sri.getFieldValueString(.node)!> |
| 231 | <#t>${(options.get(currentValue))!(currentValue)} | 219 | <#t>${(options[currentValue])!currentValue} |
| 232 | </#macro> | 220 | </#macro> |
| 233 | 221 | ||
| 234 | <#macro "date-find"></#macro> | 222 | <#macro "date-find"></#macro> |
| ... | @@ -264,9 +252,9 @@ | ... | @@ -264,9 +252,9 @@ |
| 264 | </#macro> | 252 | </#macro> |
| 265 | 253 | ||
| 266 | <#macro "drop-down"> | 254 | <#macro "drop-down"> |
| 267 | <#assign options = sri.getFieldOptions(.node)> | 255 | <#assign options = sri.getFieldOptions(.node)!> |
| 268 | <#assign currentValue = sri.getFieldValueString(.node)> | 256 | <#assign currentValue = sri.getFieldValueString(.node)!> |
| 269 | <#t>${(options.get(currentValue))!(currentValue)} | 257 | <#t>${(options[currentValue])!currentValue} |
| 270 | </#macro> | 258 | </#macro> |
| 271 | 259 | ||
| 272 | <#macro "text-area"><#t>${sri.getFieldValueString(.node)}</#macro> | 260 | <#macro "text-area"><#t>${sri.getFieldValueString(.node)}</#macro> | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -321,6 +321,9 @@ class CustomScreenTestImpl implements McpScreenTest { | ... | @@ -321,6 +321,9 @@ class CustomScreenTestImpl implements McpScreenTest { |
| 321 | 321 | ||
| 322 | // Create a persistent map for semantic data that survives nested pops | 322 | // Create a persistent map for semantic data that survives nested pops |
| 323 | Map<String, Object> mcpSemanticData = new HashMap<>() | 323 | Map<String, Object> mcpSemanticData = new HashMap<>() |
| 324 | mcpSemanticData.put("links", new ArrayList<>()) | ||
| 325 | mcpSemanticData.put("formMetadata", new HashMap<>()) | ||
| 326 | mcpSemanticData.put("listMetadata", new HashMap<>()) | ||
| 324 | cs.put("mcpSemanticData", mcpSemanticData) | 327 | cs.put("mcpSemanticData", mcpSemanticData) |
| 325 | 328 | ||
| 326 | // create the WebFacadeStub using our custom method | 329 | // create the WebFacadeStub using our custom method | ... | ... |
| ... | @@ -80,9 +80,11 @@ class UiNarrativeBuilder { | ... | @@ -80,9 +80,11 @@ class UiNarrativeBuilder { |
| 80 | 80 | ||
| 81 | def forms = semanticState?.data | 81 | def forms = semanticState?.data |
| 82 | if (forms) { | 82 | if (forms) { |
| 83 | def formNames = forms.keySet().findAll { k -> k.contains('Form') || k.contains('form') }[0..2] | 83 | def maxForms = isTerse ? 2 : 10 |
| 84 | def formNames = forms.keySet().findAll { k -> k.contains('Form') || k.contains('form') } | ||
| 84 | if (formNames) { | 85 | if (formNames) { |
| 85 | def fields = getFormFieldNames(forms, formNames[0]) | 86 | def formNamesToDescribe = formNames.take(maxForms + 1) |
| 87 | def fields = getFormFieldNames(forms, formNamesToDescribe[0]) | ||
| 86 | if (fields) { | 88 | if (fields) { |
| 87 | sb.append("Form contains: ${fields.join(', ')}. ") | 89 | sb.append("Form contains: ${fields.join(', ')}. ") |
| 88 | } | 90 | } |
| ... | @@ -93,7 +95,8 @@ class UiNarrativeBuilder { | ... | @@ -93,7 +95,8 @@ class UiNarrativeBuilder { |
| 93 | if (links && links.size() > 0) { | 95 | if (links && links.size() > 0) { |
| 94 | def linkTypes = links.collect { l -> l.type?.toString() ?: 'navigation' }.unique() | 96 | def linkTypes = links.collect { l -> l.type?.toString() ?: 'navigation' }.unique() |
| 95 | if (linkTypes) { | 97 | if (linkTypes) { |
| 96 | sb.append("Available links: ${linkTypes.take(3).join(', ')}. ") | 98 | def maxTypes = isTerse ? 3 : 15 |
| 99 | sb.append("Available links: ${linkTypes.take(maxTypes).join(', ')}. ") | ||
| 97 | } | 100 | } |
| 98 | } | 101 | } |
| 99 | 102 | ||
| ... | @@ -141,7 +144,8 @@ class UiNarrativeBuilder { | ... | @@ -141,7 +144,8 @@ class UiNarrativeBuilder { |
| 141 | if (links && links.size() > 0) { | 144 | if (links && links.size() > 0) { |
| 142 | def sortedLinks = links.sort { a, b -> (a.text <=> b.text) } | 145 | def sortedLinks = links.sort { a, b -> (a.text <=> b.text) } |
| 143 | 146 | ||
| 144 | sortedLinks.take(5).each { link -> | 147 | def linksToTake = isTerse ? 5 : 50 |
| 148 | sortedLinks.take(linksToTake).each { link -> | ||
| 145 | def linkText = link.text?.toString() | 149 | def linkText = link.text?.toString() |
| 146 | def linkPath = link.path?.toString() | 150 | def linkPath = link.path?.toString() |
| 147 | def linkType = link.type?.toString() ?: 'navigation' | 151 | def linkType = link.type?.toString() ?: 'navigation' | ... | ... |
-
Please register or sign in to post a comment