Skip to content

Commit 99e33aa

Browse files
authored
Merge pull request #452 from hjotha/submit/change-member-inherited-attribute
fix: resolve bare change members against inherited attributes
2 parents fc9b5a2 + 8f2a8d3 commit 99e33aa

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

mdl/executor/cmd_microflows_builder_actions.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,8 @@ func (fb *flowBuilder) resolveMemberChange(mc *microflows.MemberChange, memberNa
11761176
if strings.Contains(memberName, ".") {
11771177
// Already qualified, don't double-qualify
11781178
mc.AttributeQualifiedName = memberName
1179+
} else if attrQN, ok := fb.resolveAttributeInEntityHierarchy(entityQN, memberName); ok {
1180+
mc.AttributeQualifiedName = attrQN
11791181
} else {
11801182
mc.AttributeQualifiedName = entityQN + "." + memberName
11811183
}
@@ -1187,6 +1189,43 @@ func (fb *flowBuilder) resolveMemberChange(mc *microflows.MemberChange, memberNa
11871189
resolveMemberChangeFallback(mc, memberName, entityQN)
11881190
}
11891191

1192+
func (fb *flowBuilder) resolveAttributeInEntityHierarchy(entityQN, attrName string) (string, bool) {
1193+
if fb == nil || fb.backend == nil || entityQN == "" || attrName == "" {
1194+
return "", false
1195+
}
1196+
seen := make(map[string]bool)
1197+
for currentQN := entityQN; currentQN != ""; {
1198+
if seen[currentQN] {
1199+
return "", false
1200+
}
1201+
seen[currentQN] = true
1202+
1203+
parts := strings.SplitN(currentQN, ".", 2)
1204+
if len(parts) != 2 {
1205+
return "", false
1206+
}
1207+
mod, err := fb.backend.GetModuleByName(parts[0])
1208+
if err != nil || mod == nil {
1209+
return "", false
1210+
}
1211+
dm, err := fb.backend.GetDomainModel(mod.ID)
1212+
if err != nil || dm == nil {
1213+
return "", false
1214+
}
1215+
entity := dm.FindEntityByName(parts[1])
1216+
if entity == nil {
1217+
return "", false
1218+
}
1219+
for _, attr := range entity.Attributes {
1220+
if attr != nil && attr.Name == attrName {
1221+
return currentQN + "." + attrName, true
1222+
}
1223+
}
1224+
currentQN = entity.GeneralizationRef
1225+
}
1226+
return "", false
1227+
}
1228+
11901229
// resolveMemberChangeFallback preserves the authored member name shape when the
11911230
// entity metadata is unavailable.
11921231
//
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
9+
"github.com/mendixlabs/mxcli/model"
10+
"github.com/mendixlabs/mxcli/sdk/domainmodel"
11+
"github.com/mendixlabs/mxcli/sdk/microflows"
12+
)
13+
14+
func TestResolveMemberChange_BareInheritedAttributeUsesDeclaringEntity(t *testing.T) {
15+
appModuleID := model.ID("synthetic-app-module")
16+
baseModuleID := model.ID("synthetic-base-module")
17+
backend := &mock.MockBackend{
18+
GetModuleByNameFunc: func(name string) (*model.Module, error) {
19+
switch name {
20+
case "SyntheticApp":
21+
return &model.Module{BaseElement: model.BaseElement{ID: appModuleID}, Name: name}, nil
22+
case "SyntheticBase":
23+
return &model.Module{BaseElement: model.BaseElement{ID: baseModuleID}, Name: name}, nil
24+
default:
25+
return nil, nil
26+
}
27+
},
28+
GetDomainModelFunc: func(id model.ID) (*domainmodel.DomainModel, error) {
29+
switch id {
30+
case appModuleID:
31+
return &domainmodel.DomainModel{
32+
ContainerID: appModuleID,
33+
Entities: []*domainmodel.Entity{
34+
{
35+
Name: "Account",
36+
GeneralizationRef: "SyntheticBase.User",
37+
},
38+
},
39+
}, nil
40+
case baseModuleID:
41+
return &domainmodel.DomainModel{
42+
ContainerID: baseModuleID,
43+
Entities: []*domainmodel.Entity{
44+
{
45+
Name: "User",
46+
Attributes: []*domainmodel.Attribute{
47+
{Name: "CanUseWebService", Type: &domainmodel.BooleanAttributeType{}},
48+
},
49+
},
50+
},
51+
}, nil
52+
default:
53+
return nil, nil
54+
}
55+
},
56+
}
57+
58+
fb := &flowBuilder{backend: backend}
59+
mc := &microflows.MemberChange{}
60+
61+
fb.resolveMemberChange(mc, "CanUseWebService", "SyntheticApp.Account")
62+
63+
if got, want := mc.AttributeQualifiedName, "SyntheticBase.User.CanUseWebService"; got != want {
64+
t.Fatalf("AttributeQualifiedName = %q, want %q", got, want)
65+
}
66+
if mc.AssociationQualifiedName != "" {
67+
t.Fatalf("AssociationQualifiedName = %q, want empty", mc.AssociationQualifiedName)
68+
}
69+
}

0 commit comments

Comments
 (0)