Skip to content

Commit c0decae

Browse files
committed
refactor: define mutation backend interfaces with stub implementations
Add PageMutator, WorkflowMutator, WidgetSerializationBackend interfaces to mdl/backend/mutation.go for BSON-free handler decoupling. Extract BSON ID helpers (IDToBsonBinary, BsonBinaryToID, NewIDBsonBinary) to mdl/bsonutil/ package. Add panic stubs to MprBackend and mock function fields to MockBackend for all new interface methods. - Create mdl/bsonutil/bsonutil.go with BSON ID conversion utilities - Migrate 10 handler files from mpr.IDToBsonBinary to bsonutil.* - Define PageMutationBackend, WorkflowMutationBackend interfaces - Define WidgetSerializationBackend with opaque return types - Add PluggablePropertyContext for domain-typed widget property input
1 parent a807b77 commit c0decae

16 files changed

+481
-145
lines changed

mdl/backend/backend.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ type FullBackend interface {
3131
MetadataBackend
3232
WidgetBackend
3333
AgentEditorBackend
34+
PageMutationBackend
35+
WorkflowMutationBackend
36+
WidgetSerializationBackend
3437
}

mdl/backend/mock/backend.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,18 @@ type MockBackend struct {
258258
FindCustomWidgetTypeFunc func(widgetID string) (*types.RawCustomWidgetType, error)
259259
FindAllCustomWidgetTypesFunc func(widgetID string) ([]*types.RawCustomWidgetType, error)
260260

261+
// PageMutationBackend
262+
OpenPageForMutationFunc func(unitID model.ID) (backend.PageMutator, error)
263+
264+
// WorkflowMutationBackend
265+
OpenWorkflowForMutationFunc func(unitID model.ID) (backend.WorkflowMutator, error)
266+
267+
// WidgetSerializationBackend
268+
SerializeWidgetFunc func(w pages.Widget) (any, error)
269+
SerializeClientActionFunc func(a pages.ClientAction) (any, error)
270+
SerializeDataSourceFunc func(ds pages.DataSource) (any, error)
271+
SerializeWorkflowActivityFunc func(a workflows.WorkflowActivity) (any, error)
272+
261273
// AgentEditorBackend
262274
ListAgentEditorModelsFunc func() ([]*agenteditor.Model, error)
263275
ListAgentEditorKnowledgeBasesFunc func() ([]*agenteditor.KnowledgeBase, error)

mdl/backend/mock/mock_mutation.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package mock
4+
5+
import (
6+
"github.com/mendixlabs/mxcli/mdl/backend"
7+
"github.com/mendixlabs/mxcli/model"
8+
"github.com/mendixlabs/mxcli/sdk/pages"
9+
"github.com/mendixlabs/mxcli/sdk/workflows"
10+
)
11+
12+
// ---------------------------------------------------------------------------
13+
// PageMutationBackend
14+
// ---------------------------------------------------------------------------
15+
16+
func (m *MockBackend) OpenPageForMutation(unitID model.ID) (backend.PageMutator, error) {
17+
if m.OpenPageForMutationFunc != nil {
18+
return m.OpenPageForMutationFunc(unitID)
19+
}
20+
return nil, nil
21+
}
22+
23+
// ---------------------------------------------------------------------------
24+
// WorkflowMutationBackend
25+
// ---------------------------------------------------------------------------
26+
27+
func (m *MockBackend) OpenWorkflowForMutation(unitID model.ID) (backend.WorkflowMutator, error) {
28+
if m.OpenWorkflowForMutationFunc != nil {
29+
return m.OpenWorkflowForMutationFunc(unitID)
30+
}
31+
return nil, nil
32+
}
33+
34+
// ---------------------------------------------------------------------------
35+
// WidgetSerializationBackend
36+
// ---------------------------------------------------------------------------
37+
38+
func (m *MockBackend) SerializeWidget(w pages.Widget) (any, error) {
39+
if m.SerializeWidgetFunc != nil {
40+
return m.SerializeWidgetFunc(w)
41+
}
42+
return nil, nil
43+
}
44+
45+
func (m *MockBackend) SerializeClientAction(a pages.ClientAction) (any, error) {
46+
if m.SerializeClientActionFunc != nil {
47+
return m.SerializeClientActionFunc(a)
48+
}
49+
return nil, nil
50+
}
51+
52+
func (m *MockBackend) SerializeDataSource(ds pages.DataSource) (any, error) {
53+
if m.SerializeDataSourceFunc != nil {
54+
return m.SerializeDataSourceFunc(ds)
55+
}
56+
return nil, nil
57+
}
58+
59+
func (m *MockBackend) SerializeWorkflowActivity(a workflows.WorkflowActivity) (any, error) {
60+
if m.SerializeWorkflowActivityFunc != nil {
61+
return m.SerializeWorkflowActivityFunc(a)
62+
}
63+
return nil, nil
64+
}

mdl/backend/mpr/backend.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,36 @@ func (b *MprBackend) CreateAgentEditorAgent(a *agenteditor.Agent) error {
724724
func (b *MprBackend) DeleteAgentEditorAgent(id string) error {
725725
return b.writer.DeleteAgentEditorAgent(id)
726726
}
727+
728+
// ---------------------------------------------------------------------------
729+
// PageMutationBackend
730+
731+
func (b *MprBackend) OpenPageForMutation(unitID model.ID) (backend.PageMutator, error) {
732+
panic("MprBackend.OpenPageForMutation not yet implemented")
733+
}
734+
735+
// ---------------------------------------------------------------------------
736+
// WorkflowMutationBackend
737+
738+
func (b *MprBackend) OpenWorkflowForMutation(unitID model.ID) (backend.WorkflowMutator, error) {
739+
panic("MprBackend.OpenWorkflowForMutation not yet implemented")
740+
}
741+
742+
// ---------------------------------------------------------------------------
743+
// WidgetSerializationBackend
744+
745+
func (b *MprBackend) SerializeWidget(w pages.Widget) (any, error) {
746+
panic("MprBackend.SerializeWidget not yet implemented")
747+
}
748+
749+
func (b *MprBackend) SerializeClientAction(a pages.ClientAction) (any, error) {
750+
panic("MprBackend.SerializeClientAction not yet implemented")
751+
}
752+
753+
func (b *MprBackend) SerializeDataSource(ds pages.DataSource) (any, error) {
754+
panic("MprBackend.SerializeDataSource not yet implemented")
755+
}
756+
757+
func (b *MprBackend) SerializeWorkflowActivity(a workflows.WorkflowActivity) (any, error) {
758+
panic("MprBackend.SerializeWorkflowActivity not yet implemented")
759+
}

mdl/backend/mutation.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package backend
4+
5+
import (
6+
"github.com/mendixlabs/mxcli/model"
7+
"github.com/mendixlabs/mxcli/sdk/pages"
8+
"github.com/mendixlabs/mxcli/sdk/workflows"
9+
)
10+
11+
// PageMutator provides fine-grained mutation operations on a single
12+
// page, layout, or snippet unit. Obtain one via PageMutationBackend.OpenPageForMutation.
13+
// All methods operate on the in-memory representation; call Save to persist.
14+
type PageMutator interface {
15+
// ContainerType returns "page", "layout", or "snippet".
16+
ContainerType() string
17+
18+
// --- Widget property operations ---
19+
20+
// SetWidgetProperty sets a simple property on the named widget.
21+
// For pluggable widget properties, prop is the Mendix property key
22+
// and value is the string representation.
23+
SetWidgetProperty(widgetRef string, prop string, value any) error
24+
25+
// SetWidgetDataSource sets the DataSource on the named widget.
26+
SetWidgetDataSource(widgetRef string, ds pages.DataSource) error
27+
28+
// SetColumnProperty sets a property on a column within a grid widget.
29+
SetColumnProperty(gridRef string, columnRef string, prop string, value any) error
30+
31+
// --- Widget tree operations ---
32+
33+
// InsertWidget inserts serialized widgets at the given position
34+
// relative to the target widget. Position is "before" or "after".
35+
InsertWidget(targetWidget string, position string, widgets []pages.Widget) error
36+
37+
// DropWidget removes widgets by name from the tree.
38+
DropWidget(widgetRefs []string) error
39+
40+
// ReplaceWidget replaces the target widget with the given widgets.
41+
ReplaceWidget(targetWidget string, widgets []pages.Widget) error
42+
43+
// --- Variable operations ---
44+
45+
// AddVariable adds a local variable to the page/snippet.
46+
AddVariable(name, dataType, defaultValue string) error
47+
48+
// DropVariable removes a local variable by name.
49+
DropVariable(name string) error
50+
51+
// --- Layout operations ---
52+
53+
// SetLayout changes the layout reference and remaps placeholder parameters.
54+
SetLayout(newLayout string, paramMappings map[string]string) error
55+
56+
// --- Pluggable widget operations ---
57+
58+
// SetPluggableProperty sets a typed property on a pluggable widget's object.
59+
// propKey is the Mendix property key, opName is the operation type
60+
// ("attribute", "association", "primitive", "selection", "datasource",
61+
// "widgets", "texttemplate", "action", "attributeObjects").
62+
// ctx carries the operation-specific values.
63+
SetPluggableProperty(widgetRef string, propKey string, opName string, ctx PluggablePropertyContext) error
64+
65+
// --- Introspection ---
66+
67+
// EnclosingEntity returns the qualified entity name for the given widget's
68+
// data context, or "" if none.
69+
EnclosingEntity(widgetRef string) string
70+
71+
// WidgetScope returns a map of widget name → unit ID for all widgets in the tree.
72+
WidgetScope() map[string]model.ID
73+
74+
// Save persists the mutations to the backend.
75+
Save() error
76+
}
77+
78+
// PluggablePropertyContext carries operation-specific values for
79+
// SetPluggableProperty. Only fields relevant to the operation are used.
80+
type PluggablePropertyContext struct {
81+
AttributePath string // "attribute", "association"
82+
AttributePaths []string // "attributeObjects"
83+
AssocPath string // "association"
84+
EntityName string // "association"
85+
PrimitiveVal string // "primitive"
86+
DataSource pages.DataSource // "datasource"
87+
ChildWidgets []pages.Widget // "widgets"
88+
Action pages.ClientAction // "action"
89+
TextTemplate string // "texttemplate"
90+
Selection string // "selection"
91+
}
92+
93+
// WorkflowMutator provides fine-grained mutation operations on a single
94+
// workflow unit. Obtain one via WorkflowMutationBackend.OpenWorkflowForMutation.
95+
// All methods operate on the in-memory representation; call Save to persist.
96+
type WorkflowMutator interface {
97+
// --- Top-level property operations ---
98+
99+
// SetProperty sets a workflow-level property (DisplayName, Description,
100+
// ExportLevel, DueDate, Parameter, OverviewPage).
101+
SetProperty(prop string, value string) error
102+
103+
// SetPropertyWithEntity sets a workflow-level property that references
104+
// an entity (e.g. Parameter).
105+
SetPropertyWithEntity(prop string, value string, entity string) error
106+
107+
// --- Activity operations ---
108+
109+
// SetActivityProperty sets a property on an activity identified by
110+
// caption and optional position index.
111+
SetActivityProperty(activityRef string, atPos int, prop string, value string) error
112+
113+
// InsertAfterActivity inserts new activities after the referenced activity.
114+
InsertAfterActivity(activityRef string, atPos int, activities []workflows.WorkflowActivity) error
115+
116+
// DropActivity removes the referenced activity.
117+
DropActivity(activityRef string, atPos int) error
118+
119+
// ReplaceActivity replaces the referenced activity with new ones.
120+
ReplaceActivity(activityRef string, atPos int, activities []workflows.WorkflowActivity) error
121+
122+
// --- Outcome operations ---
123+
124+
// InsertOutcome adds a new outcome to the referenced activity.
125+
InsertOutcome(activityRef string, atPos int, outcomeName string, activities []workflows.WorkflowActivity) error
126+
127+
// DropOutcome removes an outcome by name from the referenced activity.
128+
DropOutcome(activityRef string, atPos int, outcomeName string) error
129+
130+
// --- Path operations (parallel split) ---
131+
132+
InsertPath(activityRef string, atPos int, pathCaption string, activities []workflows.WorkflowActivity) error
133+
DropPath(activityRef string, atPos int, pathCaption string) error
134+
135+
// --- Branch operations (exclusive split) ---
136+
137+
InsertBranch(activityRef string, atPos int, condition string, activities []workflows.WorkflowActivity) error
138+
DropBranch(activityRef string, atPos int, branchName string) error
139+
140+
// --- Boundary event operations ---
141+
142+
InsertBoundaryEvent(activityRef string, atPos int, eventType string, delay string, activities []workflows.WorkflowActivity) error
143+
DropBoundaryEvent(activityRef string, atPos int) error
144+
145+
// Save persists the mutations to the backend.
146+
Save() error
147+
}
148+
149+
// PageMutationBackend provides page/layout/snippet mutation capabilities.
150+
type PageMutationBackend interface {
151+
// OpenPageForMutation loads a page, layout, or snippet unit and returns
152+
// a mutator for applying changes. Call Save() on the returned mutator
153+
// to persist.
154+
OpenPageForMutation(unitID model.ID) (PageMutator, error)
155+
}
156+
157+
// WorkflowMutationBackend provides workflow mutation capabilities.
158+
type WorkflowMutationBackend interface {
159+
// OpenWorkflowForMutation loads a workflow unit and returns a mutator
160+
// for applying changes. Call Save() on the returned mutator to persist.
161+
OpenWorkflowForMutation(unitID model.ID) (WorkflowMutator, error)
162+
}
163+
164+
// WidgetSerializationBackend provides widget and activity serialization
165+
// for CREATE paths where the executor builds domain objects that need
166+
// to be converted to the storage format.
167+
type WidgetSerializationBackend interface {
168+
// SerializeWidget converts a domain Widget to its storage representation.
169+
// The returned value is opaque to the caller; it is only used as input
170+
// to mutation operations or passed to the backend for persistence.
171+
SerializeWidget(w pages.Widget) (any, error)
172+
173+
// SerializeClientAction converts a domain ClientAction to storage format.
174+
SerializeClientAction(a pages.ClientAction) (any, error)
175+
176+
// SerializeDataSource converts a domain DataSource to storage format.
177+
SerializeDataSource(ds pages.DataSource) (any, error)
178+
179+
// SerializeWorkflowActivity converts a domain WorkflowActivity to storage format.
180+
SerializeWorkflowActivity(a workflows.WorkflowActivity) (any, error)
181+
}

mdl/bsonutil/bsonutil.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
// Package bsonutil provides BSON-aware ID conversion utilities for model elements.
4+
// It depends on mdl/types (WASM-safe) and the BSON driver (also WASM-safe),
5+
// but does NOT depend on sdk/mpr (which pulls in SQLite/CGO).
6+
package bsonutil
7+
8+
import (
9+
"github.com/mendixlabs/mxcli/mdl/types"
10+
"go.mongodb.org/mongo-driver/bson/primitive"
11+
)
12+
13+
// IDToBsonBinary converts a hex UUID string to a BSON binary value.
14+
func IDToBsonBinary(id string) primitive.Binary {
15+
blob := types.UUIDToBlob(id)
16+
if blob == nil || len(blob) != 16 {
17+
blob = types.UUIDToBlob(types.GenerateID())
18+
}
19+
return primitive.Binary{
20+
Subtype: 0x00,
21+
Data: blob,
22+
}
23+
}
24+
25+
// BsonBinaryToID converts a BSON binary value to a hex UUID string.
26+
func BsonBinaryToID(bin primitive.Binary) string {
27+
return types.BlobToUUID(bin.Data)
28+
}
29+
30+
// NewIDBsonBinary generates a new unique ID and returns it as a BSON binary value.
31+
func NewIDBsonBinary() primitive.Binary {
32+
return IDToBsonBinary(types.GenerateID())
33+
}

0 commit comments

Comments
 (0)