Skip to content

Commit 9c14ffc

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 9c14ffc

7 files changed

Lines changed: 99 additions & 17 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 & 3 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

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: 38 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,43 @@ 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+
"MicroflowParameter": name,
394+
})
395+
}
396+
return params
397+
}
398+
399+
// extractPathParams returns parameter names from {param} placeholders in a path.
400+
func extractPathParams(path string) []string {
401+
var names []string
402+
for {
403+
start := strings.Index(path, "{")
404+
if start < 0 {
405+
break
406+
}
407+
end := strings.Index(path[start:], "}")
408+
if end < 0 {
409+
break
410+
}
411+
names = append(names, path[start+1:start+end])
412+
path = path[start+end+1:]
413+
}
414+
return names
415+
}
416+
380417
// httpMethodToMendix converts uppercase HTTP method names to Mendix casing.
381418
func httpMethodToMendix(method string) string {
382419
switch strings.ToUpper(method) {

0 commit comments

Comments
 (0)