Skip to content
Open
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
146 changes: 146 additions & 0 deletions mdl/backend/mock/mock_page_mutator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: Apache-2.0

package mock

import (
"github.com/mendixlabs/mxcli/mdl/backend"
"github.com/mendixlabs/mxcli/model"
"github.com/mendixlabs/mxcli/sdk/pages"
)

var _ backend.PageMutator = (*MockPageMutator)(nil)

// MockPageMutator implements backend.PageMutator. Every interface method is
// backed by a public function field. If the field is nil the method returns
// nil error (never panics). ContainerType defaults to ContainerPage when unset;
// all other methods return zero values.
type MockPageMutator struct {
ContainerTypeFunc func() backend.ContainerKind
SetWidgetPropertyFunc func(widgetRef string, prop string, value any) error
SetWidgetDataSourceFunc func(widgetRef string, ds pages.DataSource) error
SetColumnPropertyFunc func(gridRef string, columnRef string, prop string, value any) error
InsertWidgetFunc func(widgetRef string, columnRef string, position backend.InsertPosition, widgets []pages.Widget) error
DropWidgetFunc func(refs []backend.WidgetRef) error
ReplaceWidgetFunc func(widgetRef string, columnRef string, widgets []pages.Widget) error
FindWidgetFunc func(name string) bool
AddVariableFunc func(name, dataType, defaultValue string) error
DropVariableFunc func(name string) error
SetLayoutFunc func(newLayout string, paramMappings map[string]string) error
SetPluggablePropertyFunc func(widgetRef string, propKey string, op backend.PluggablePropertyOp, ctx backend.PluggablePropertyContext) error
EnclosingEntityFunc func(widgetRef string) string
WidgetScopeFunc func() map[string]model.ID
ParamScopeFunc func() (map[string]model.ID, map[string]string)
SaveFunc func() error
}

func (m *MockPageMutator) ContainerType() backend.ContainerKind {
if m.ContainerTypeFunc != nil {
return m.ContainerTypeFunc()
}
return backend.ContainerPage
}
Comment thread
retran marked this conversation as resolved.

func (m *MockPageMutator) SetWidgetProperty(widgetRef string, prop string, value any) error {
if m.SetWidgetPropertyFunc != nil {
return m.SetWidgetPropertyFunc(widgetRef, prop, value)
}
return nil
}

func (m *MockPageMutator) SetWidgetDataSource(widgetRef string, ds pages.DataSource) error {
if m.SetWidgetDataSourceFunc != nil {
return m.SetWidgetDataSourceFunc(widgetRef, ds)
}
return nil
}

func (m *MockPageMutator) SetColumnProperty(gridRef string, columnRef string, prop string, value any) error {
if m.SetColumnPropertyFunc != nil {
return m.SetColumnPropertyFunc(gridRef, columnRef, prop, value)
}
return nil
}

func (m *MockPageMutator) InsertWidget(widgetRef string, columnRef string, position backend.InsertPosition, widgets []pages.Widget) error {
if m.InsertWidgetFunc != nil {
return m.InsertWidgetFunc(widgetRef, columnRef, position, widgets)
}
return nil
}

func (m *MockPageMutator) DropWidget(refs []backend.WidgetRef) error {
if m.DropWidgetFunc != nil {
return m.DropWidgetFunc(refs)
}
return nil
}

func (m *MockPageMutator) ReplaceWidget(widgetRef string, columnRef string, widgets []pages.Widget) error {
if m.ReplaceWidgetFunc != nil {
return m.ReplaceWidgetFunc(widgetRef, columnRef, widgets)
}
return nil
}

func (m *MockPageMutator) FindWidget(name string) bool {
if m.FindWidgetFunc != nil {
return m.FindWidgetFunc(name)
}
return false
}

func (m *MockPageMutator) AddVariable(name, dataType, defaultValue string) error {
if m.AddVariableFunc != nil {
return m.AddVariableFunc(name, dataType, defaultValue)
}
return nil
}

func (m *MockPageMutator) DropVariable(name string) error {
if m.DropVariableFunc != nil {
return m.DropVariableFunc(name)
}
return nil
}

func (m *MockPageMutator) SetLayout(newLayout string, paramMappings map[string]string) error {
if m.SetLayoutFunc != nil {
return m.SetLayoutFunc(newLayout, paramMappings)
}
return nil
}

func (m *MockPageMutator) SetPluggableProperty(widgetRef string, propKey string, op backend.PluggablePropertyOp, ctx backend.PluggablePropertyContext) error {
if m.SetPluggablePropertyFunc != nil {
return m.SetPluggablePropertyFunc(widgetRef, propKey, op, ctx)
}
return nil
}

func (m *MockPageMutator) EnclosingEntity(widgetRef string) string {
if m.EnclosingEntityFunc != nil {
return m.EnclosingEntityFunc(widgetRef)
}
return ""
}

func (m *MockPageMutator) WidgetScope() map[string]model.ID {
if m.WidgetScopeFunc != nil {
return m.WidgetScopeFunc()
}
return nil
}

func (m *MockPageMutator) ParamScope() (map[string]model.ID, map[string]string) {
if m.ParamScopeFunc != nil {
return m.ParamScopeFunc()
}
return nil, nil
}

func (m *MockPageMutator) Save() error {
if m.SaveFunc != nil {
return m.SaveFunc()
}
return nil
}
136 changes: 136 additions & 0 deletions mdl/backend/mock/mock_workflow_mutator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: Apache-2.0

package mock

import (
"github.com/mendixlabs/mxcli/mdl/backend"
"github.com/mendixlabs/mxcli/sdk/workflows"
)

var _ backend.WorkflowMutator = (*MockWorkflowMutator)(nil)

// MockWorkflowMutator implements backend.WorkflowMutator. Every interface
// method is backed by a public function field. If the field is nil the
// method returns zero values / nil error (never panics).
type MockWorkflowMutator struct {
SetPropertyFunc func(prop string, value string) error
SetPropertyWithEntityFunc func(prop string, value string, entity string) error
SetActivityPropertyFunc func(activityRef string, atPos int, prop string, value string) error
InsertAfterActivityFunc func(activityRef string, atPos int, activities []workflows.WorkflowActivity) error
DropActivityFunc func(activityRef string, atPos int) error
ReplaceActivityFunc func(activityRef string, atPos int, activities []workflows.WorkflowActivity) error
InsertOutcomeFunc func(activityRef string, atPos int, outcomeName string, activities []workflows.WorkflowActivity) error
DropOutcomeFunc func(activityRef string, atPos int, outcomeName string) error
InsertPathFunc func(activityRef string, atPos int, pathCaption string, activities []workflows.WorkflowActivity) error
DropPathFunc func(activityRef string, atPos int, pathCaption string) error
InsertBranchFunc func(activityRef string, atPos int, condition string, activities []workflows.WorkflowActivity) error
DropBranchFunc func(activityRef string, atPos int, branchName string) error
InsertBoundaryEventFunc func(activityRef string, atPos int, eventType string, delay string, activities []workflows.WorkflowActivity) error
DropBoundaryEventFunc func(activityRef string, atPos int) error
SaveFunc func() error
}

func (m *MockWorkflowMutator) SetProperty(prop string, value string) error {
if m.SetPropertyFunc != nil {
return m.SetPropertyFunc(prop, value)
}
return nil
}

func (m *MockWorkflowMutator) SetPropertyWithEntity(prop string, value string, entity string) error {
if m.SetPropertyWithEntityFunc != nil {
return m.SetPropertyWithEntityFunc(prop, value, entity)
}
return nil
}

func (m *MockWorkflowMutator) SetActivityProperty(activityRef string, atPos int, prop string, value string) error {
if m.SetActivityPropertyFunc != nil {
return m.SetActivityPropertyFunc(activityRef, atPos, prop, value)
}
return nil
}

func (m *MockWorkflowMutator) InsertAfterActivity(activityRef string, atPos int, activities []workflows.WorkflowActivity) error {
if m.InsertAfterActivityFunc != nil {
return m.InsertAfterActivityFunc(activityRef, atPos, activities)
}
return nil
}

func (m *MockWorkflowMutator) DropActivity(activityRef string, atPos int) error {
if m.DropActivityFunc != nil {
return m.DropActivityFunc(activityRef, atPos)
}
return nil
}

func (m *MockWorkflowMutator) ReplaceActivity(activityRef string, atPos int, activities []workflows.WorkflowActivity) error {
if m.ReplaceActivityFunc != nil {
return m.ReplaceActivityFunc(activityRef, atPos, activities)
}
return nil
}

func (m *MockWorkflowMutator) InsertOutcome(activityRef string, atPos int, outcomeName string, activities []workflows.WorkflowActivity) error {
if m.InsertOutcomeFunc != nil {
return m.InsertOutcomeFunc(activityRef, atPos, outcomeName, activities)
}
return nil
}

func (m *MockWorkflowMutator) DropOutcome(activityRef string, atPos int, outcomeName string) error {
if m.DropOutcomeFunc != nil {
return m.DropOutcomeFunc(activityRef, atPos, outcomeName)
}
return nil
}

func (m *MockWorkflowMutator) InsertPath(activityRef string, atPos int, pathCaption string, activities []workflows.WorkflowActivity) error {
if m.InsertPathFunc != nil {
return m.InsertPathFunc(activityRef, atPos, pathCaption, activities)
}
return nil
}

func (m *MockWorkflowMutator) DropPath(activityRef string, atPos int, pathCaption string) error {
if m.DropPathFunc != nil {
return m.DropPathFunc(activityRef, atPos, pathCaption)
}
return nil
}

func (m *MockWorkflowMutator) InsertBranch(activityRef string, atPos int, condition string, activities []workflows.WorkflowActivity) error {
if m.InsertBranchFunc != nil {
return m.InsertBranchFunc(activityRef, atPos, condition, activities)
}
return nil
}

func (m *MockWorkflowMutator) DropBranch(activityRef string, atPos int, branchName string) error {
if m.DropBranchFunc != nil {
return m.DropBranchFunc(activityRef, atPos, branchName)
}
return nil
}

func (m *MockWorkflowMutator) InsertBoundaryEvent(activityRef string, atPos int, eventType string, delay string, activities []workflows.WorkflowActivity) error {
if m.InsertBoundaryEventFunc != nil {
return m.InsertBoundaryEventFunc(activityRef, atPos, eventType, delay, activities)
}
return nil
}

func (m *MockWorkflowMutator) DropBoundaryEvent(activityRef string, atPos int) error {
if m.DropBoundaryEventFunc != nil {
return m.DropBoundaryEventFunc(activityRef, atPos)
}
return nil
}

func (m *MockWorkflowMutator) Save() error {
if m.SaveFunc != nil {
return m.SaveFunc()
}
return nil
}
18 changes: 12 additions & 6 deletions mdl/backend/mpr/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ func (b *MprBackend) Path() string { return b.path }
// for new code.
func (b *MprBackend) MprReader() *mpr.Reader { return b.reader }

func (b *MprBackend) Version() types.MPRVersion { return convertMPRVersion(b.reader.Version()) }
func (b *MprBackend) ProjectVersion() *types.ProjectVersion { return convertProjectVersion(b.reader.ProjectVersion()) }
func (b *MprBackend) GetMendixVersion() (string, error) { return b.reader.GetMendixVersion() }
func (b *MprBackend) Version() types.MPRVersion { return convertMPRVersion(b.reader.Version()) }
func (b *MprBackend) ProjectVersion() *types.ProjectVersion {
return convertProjectVersion(b.reader.ProjectVersion())
}
func (b *MprBackend) GetMendixVersion() (string, error) { return b.reader.GetMendixVersion() }

// Commit is a no-op — the MPR writer auto-commits on each write operation.
func (b *MprBackend) Commit() error { return nil }
Expand All @@ -112,7 +114,9 @@ func (b *MprBackend) DeleteModuleWithCleanup(id model.ID, moduleName string) err
// FolderBackend
// ---------------------------------------------------------------------------

func (b *MprBackend) ListFolders() ([]*types.FolderInfo, error) { return convertFolderInfoSlice(b.reader.ListFolders()) }
func (b *MprBackend) ListFolders() ([]*types.FolderInfo, error) {
return convertFolderInfoSlice(b.reader.ListFolders())
}
func (b *MprBackend) CreateFolder(folder *model.Folder) error { return b.writer.CreateFolder(folder) }
func (b *MprBackend) DeleteFolder(id model.ID) error { return b.writer.DeleteFolder(id) }
func (b *MprBackend) MoveFolder(id model.ID, newContainerID model.ID) error {
Expand Down Expand Up @@ -678,8 +682,10 @@ func (b *MprBackend) UpdateRawUnit(unitID string, contents []byte) error {
// MetadataBackend
// ---------------------------------------------------------------------------

func (b *MprBackend) ListAllUnitIDs() ([]string, error) { return b.reader.ListAllUnitIDs() }
func (b *MprBackend) ListUnits() ([]*types.UnitInfo, error) { return convertUnitInfoSlice(b.reader.ListUnits()) }
func (b *MprBackend) ListAllUnitIDs() ([]string, error) { return b.reader.ListAllUnitIDs() }
func (b *MprBackend) ListUnits() ([]*types.UnitInfo, error) {
return convertUnitInfoSlice(b.reader.ListUnits())
}
func (b *MprBackend) GetUnitTypes() (map[string]int, error) { return b.reader.GetUnitTypes() }
func (b *MprBackend) GetProjectRootID() (string, error) { return b.reader.GetProjectRootID() }
func (b *MprBackend) ContentsDir() string { return b.reader.ContentsDir() }
Expand Down
1 change: 0 additions & 1 deletion mdl/backend/mpr/convert_roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,4 +648,3 @@ func TestFieldCountDrift(t *testing.T) {
assertFieldCount(t, "mpr.EntityAccessRevocation", mpr.EntityAccessRevocation{}, 6)
assertFieldCount(t, "types.EntityAccessRevocation", types.EntityAccessRevocation{}, 6)
}

55 changes: 48 additions & 7 deletions mdl/backend/mpr/workflow_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,19 @@ func (m *mprWorkflowMutator) SetActivityProperty(activityRef string, atPos int,
case "PAGE":
taskPage := dGetDoc(actDoc, "TaskPage")
if taskPage != nil {
// TaskPage exists and has a value — update the Page field in place.
dSet(taskPage, "Page", value)
} else {
pageRef := bson.D{
{Key: "$ID", Value: bsonutil.NewIDBsonBinary()},
{Key: "$Type", Value: "Workflows$PageReference"},
{Key: "Page", Value: value},
}
dSet(actDoc, "TaskPage", pageRef)
return nil
}
pageRef := bson.D{
{Key: "$ID", Value: bsonutil.NewIDBsonBinary()},
{Key: "$Type", Value: "Workflows$PageReference"},
{Key: "Page", Value: value},
}
if !dSet(actDoc, "TaskPage", pageRef) {
// TaskPage key absent — append to activity and replace in BSON tree.
actDoc = append(actDoc, bson.E{Key: "TaskPage", Value: pageRef})
m.replaceActivity(actDoc)
}
return nil
Comment thread
retran marked this conversation as resolved.

Expand Down Expand Up @@ -555,6 +560,42 @@ func (m *mprWorkflowMutator) Save() error {
// Internal helpers — activity search
// ---------------------------------------------------------------------------

// replaceActivity replaces an activity document in the workflow's BSON tree
// by matching on $ID. This is needed when appending new keys to an activity
// document, because the slice header returned by findActivityByCaption cannot
// propagate appends back to the parent bson.A.
func (m *mprWorkflowMutator) replaceActivity(updated bson.D) {
actID := extractBinaryIDFromDoc(dGet(updated, "$ID"))
if actID == "" {
return
}
flow := dGetDoc(m.rawData, "Flow")
if flow == nil {
return
}
replaceActivityRecursive(flow, actID, updated)
}

func replaceActivityRecursive(flow bson.D, actID string, updated bson.D) bool {
elements := dGetArrayElements(dGet(flow, "Activities"))
for i, elem := range elements {
actDoc, ok := elem.(bson.D)
if !ok {
continue
}
if extractBinaryIDFromDoc(dGet(actDoc, "$ID")) == actID {
elements[i] = updated
return true
}
for _, nestedFlow := range getNestedFlows(actDoc) {
if replaceActivityRecursive(nestedFlow, actID, updated) {
return true
}
}
}
return false
}

// findActivityByCaption searches the workflow for an activity matching caption.
func (m *mprWorkflowMutator) findActivityByCaption(caption string, atPosition int) (bson.D, error) {
flow := dGetDoc(m.rawData, "Flow")
Expand Down
Loading
Loading