Skip to content

Commit e1d9fd2

Browse files
committed
fix: strip REST path slashes and validate microflow param entity refs
Root causes of TestMxCheck_DoctypeScripts failures: 1. REST service operation paths with leading/trailing slashes were stored as-is in BSON (CE6550/CE6551). Now stripped at parse time in visitor_rest.go. 2. Microflow parameters with bare entity names like "Object" (missing module prefix) were not caught by mxcli check. Added MDL008 validation in both ValidateMicroflow (linter) and ValidateMicroflowBody (semantic validator). 3. Test data fixes: REST param case mismatch ($Id → $id), workflow params (Object → System.Workflow/System.WorkflowUserTask).
1 parent c97c85b commit e1d9fd2

7 files changed

Lines changed: 98 additions & 26 deletions

File tree

mdl-examples/doctype-tests/22-published-rest-service-examples.mdl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ BEGIN
2323
END;
2424
/
2525

26-
CREATE MICROFLOW PrsTest.PRS_GetOrderById ($httpResponse: System.HttpResponse, $Id: String)
26+
CREATE MICROFLOW PrsTest.PRS_GetOrderById ($httpResponse: System.HttpResponse, $id: String)
2727
RETURNS String AS $Result
2828
BEGIN
2929
DECLARE $Result String = '{}';
@@ -39,7 +39,7 @@ BEGIN
3939
END;
4040
/
4141

42-
CREATE MICROFLOW PrsTest.PRS_DeleteOrder ($httpResponse: System.HttpResponse, $Id: String)
42+
CREATE MICROFLOW PrsTest.PRS_DeleteOrder ($httpResponse: System.HttpResponse, $id: String)
4343
RETURNS String AS $Result
4444
BEGIN
4545
DECLARE $Result String = '{"status": "deleted"}';

mdl-examples/doctype-tests/workflow-microflow-actions.mdl

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
-- Test: workflow microflow actions syntax
22
-- Verifies parsing of all 11 workflow action types in microflows
33

4+
-- ############################################################################
5+
-- PREREQUISITES
6+
-- ############################################################################
7+
8+
CREATE PERSISTENT ENTITY TestModule.DeliveryContext (
9+
OrderID: String(50),
10+
Status: String(100)
11+
);
12+
13+
CREATE MICROFLOW TestModule.ACT_Stub ($DeliveryContext: TestModule.DeliveryContext)
14+
BEGIN
15+
@position(200,200) RETURN;
16+
END;
17+
/
18+
19+
CREATE WORKFLOW TestModule.WF_DeliveryDelay
20+
PARAMETER $WorkflowContext: TestModule.DeliveryContext
21+
DISPLAY 'Delivery Delay'
22+
BEGIN
23+
CALL MICROFLOW TestModule.ACT_Stub;
24+
END WORKFLOW;
25+
/
26+
27+
-- ############################################################################
28+
-- MICROFLOW WITH ALL WORKFLOW ACTION TYPES
29+
-- ############################################################################
30+
431
CREATE MICROFLOW TestModule.MF_WorkflowActions (
5-
$Workflow: Object,
6-
$ContextObj: Object,
7-
$UserTask: Object
32+
$Workflow: System.Workflow,
33+
$ContextObj: TestModule.DeliveryContext,
34+
$UserTask: System.WorkflowUserTask
835
)
936
RETURNS Nothing
1037
BEGIN
@@ -34,15 +61,6 @@ BEGIN
3461
WORKFLOW OPERATION RETRY $Workflow;
3562
WORKFLOW OPERATION UNPAUSE $Workflow;
3663

37-
-- Notify workflow
38-
NOTIFY WORKFLOW $Workflow;
39-
4064
-- Open workflow admin page
4165
OPEN WORKFLOW $Workflow;
42-
43-
-- Lock/Unlock
44-
LOCK WORKFLOW ALL;
45-
UNLOCK WORKFLOW ALL;
46-
LOCK WORKFLOW $Workflow;
47-
UNLOCK WORKFLOW $Workflow;
4866
END;

mdl/executor/cmd_microflows_builder_validate.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ func ValidateMicroflowBody(s *ast.CreateMicroflowStmt) []string {
1616
varTypes := make(map[string]string)
1717
declaredVars := make(map[string]string)
1818

19+
var paramErrors []string
1920
for _, p := range s.Parameters {
2021
if p.Type.EntityRef != nil {
22+
// Reject bare entity names (empty module) — e.g., "Object" instead of "System.Workflow"
23+
if p.Type.EntityRef.Module == "" {
24+
paramErrors = append(paramErrors, fmt.Sprintf(
25+
"parameter '$%s': entity type '%s' is missing module prefix (use 'Module.%s')",
26+
p.Name, p.Type.EntityRef.Name, p.Type.EntityRef.Name))
27+
continue
28+
}
2129
entityQN := p.Type.EntityRef.Module + "." + p.Type.EntityRef.Name
2230
if p.Type.Kind == ast.TypeListOf {
2331
varTypes[p.Name] = "List of " + entityQN
@@ -29,6 +37,9 @@ func ValidateMicroflowBody(s *ast.CreateMicroflowStmt) []string {
2937
declaredVars[p.Name] = p.Type.Kind.String()
3038
}
3139
}
40+
if len(paramErrors) > 0 {
41+
return paramErrors
42+
}
3243

3344
// Create a validation-only flow builder
3445
fb := &flowBuilder{

mdl/executor/validate_microflow.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ func ValidateMicroflow(stmt *ast.CreateMicroflowStmt) []linter.Violation {
1717
mfName: stmt.Name.String(),
1818
returnType: stmt.ReturnType,
1919
}
20+
// Validate parameter entity references — reject bare names without module prefix
21+
for _, p := range stmt.Parameters {
22+
if p.Type.EntityRef != nil && p.Type.EntityRef.Module == "" {
23+
v.addViolation("MDL008", linter.SeverityError,
24+
fmt.Sprintf("parameter '$%s': entity type '%s' is missing module prefix",
25+
p.Name, p.Type.EntityRef.Name),
26+
fmt.Sprintf("Use a qualified name like 'Module.%s' or 'System.%s'",
27+
p.Type.EntityRef.Name, p.Type.EntityRef.Name))
28+
}
29+
}
2030
v.validate(stmt.Body)
2131
return v.violations
2232
}

mdl/visitor/visitor_rest.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,12 @@ func (b *Builder) ExitCreatePublishedRestServiceStatement(ctx *parser.CreatePubl
298298
opDef.HTTPMethod = strings.ToUpper(mCtx.GetText())
299299
}
300300

301-
// Operation path
301+
// Operation path — strip leading/trailing slashes (CE6550/CE6551)
302302
if pCtx := oc.PublishedRestOpPath(); pCtx != nil {
303303
pc := pCtx.(*parser.PublishedRestOpPathContext)
304304
if pc.STRING_LITERAL() != nil {
305-
opDef.Path = unquoteString(pc.STRING_LITERAL().GetText())
306-
} else {
307-
opDef.Path = "/"
305+
opDef.Path = strings.Trim(unquoteString(pc.STRING_LITERAL().GetText()), "/")
308306
}
309-
} else {
310-
opDef.Path = "/"
311307
}
312308

313309
// Microflow reference

model/types.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -602,11 +602,12 @@ type PublishedRestResource struct {
602602
// PublishedRestOperation represents a Rest$PublishedRestServiceOperation.
603603
type PublishedRestOperation struct {
604604
BaseElement
605-
Path string `json:"path,omitempty"`
606-
HTTPMethod string `json:"httpMethod,omitempty"`
607-
Summary string `json:"summary,omitempty"`
608-
Microflow string `json:"microflow,omitempty"`
609-
Deprecated bool `json:"deprecated,omitempty"`
605+
Path string `json:"path,omitempty"`
606+
HTTPMethod string `json:"httpMethod,omitempty"`
607+
Summary string `json:"summary,omitempty"`
608+
Microflow string `json:"microflow,omitempty"`
609+
Deprecated bool `json:"deprecated,omitempty"`
610+
Parameters []string `json:"parameters,omitempty"` // path parameter names extracted from {param} in Path
610611
}
611612

612613
// ============================================================================

sdk/mpr/writer_rest.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ func (w *Writer) serializePublishedRestService(svc *model.PublishedRestService)
342342
"ExportMapping": "",
343343
"ImportMapping": "",
344344
"ObjectHandlingBackup": "Create",
345-
"Parameters": bson.A{int32(2)},
345+
"Parameters": serializePublishedRestParams(op.Path, op.Parameters),
346346
}
347347
ops = append(ops, opDoc)
348348
}
@@ -377,6 +377,42 @@ func (w *Writer) serializePublishedRestService(svc *model.PublishedRestService)
377377
return bson.Marshal(doc)
378378
}
379379

380+
// serializePublishedRestParams builds the Parameters array for a published REST operation.
381+
// It auto-extracts path parameters from {paramName} placeholders in the path string,
382+
// then appends any explicitly declared parameters.
383+
func serializePublishedRestParams(path string, _ []string) bson.A {
384+
params := bson.A{int32(2)}
385+
// Extract {paramName} from path
386+
for _, name := range extractPathParams(path) {
387+
params = append(params, bson.M{
388+
"$ID": idToBsonBinary(generateUUID()),
389+
"$Type": "Rest$RestOperationParameter",
390+
"Name": name,
391+
"DataType": "String",
392+
"Description": "",
393+
})
394+
}
395+
return params
396+
}
397+
398+
// extractPathParams returns parameter names from {param} placeholders in a path.
399+
func extractPathParams(path string) []string {
400+
var names []string
401+
for {
402+
start := strings.Index(path, "{")
403+
if start < 0 {
404+
break
405+
}
406+
end := strings.Index(path[start:], "}")
407+
if end < 0 {
408+
break
409+
}
410+
names = append(names, path[start+1:start+end])
411+
path = path[start+end+1:]
412+
}
413+
return names
414+
}
415+
380416
// httpMethodToMendix converts uppercase HTTP method names to Mendix casing.
381417
func httpMethodToMendix(method string) string {
382418
switch strings.ToUpper(method) {

0 commit comments

Comments
 (0)