Skip to content

Commit 50e3c10

Browse files
authored
Merge pull request #170 from engalar/fix/doctype-test-failures
fix: strip REST path slashes and validate microflow param entity refs
2 parents 73f6683 + e1d9fd2 commit 50e3c10

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)