Skip to content

Commit e17e51d

Browse files
engalarclaude
andcommitted
fix: address review feedback on user targeting PR
- DESCRIBE output now emits explicit TARGETING USERS for user targeting types (symmetry with TARGETING GROUPS) - Add version history comment for UserSource vs UserTargeting field names - Add explicit NoUserSource/NoUserTargeting case in parseUserSource - Swap XPathConstraint/XPath priority to match writer output - Add doctype-test for all targeting variants - Update quick reference with GROUPS modifier Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0fa61f5 commit e17e51d

File tree

4 files changed

+123
-9
lines changed

4 files changed

+123
-9
lines changed

docs/01-project/MDL_QUICK_REFERENCE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ Nested folders use `/` separator: `'Parent/Child/Grandchild'`. Missing folders a
276276
| Drop workflow | `DROP WORKFLOW Module.Name;` | |
277277

278278
**Workflow Activity Types:**
279-
- `USER TASK <name> '<caption>' [PAGE Mod.Page] [TARGETING MICROFLOW Mod.MF] [OUTCOMES '<out>' { } ...];`
279+
- `USER TASK <name> '<caption>' [PAGE Mod.Page] [TARGETING [USERS|GROUPS] MICROFLOW Mod.MF] [TARGETING [USERS|GROUPS] XPATH '<expr>'] [OUTCOMES '<out>' { } ...];`
280280
- `CALL MICROFLOW Mod.MF [COMMENT '<text>'] [OUTCOMES '<out>' { } ...];`
281281
- `CALL WORKFLOW Mod.WF [COMMENT '<text>'];`
282282
- `DECISION ['<caption>'] OUTCOMES '<out>' { } ...;`
@@ -312,8 +312,8 @@ Modify an existing workflow's properties, activities, outcomes, paths, condition
312312
| Set parameter | `SET PARAMETER $Var: Module.Entity` | Workflow context parameter |
313313
| Set activity page | `SET ACTIVITY name PAGE Module.Page` | Change user task page |
314314
| Set activity description | `SET ACTIVITY name DESCRIPTION 'text'` | Activity description |
315-
| Set activity targeting | `SET ACTIVITY name TARGETING MICROFLOW Module.MF` | Target user assignment |
316-
| Set activity XPath | `SET ACTIVITY name TARGETING XPATH '[expr]'` | XPath targeting |
315+
| Set activity targeting | `SET ACTIVITY name TARGETING [USERS\|GROUPS] MICROFLOW Module.MF` | Target user/group assignment |
316+
| Set activity XPath | `SET ACTIVITY name TARGETING [USERS\|GROUPS] XPATH '[expr]'` | XPath targeting |
317317
| Set activity due date | `SET ACTIVITY name DUE DATE 'expr'` | Activity-level due date |
318318
| Insert activity | `INSERT AFTER name CALL MICROFLOW Module.MF` | Insert after named activity |
319319
| Drop activity | `DROP ACTIVITY name` | Remove activity by name |
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
-- ============================================================================
2+
-- Workflow User Targeting — all targeting variants
3+
-- ============================================================================
4+
--
5+
-- Covers USER TASK and MULTI USER TASK with:
6+
-- TARGETING USERS MICROFLOW (user targeting via microflow)
7+
-- TARGETING USERS XPATH (user targeting via XPath)
8+
-- TARGETING GROUPS MICROFLOW (group targeting via microflow)
9+
-- TARGETING GROUPS XPATH (group targeting via XPath)
10+
-- TARGETING MICROFLOW (legacy short form, still valid)
11+
-- TARGETING XPATH (legacy short form, still valid)
12+
--
13+
-- ============================================================================
14+
15+
-- Prerequisites
16+
17+
CREATE PERSISTENT ENTITY WFTarget.Request (
18+
Status: String(200)
19+
);
20+
21+
-- Targeting microflows: (System.Workflow, context) -> List of System.User
22+
CREATE MICROFLOW WFTarget.ACT_GetUsers (
23+
$Workflow: System.Workflow,
24+
$Context: WFTarget.Request
25+
)
26+
RETURNS List of System.User AS $Users
27+
BEGIN
28+
@position(200,200)
29+
RETRIEVE $Users FROM System.User;
30+
@position(400,200) RETURN $Users;
31+
END;
32+
/
33+
34+
CREATE MICROFLOW WFTarget.ACT_GetGroups (
35+
$Workflow: System.Workflow,
36+
$Context: WFTarget.Request
37+
)
38+
RETURNS List of System.UserGroup AS $Groups
39+
BEGIN
40+
@position(200,200)
41+
RETRIEVE $Groups FROM System.UserGroup;
42+
@position(400,200) RETURN $Groups;
43+
END;
44+
/
45+
46+
CREATE PAGE WFTarget.TaskPage (
47+
Title: 'Task Page',
48+
Layout: Atlas_Core.Atlas_Default,
49+
Params: { $WorkflowUserTask: System.WorkflowUserTask }
50+
) {
51+
LAYOUTGRID g1 {
52+
ROW r1 {
53+
COLUMN c1 (DesktopWidth: 12) {
54+
DYNAMICTEXT txt1 (Content: 'Task', RenderMode: H2)
55+
}
56+
}
57+
}
58+
}
59+
60+
-- Workflow with all targeting variants
61+
62+
CREATE WORKFLOW WFTarget.TargetingDemo
63+
PARAMETER $WorkflowContext: WFTarget.Request
64+
BEGIN
65+
-- TARGETING USERS MICROFLOW (explicit user targeting)
66+
USER TASK ByUserMF 'User microflow targeting'
67+
PAGE WFTarget.TaskPage
68+
TARGETING USERS MICROFLOW WFTarget.ACT_GetUsers
69+
OUTCOMES 'Done' { };
70+
71+
-- TARGETING USERS XPATH (explicit user xpath targeting)
72+
USER TASK ByUserXP 'User xpath targeting'
73+
PAGE WFTarget.TaskPage
74+
TARGETING USERS XPATH '[Name = ''admin'']'
75+
OUTCOMES 'Done' { };
76+
77+
-- TARGETING GROUPS MICROFLOW (group microflow targeting)
78+
USER TASK ByGroupMF 'Group microflow targeting'
79+
PAGE WFTarget.TaskPage
80+
TARGETING GROUPS MICROFLOW WFTarget.ACT_GetGroups
81+
OUTCOMES 'Done' { };
82+
83+
-- TARGETING GROUPS XPATH (group xpath targeting)
84+
USER TASK ByGroupXP 'Group xpath targeting'
85+
PAGE WFTarget.TaskPage
86+
TARGETING GROUPS XPATH '[Name = ''Administrators'']'
87+
OUTCOMES 'Done' { };
88+
89+
-- TARGETING MICROFLOW (legacy short form)
90+
USER TASK LegacyMF 'Legacy microflow targeting'
91+
PAGE WFTarget.TaskPage
92+
TARGETING MICROFLOW WFTarget.ACT_GetUsers
93+
OUTCOMES 'Done' { };
94+
95+
-- TARGETING XPATH (legacy short form)
96+
USER TASK LegacyXP 'Legacy xpath targeting'
97+
PAGE WFTarget.TaskPage
98+
TARGETING XPATH '[Name = ''admin'']'
99+
OUTCOMES 'Done' { };
100+
101+
-- MULTI USER TASK with group targeting
102+
MULTI USER TASK MultiByGroup 'Multi group targeting'
103+
PAGE WFTarget.TaskPage
104+
TARGETING GROUPS MICROFLOW WFTarget.ACT_GetGroups
105+
OUTCOMES 'Done' { };
106+
107+
END WORKFLOW;
108+
/

mdl/executor/cmd_workflows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,11 @@ func formatUserTask(a *workflows.UserTask, indent string) []string {
425425
switch us := a.UserSource.(type) {
426426
case *workflows.MicroflowBasedUserSource:
427427
if us.Microflow != "" {
428-
lines = append(lines, fmt.Sprintf("%s TARGETING MICROFLOW %s", indent, us.Microflow))
428+
lines = append(lines, fmt.Sprintf("%s TARGETING USERS MICROFLOW %s", indent, us.Microflow))
429429
}
430430
case *workflows.XPathBasedUserSource:
431431
if us.XPath != "" {
432-
lines = append(lines, fmt.Sprintf("%s TARGETING XPATH '%s'", indent, us.XPath))
432+
lines = append(lines, fmt.Sprintf("%s TARGETING USERS XPATH '%s'", indent, us.XPath))
433433
}
434434
case *workflows.MicroflowGroupSource:
435435
if us.Microflow != "" {

sdk/mpr/parser_workflow.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,13 +571,19 @@ func parseConditionOutcomes(v any) []workflows.ConditionOutcome {
571571
}
572572

573573
// parseUserSource parses a UserSource from raw BSON data.
574+
// Mendix versions before 10.12 use "UserSource" BSON field with $Type names like
575+
// "Workflows$MicroflowBasedUserSource". Mendix 10.12+ uses "UserTargeting" field
576+
// with $Type names like "Workflows$MicroflowUserTargeting". Both are supported.
574577
func parseUserSource(raw map[string]any) workflows.UserSource {
575578
if raw == nil {
576579
return &workflows.NoUserSource{}
577580
}
578581

579582
typeName := extractString(raw["$Type"])
580583
switch typeName {
584+
case "Workflows$NoUserSource", "Workflows$NoUserTargeting":
585+
return &workflows.NoUserSource{}
586+
581587
case "Workflows$MicroflowBasedUserSource", "Workflows$MicroflowUserTargeting":
582588
source := &workflows.MicroflowBasedUserSource{}
583589
if mf, ok := raw["Microflow"].(string); ok {
@@ -590,10 +596,10 @@ func parseUserSource(raw map[string]any) workflows.UserSource {
590596

591597
case "Workflows$XPathBasedUserSource", "Workflows$XPathUserTargeting":
592598
source := &workflows.XPathBasedUserSource{}
593-
if xpath, ok := raw["XPath"].(string); ok {
599+
if xpath, ok := raw["XPathConstraint"].(string); ok {
594600
source.XPath = xpath
595601
}
596-
if xpath, ok := raw["XPathConstraint"].(string); ok && source.XPath == "" {
602+
if xpath, ok := raw["XPath"].(string); ok && source.XPath == "" {
597603
source.XPath = xpath
598604
}
599605
return source
@@ -607,10 +613,10 @@ func parseUserSource(raw map[string]any) workflows.UserSource {
607613

608614
case "Workflows$XPathGroupTargeting":
609615
source := &workflows.XPathGroupSource{}
610-
if xpath, ok := raw["XPath"].(string); ok {
616+
if xpath, ok := raw["XPathConstraint"].(string); ok {
611617
source.XPath = xpath
612618
}
613-
if xpath, ok := raw["XPathConstraint"].(string); ok && source.XPath == "" {
619+
if xpath, ok := raw["XPath"].(string); ok && source.XPath == "" {
614620
source.XPath = xpath
615621
}
616622
return source

0 commit comments

Comments
 (0)