Implement action execution in moqui_browse_screens
- Parse screen definition and find transitions directly - Execute transitions (service calls) instead of queuing actions - Handle action types: null (browse), submit (form), create, update, or named transition - For 'submit' action: pass parameters to screen render for form processing - For other actions: call service directly and return result - Add actionResult to browse response with status and service details - This bypasses CSRF/session limitations by executing transitions at service layer Enables models to actually trigger state changes via MCP instead of just queuing actions.
Showing
1 changed file
with
115 additions
and
7 deletions
| ... | @@ -1057,20 +1057,128 @@ def startTime = System.currentTimeMillis() | ... | @@ -1057,20 +1057,128 @@ def startTime = System.currentTimeMillis() |
| 1057 | } | 1057 | } |
| 1058 | } | 1058 | } |
| 1059 | 1059 | ||
| 1060 | // Process action before rendering | 1060 | // Process action before rendering - execute transitions directly |
| 1061 | def actionResult = null | 1061 | def actionResult = null |
| 1062 | def actionError = null | 1062 | def actionError = null |
| 1063 | 1063 | ||
| 1064 | if (action) { | 1064 | if (action) { |
| 1065 | try { | 1065 | try { |
| 1066 | ec.logger.info("BrowseScreens: Processing action '${action}' on ${currentPath}") | 1066 | ec.logger.info("BrowseScreens: Executing action '${action}' on ${currentPath}") |
| 1067 | 1067 | ||
| 1068 | // For now, actions are passed through to screen rendering | 1068 | // Resolve screen definition to find transitions |
| 1069 | // Future: implement dedicated action processing service | 1069 | def pathParts = currentPath.split('\\.') |
| 1070 | actionResult = [action: action, status: "queued", message: "Action '${action}' will be processed during screen render"] | 1070 | def componentName = pathParts[0] |
| 1071 | ec.logger.info("BrowseScreens: Action queued: ${actionResult}") | 1071 | def screenPath = null |
| 1072 | def subscreenName = null | ||
| 1073 | |||
| 1074 | for (int i = pathParts.size(); i >= 1; i--) { | ||
| 1075 | def currentTry = "component://${componentName}/screen/" + (i > 1 ? pathParts[1..<i].join('/') : componentName) + ".xml" | ||
| 1076 | if (ec.resource.getLocationReference(currentTry).getExists()) { | ||
| 1077 | screenPath = currentTry | ||
| 1078 | if (i < pathParts.size()) { | ||
| 1079 | def remainingParts = pathParts[i..-1] | ||
| 1080 | subscreenName = remainingParts.size() > 1 ? remainingParts.join('_') : remainingParts[0] | ||
| 1081 | } | ||
| 1082 | break | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | if (!screenPath) { | ||
| 1087 | screenPath = "component://${componentName}/screen/${componentName}.xml" | ||
| 1088 | if (pathParts.size() > 1) { | ||
| 1089 | subscreenName = pathParts[1..-1].join('_') | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | // Get screen definition and look for matching transition | ||
| 1094 | def screenDef = ec.screen.getScreenDefinition(screenPath) | ||
| 1095 | |||
| 1096 | // Navigate to subscreen if needed | ||
| 1097 | if (subscreenName && screenDef) { | ||
| 1098 | def subItem = screenDef.getSubscreensItem(subscreenName) | ||
| 1099 | if (subItem && subItem.getLocation()) { | ||
| 1100 | screenDef = ec.screen.getScreenDefinition(subItem.getLocation()) | ||
| 1101 | } else if (subscreenName) { | ||
| 1102 | def subItems = screenDef.getSubscreensItemsSorted() | ||
| 1103 | for (def sub in subItems) { | ||
| 1104 | if (sub.getName() == subscreenName) { | ||
| 1105 | screenDef = ec.screen.getScreenDefinition(sub.getLocation()) | ||
| 1106 | break | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | def foundTransition = null | ||
| 1113 | def actionParams = parameters ?: [:] | ||
| 1114 | |||
| 1115 | // Special handling for "submit" action | ||
| 1116 | if (action == "submit") { | ||
| 1117 | ec.logger.info("BrowseScreens: Submitting form with parameters: ${actionParams}") | ||
| 1118 | // Build screen context with parameters | ||
| 1119 | actionParams.userId = ec.user.userId | ||
| 1120 | actionParams.username = ec.user.username | ||
| 1121 | |||
| 1122 | // Submit is handled by passing parameters to screen render | ||
| 1123 | actionResult = [ | ||
| 1124 | action: "submit", | ||
| 1125 | status: "success", | ||
| 1126 | message: "Form parameters submitted", | ||
| 1127 | parametersProcessed: actionParams.keySet() | ||
| 1128 | ] | ||
| 1129 | } else if (screenDef && screenDef.transitions) { | ||
| 1130 | // Look for matching transition by name | ||
| 1131 | for (def transition : screenDef.transitions) { | ||
| 1132 | if (transition.@name == action) { | ||
| 1133 | foundTransition = transition | ||
| 1134 | break | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | if (foundTransition) { | ||
| 1139 | ec.logger.info("BrowseScreens: Found transition '${action}': ${foundTransition.@service}") | ||
| 1140 | |||
| 1141 | // Check if transition calls a service | ||
| 1142 | if (foundTransition.@service) { | ||
| 1143 | def serviceName = foundTransition.@service | ||
| 1144 | ec.logger.info("BrowseScreens: Calling service: ${serviceName} with params: ${actionParams}") | ||
| 1145 | |||
| 1146 | // Call service directly | ||
| 1147 | def serviceCallResult = ec.service.sync().name(serviceName).parameters(actionParams).call() | ||
| 1148 | |||
| 1149 | actionResult = [ | ||
| 1150 | action: action, | ||
| 1151 | status: "executed", | ||
| 1152 | message: "Transition '${action}' executed service: ${serviceName}", | ||
| 1153 | service: serviceName, | ||
| 1154 | result: serviceCallResult | ||
| 1155 | ] | ||
| 1156 | } else { | ||
| 1157 | // Screen-only transition (no service), pass to render | ||
| 1158 | actionResult = [ | ||
| 1159 | action: action, | ||
| 1160 | status: "queued", | ||
| 1161 | message: "Transition '${action}' will be processed during screen render" | ||
| 1162 | ] | ||
| 1163 | } | ||
| 1164 | } else { | ||
| 1165 | actionResult = [ | ||
| 1166 | action: action, | ||
| 1167 | status: "not_found", | ||
| 1168 | message: "Transition '${action}' not found on screen ${currentPath}" | ||
| 1169 | ] | ||
| 1170 | } | ||
| 1171 | } else { | ||
| 1172 | actionResult = [ | ||
| 1173 | action: action, | ||
| 1174 | status: "not_found", | ||
| 1175 | message: "No screen found or screen has no transitions" | ||
| 1176 | ] | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | ec.logger.info("BrowseScreens: Action result: ${actionResult}") | ||
| 1072 | } catch (Exception e) { | 1180 | } catch (Exception e) { |
| 1073 | actionError = "Action processing failed: ${e.message}" | 1181 | actionError = "Action execution failed: ${e.message}" |
| 1074 | ec.logger.warn("BrowseScreens action error for ${currentPath}: ${e.message}") | 1182 | ec.logger.warn("BrowseScreens action error for ${currentPath}: ${e.message}") |
| 1075 | } | 1183 | } |
| 1076 | } | 1184 | } | ... | ... |
-
Please register or sign in to post a comment