Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions mdl/executor/cmd_microflows_builder_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func (fb *flowBuilder) addRetrieveAction(s *ast.RetrieveStmt) model.ID {
}

if assocInfo != nil && assocInfo.Type == domainmodel.AssociationTypeReference &&
assocInfo.Owner != domainmodel.AssociationOwnerBoth &&
assocInfo.Owner != "" &&
assocInfo.parentPersistable &&
assocInfo.childEntityQN != "" && startVarType == assocInfo.childEntityQN {
// Reverse traversal on Reference: child → parent (one-to-many)
Expand All @@ -326,12 +326,18 @@ func (fb *flowBuilder) addRetrieveAction(s *ast.RetrieveStmt) model.ID {
}
if fb.varTypes != nil {
if assocInfo != nil && assocInfo.Type == domainmodel.AssociationTypeReference {
// Reference forward traversal: returns single object
// Forward Reference traversal returns a single object. Legacy or
// non-persistable reverse traversal can still use association
// source syntax, but keeps list typing for downstream actions.
otherEntity := assocInfo.childEntityQN
if startVarType == assocInfo.childEntityQN {
otherEntity = assocInfo.parentEntityQN
}
fb.varTypes[s.Variable] = otherEntity
if startVarType == assocInfo.childEntityQN {
fb.varTypes[s.Variable] = "List of " + otherEntity
} else {
fb.varTypes[s.Variable] = otherEntity
}
} else if assocInfo != nil && assocInfo.Type == domainmodel.AssociationTypeReferenceSet {
// ReferenceSet traversal returns a list of the entity on the other side,
// not a list typed as the association itself.
Expand Down
43 changes: 33 additions & 10 deletions mdl/executor/cmd_microflows_builder_retrieve_reverse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ import (
"github.com/mendixlabs/mxcli/sdk/microflows"
)

func TestAddRetrieveAction_ReverseReferenceOwnerBothUsesAssociationSource(t *testing.T) {
func TestAddRetrieveAction_ReverseReferenceOwnerBothUsesDatabaseSource(t *testing.T) {
fb := newRetrieveAssociationFlowBuilder(domainmodel.AssociationOwnerBoth)
fb.varTypes["Child"] = "Sample.Child"

fb.addRetrieveAction(&ast.RetrieveStmt{
Variable: "Parent",
Variable: "Parents",
StartVariable: "Child",
Source: ast.QualifiedName{Module: "Sample", Name: "Parent_Child"},
})

action := onlyRetrieveAction(t, fb)
source, ok := action.Source.(*microflows.AssociationRetrieveSource)
source, ok := action.Source.(*microflows.DatabaseRetrieveSource)
if !ok {
t.Fatalf("owner-both reverse retrieve source = %T, want AssociationRetrieveSource", action.Source)
t.Fatalf("owner-both reverse retrieve source = %T, want DatabaseRetrieveSource", action.Source)
}
if source.StartVariable != "Child" || source.AssociationQualifiedName != "Sample.Parent_Child" {
t.Fatalf("association source = %#v", source)
if source.EntityQualifiedName != "Sample.Parent" || source.XPathConstraint != "[Sample.Parent_Child = $Child]" {
t.Fatalf("database source = %#v", source)
}
if got := fb.varTypes["Parent"]; got != "Sample.Parent" {
t.Fatalf("result var type = %q, want Sample.Parent", got)
if got := fb.varTypes["Parents"]; got != "List of Sample.Parent" {
t.Fatalf("result var type = %q, want List of Sample.Parent", got)
}
}

Expand Down Expand Up @@ -76,8 +76,31 @@ func TestAddRetrieveAction_ReverseReferenceNonPersistableParentUsesAssociationSo
if source.StartVariable != "Child" || source.AssociationQualifiedName != "Sample.Parent_Child" {
t.Fatalf("association source = %#v", source)
}
if got := fb.varTypes["Parents"]; got != "Sample.Parent" {
t.Fatalf("result var type = %q, want Sample.Parent", got)
if got := fb.varTypes["Parents"]; got != "List of Sample.Parent" {
t.Fatalf("result var type = %q, want List of Sample.Parent", got)
}
}

func TestAddRetrieveAction_ReverseReferenceUnknownOwnerPreservesAssociationSource(t *testing.T) {
fb := newRetrieveAssociationFlowBuilder("")
fb.varTypes["Child"] = "Sample.Child"

fb.addRetrieveAction(&ast.RetrieveStmt{
Variable: "Parents",
StartVariable: "Child",
Source: ast.QualifiedName{Module: "Sample", Name: "Parent_Child"},
})

action := onlyRetrieveAction(t, fb)
source, ok := action.Source.(*microflows.AssociationRetrieveSource)
if !ok {
t.Fatalf("unknown-owner reverse retrieve source = %T, want AssociationRetrieveSource", action.Source)
}
if source.StartVariable != "Child" || source.AssociationQualifiedName != "Sample.Parent_Child" {
t.Fatalf("association source = %#v", source)
}
if got := fb.varTypes["Parents"]; got != "List of Sample.Parent" {
t.Fatalf("result var type = %q, want List of Sample.Parent", got)
}
}

Expand Down