From e79e879cf4152c0c47ef665b316829528ee2645b Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 19:09:23 +0200 Subject: [PATCH 1/6] refactor: add Backend/MprPath/SqlMgr/ThemeRegistry/Settings to ExecContext, populate Backend on connect --- mdl/backend/java.go | 1 + mdl/backend/mock/backend.go | 1 + mdl/backend/mock/mock_java.go | 7 +++++++ mdl/backend/mpr/backend.go | 15 +++++++++++++++ mdl/executor/exec_context.go | 32 ++++++++++++++++++++++++++++--- mdl/executor/executor.go | 3 +++ mdl/executor/executor_connect.go | 6 ++++++ mdl/executor/executor_dispatch.go | 25 +++++++++++++----------- 8 files changed, 76 insertions(+), 14 deletions(-) diff --git a/mdl/backend/java.go b/mdl/backend/java.go index 384b27d3..4908f2c4 100644 --- a/mdl/backend/java.go +++ b/mdl/backend/java.go @@ -11,6 +11,7 @@ import ( // JavaBackend provides Java and JavaScript action operations. type JavaBackend interface { ListJavaActions() ([]*mpr.JavaAction, error) + ListJavaActionsFull() ([]*javaactions.JavaAction, error) ListJavaScriptActions() ([]*mpr.JavaScriptAction, error) ReadJavaActionByName(qualifiedName string) (*javaactions.JavaAction, error) ReadJavaScriptActionByName(qualifiedName string) (*mpr.JavaScriptAction, error) diff --git a/mdl/backend/mock/backend.go b/mdl/backend/mock/backend.go index ae1b9ea8..a330aa1e 100644 --- a/mdl/backend/mock/backend.go +++ b/mdl/backend/mock/backend.go @@ -203,6 +203,7 @@ type MockBackend struct { // JavaBackend ListJavaActionsFunc func() ([]*mpr.JavaAction, error) + ListJavaActionsFullFunc func() ([]*javaactions.JavaAction, error) ListJavaScriptActionsFunc func() ([]*mpr.JavaScriptAction, error) ReadJavaActionByNameFunc func(qualifiedName string) (*javaactions.JavaAction, error) ReadJavaScriptActionByNameFunc func(qualifiedName string) (*mpr.JavaScriptAction, error) diff --git a/mdl/backend/mock/mock_java.go b/mdl/backend/mock/mock_java.go index eef4a27c..776c6e80 100644 --- a/mdl/backend/mock/mock_java.go +++ b/mdl/backend/mock/mock_java.go @@ -15,6 +15,13 @@ func (m *MockBackend) ListJavaActions() ([]*mpr.JavaAction, error) { return nil, nil } +func (m *MockBackend) ListJavaActionsFull() ([]*javaactions.JavaAction, error) { + if m.ListJavaActionsFullFunc != nil { + return m.ListJavaActionsFullFunc() + } + return nil, nil +} + func (m *MockBackend) ListJavaScriptActions() ([]*mpr.JavaScriptAction, error) { if m.ListJavaScriptActionsFunc != nil { return m.ListJavaScriptActionsFunc() diff --git a/mdl/backend/mpr/backend.go b/mdl/backend/mpr/backend.go index f794e63f..5a8475c1 100644 --- a/mdl/backend/mpr/backend.go +++ b/mdl/backend/mpr/backend.go @@ -34,6 +34,18 @@ type MprBackend struct { path string } +// Wrap creates an MprBackend that wraps an existing Writer (and its Reader). +// This is used during migration when the Executor already owns the Writer +// and we want to expose it through the Backend interface without opening +// a second connection. +func Wrap(writer *mpr.Writer, path string) *MprBackend { + return &MprBackend{ + reader: writer.Reader(), + writer: writer, + path: path, + } +} + // --------------------------------------------------------------------------- // ConnectionBackend // --------------------------------------------------------------------------- @@ -526,6 +538,9 @@ func (b *MprBackend) DeleteJsonStructure(id string) error { func (b *MprBackend) ListJavaActions() ([]*mpr.JavaAction, error) { return b.reader.ListJavaActions() } +func (b *MprBackend) ListJavaActionsFull() ([]*javaactions.JavaAction, error) { + return b.reader.ListJavaActionsFull() +} func (b *MprBackend) ListJavaScriptActions() ([]*mpr.JavaScriptAction, error) { return b.reader.ListJavaScriptActions() } diff --git a/mdl/executor/exec_context.go b/mdl/executor/exec_context.go index 24881476..0d3a4157 100644 --- a/mdl/executor/exec_context.go +++ b/mdl/executor/exec_context.go @@ -10,10 +10,10 @@ import ( "github.com/mendixlabs/mxcli/mdl/backend" "github.com/mendixlabs/mxcli/mdl/catalog" "github.com/mendixlabs/mxcli/mdl/diaglog" + sqllib "github.com/mendixlabs/mxcli/sql" ) // ExecContext carries all dependencies a statement handler needs. -// It will replace the direct *Executor receiver once handlers are migrated. // // Design notes: // - Embeds context.Context for cancellation and timeout propagation. @@ -24,7 +24,8 @@ import ( type ExecContext struct { context.Context - // Backend provides all domain operations. + // Backend provides all domain operations (read/write/connect). + // Nil when not connected. Backend backend.FullBackend // Output is the writer for user-visible output (with line-limit guard). @@ -48,9 +49,34 @@ type ExecContext struct { // Cache holds per-session cached data for performance. Cache *executorCache + // MprPath is the filesystem path to the connected .mpr file. + // Empty when not connected. + MprPath string + + // SqlMgr manages external SQL database connections (lazy init). + SqlMgr *sqllib.Manager + + // ThemeRegistry holds cached theme design property definitions (lazy init). + ThemeRegistry *ThemeRegistry + + // Settings holds session-scoped key-value settings (SET command). + Settings map[string]any + // executor is a temporary back-reference used during incremental migration. // Handlers that have not yet been migrated to use Backend can access the // original Executor through this field. It will be removed once all handlers - // are migrated. + // are fully migrated to ctx.Backend. executor *Executor } + +// Connected returns true if a project is connected via the Backend. +func (ctx *ExecContext) Connected() bool { + return ctx.Backend != nil && ctx.Backend.IsConnected() +} + +// ConnectedForWrite returns true if a project is connected and the backend +// supports write operations. Currently equivalent to Connected() since +// MprBackend always supports writes. +func (ctx *ExecContext) ConnectedForWrite() bool { + return ctx.Connected() +} diff --git a/mdl/executor/executor.go b/mdl/executor/executor.go index 2f918b01..96ab981d 100644 --- a/mdl/executor/executor.go +++ b/mdl/executor/executor.go @@ -11,6 +11,7 @@ import ( "time" "github.com/mendixlabs/mxcli/mdl/ast" + "github.com/mendixlabs/mxcli/mdl/backend" "github.com/mendixlabs/mxcli/mdl/catalog" "github.com/mendixlabs/mxcli/mdl/diaglog" mdlerrors "github.com/mendixlabs/mxcli/mdl/errors" @@ -122,6 +123,7 @@ const ( type Executor struct { writer *mpr.Writer reader *mpr.Reader + backend backend.FullBackend // domain backend (populated on Connect) output io.Writer guard *outputGuard // line-limit wrapper around output mprPath string @@ -297,6 +299,7 @@ func (e *Executor) Close() error { e.writer.Close() e.writer = nil e.reader = nil + e.backend = nil } if e.sqlMgr != nil { e.sqlMgr.CloseAll() diff --git a/mdl/executor/executor_connect.go b/mdl/executor/executor_connect.go index 4c3a178d..ead11057 100644 --- a/mdl/executor/executor_connect.go +++ b/mdl/executor/executor_connect.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/mendixlabs/mxcli/mdl/ast" + mprbackend "github.com/mendixlabs/mxcli/mdl/backend/mpr" mdlerrors "github.com/mendixlabs/mxcli/mdl/errors" "github.com/mendixlabs/mxcli/sdk/mpr" ) @@ -26,6 +27,9 @@ func execConnect(ctx *ExecContext, s *ast.ConnectStmt) error { e.mprPath = s.Path e.cache = &executorCache{} // Initialize fresh cache + // Wrap the writer in an MprBackend for ctx.Backend propagation. + e.backend = mprbackend.Wrap(writer, s.Path) + // Display connection info with version pv := e.reader.ProjectVersion() if !ctx.Quiet { @@ -59,6 +63,7 @@ func reconnect(ctx *ExecContext) error { e.writer = writer e.reader = writer.Reader() e.cache = &executorCache{} // Reset cache + e.backend = mprbackend.Wrap(writer, e.mprPath) return nil } @@ -80,6 +85,7 @@ func execDisconnect(ctx *ExecContext) error { e.reader = nil e.mprPath = "" e.cache = nil + e.backend = nil return nil } diff --git a/mdl/executor/executor_dispatch.go b/mdl/executor/executor_dispatch.go index b22c05bb..a6afdcee 100644 --- a/mdl/executor/executor_dispatch.go +++ b/mdl/executor/executor_dispatch.go @@ -15,18 +15,21 @@ func (e *Executor) executeInner(ctx context.Context, stmt ast.Statement) error { } // newExecContext builds an ExecContext from the current Executor state. -// During the migration period, Backend may be nil; handlers access the -// Executor via ctx.executor for operations not yet routed through Backend. func (e *Executor) newExecContext(ctx context.Context) *ExecContext { return &ExecContext{ - Context: ctx, - Output: e.output, - Format: e.format, - Quiet: e.quiet, - Logger: e.logger, - Fragments: e.fragments, - Catalog: e.catalog, - Cache: e.cache, - executor: e, + Context: ctx, + Backend: e.backend, + Output: e.output, + Format: e.format, + Quiet: e.quiet, + Logger: e.logger, + Fragments: e.fragments, + Catalog: e.catalog, + Cache: e.cache, + MprPath: e.mprPath, + SqlMgr: e.sqlMgr, + ThemeRegistry: e.themeRegistry, + Settings: e.settings, + executor: e, } } From e26b4a88bea08057dfd7b75670ecb5f2bc98982f Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 19:19:07 +0200 Subject: [PATCH 2/6] refactor: replace e.reader/writer nil checks with ctx.Connected()/ConnectedForWrite() --- mdl/executor/autocomplete.go | 30 ++++++++-------- mdl/executor/cmd_agenteditor_agents.go | 5 ++- mdl/executor/cmd_agenteditor_kbs.go | 5 ++- mdl/executor/cmd_agenteditor_mcpservices.go | 5 ++- mdl/executor/cmd_agenteditor_models.go | 5 ++- mdl/executor/cmd_alter_page.go | 4 +-- mdl/executor/cmd_alter_workflow.go | 4 +-- mdl/executor/cmd_associations.go | 6 ++-- mdl/executor/cmd_businessevents.go | 10 +++--- mdl/executor/cmd_catalog.go | 8 ++--- mdl/executor/cmd_constants.go | 4 +-- mdl/executor/cmd_contract.go | 2 +- mdl/executor/cmd_datatransformer.go | 4 +-- mdl/executor/cmd_dbconnection.go | 2 +- mdl/executor/cmd_diff.go | 3 +- mdl/executor/cmd_diff_local.go | 4 +-- mdl/executor/cmd_domainmodel_elk.go | 4 +-- mdl/executor/cmd_entities.go | 8 ++--- mdl/executor/cmd_enumerations.go | 4 +-- mdl/executor/cmd_export_mappings.go | 8 ++--- mdl/executor/cmd_features.go | 4 +-- mdl/executor/cmd_folders.go | 4 +-- mdl/executor/cmd_fragments.go | 2 +- mdl/executor/cmd_imagecollections.go | 4 +-- mdl/executor/cmd_import.go | 3 +- mdl/executor/cmd_import_mappings.go | 8 ++--- mdl/executor/cmd_javaactions.go | 4 +-- mdl/executor/cmd_jsonstructures.go | 4 +-- mdl/executor/cmd_lint.go | 2 +- mdl/executor/cmd_mermaid.go | 3 +- mdl/executor/cmd_microflow_elk.go | 2 +- mdl/executor/cmd_microflows_create.go | 4 +-- mdl/executor/cmd_microflows_drop.go | 2 +- mdl/executor/cmd_microflows_format_action.go | 2 +- mdl/executor/cmd_misc.go | 2 +- mdl/executor/cmd_module_overview.go | 3 +- mdl/executor/cmd_modules.go | 8 ++--- mdl/executor/cmd_move.go | 3 +- mdl/executor/cmd_navigation.go | 2 +- mdl/executor/cmd_odata.go | 14 ++++---- mdl/executor/cmd_page_wireframe.go | 4 +-- mdl/executor/cmd_pages_builder.go | 4 +-- mdl/executor/cmd_pages_create_v3.go | 4 +-- mdl/executor/cmd_published_rest.go | 6 ++-- mdl/executor/cmd_rename.go | 3 +- mdl/executor/cmd_rest_clients.go | 4 +-- mdl/executor/cmd_security_write.go | 38 ++++++++++---------- mdl/executor/cmd_settings.go | 10 +++--- mdl/executor/cmd_structure.go | 3 +- mdl/executor/cmd_styling.go | 9 +++-- mdl/executor/cmd_widgets.go | 9 ++--- mdl/executor/cmd_workflows_write.go | 4 +-- mdl/executor/executor_connect.go | 8 ++--- mdl/executor/executor_query.go | 6 ++-- mdl/executor/helpers.go | 8 ++--- mdl/executor/hierarchy.go | 2 +- mdl/executor/validate.go | 6 ++-- 57 files changed, 155 insertions(+), 178 deletions(-) diff --git a/mdl/executor/autocomplete.go b/mdl/executor/autocomplete.go index c106c1fd..ab5aba06 100644 --- a/mdl/executor/autocomplete.go +++ b/mdl/executor/autocomplete.go @@ -9,7 +9,7 @@ import "context" // getModuleNames returns a list of all module names for autocomplete. func getModuleNames(ctx *ExecContext) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } modules, err := e.reader.ListModules() @@ -26,7 +26,7 @@ func getModuleNames(ctx *ExecContext) []string { // getMicroflowNamesAC returns qualified microflow names, optionally filtered by module. func getMicroflowNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -51,7 +51,7 @@ func getMicroflowNamesAC(ctx *ExecContext, moduleFilter string) []string { // getEntityNamesAC returns qualified entity names, optionally filtered by module. func getEntityNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -78,7 +78,7 @@ func getEntityNamesAC(ctx *ExecContext, moduleFilter string) []string { // getPageNamesAC returns qualified page names, optionally filtered by module. func getPageNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -103,7 +103,7 @@ func getPageNamesAC(ctx *ExecContext, moduleFilter string) []string { // getSnippetNamesAC returns qualified snippet names, optionally filtered by module. func getSnippetNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -128,7 +128,7 @@ func getSnippetNamesAC(ctx *ExecContext, moduleFilter string) []string { // getAssociationNamesAC returns qualified association names, optionally filtered by module. func getAssociationNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -155,7 +155,7 @@ func getAssociationNamesAC(ctx *ExecContext, moduleFilter string) []string { // getEnumerationNamesAC returns qualified enumeration names, optionally filtered by module. func getEnumerationNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -180,7 +180,7 @@ func getEnumerationNamesAC(ctx *ExecContext, moduleFilter string) []string { // getLayoutNamesAC returns qualified layout names, optionally filtered by module. func getLayoutNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -205,7 +205,7 @@ func getLayoutNamesAC(ctx *ExecContext, moduleFilter string) []string { // getJavaActionNamesAC returns qualified Java action names, optionally filtered by module. func getJavaActionNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -230,7 +230,7 @@ func getJavaActionNamesAC(ctx *ExecContext, moduleFilter string) []string { // getODataClientNamesAC returns qualified consumed OData service names, optionally filtered by module. func getODataClientNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -255,7 +255,7 @@ func getODataClientNamesAC(ctx *ExecContext, moduleFilter string) []string { // getODataServiceNamesAC returns qualified published OData service names, optionally filtered by module. func getODataServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -280,7 +280,7 @@ func getODataServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { // getRestClientNamesAC returns qualified consumed REST service names, optionally filtered by module. func getRestClientNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -305,7 +305,7 @@ func getRestClientNamesAC(ctx *ExecContext, moduleFilter string) []string { // getDatabaseConnectionNamesAC returns qualified database connection names, optionally filtered by module. func getDatabaseConnectionNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -330,7 +330,7 @@ func getDatabaseConnectionNamesAC(ctx *ExecContext, moduleFilter string) []strin // getBusinessEventServiceNamesAC returns qualified business event service names, optionally filtered by module. func getBusinessEventServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) @@ -355,7 +355,7 @@ func getBusinessEventServiceNamesAC(ctx *ExecContext, moduleFilter string) []str // getJsonStructureNamesAC returns qualified JSON structure names, optionally filtered by module. func getJsonStructureNamesAC(ctx *ExecContext, moduleFilter string) []string { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil } h, err := getHierarchy(ctx) diff --git a/mdl/executor/cmd_agenteditor_agents.go b/mdl/executor/cmd_agenteditor_agents.go index af051ebb..f8172874 100644 --- a/mdl/executor/cmd_agenteditor_agents.go +++ b/mdl/executor/cmd_agenteditor_agents.go @@ -19,7 +19,7 @@ import ( // showAgentEditorAgents handles SHOW AGENTS [IN module]. func showAgentEditorAgents(ctx *ExecContext, moduleName string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -65,8 +65,7 @@ func showAgentEditorAgents(ctx *ExecContext, moduleName string) error { // describeAgentEditorAgent handles DESCRIBE AGENT Module.Name. Emits a // round-trippable CREATE AGENT statement reflecting the Contents JSON. func describeAgentEditorAgent(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_agenteditor_kbs.go b/mdl/executor/cmd_agenteditor_kbs.go index 7258ee20..78e6047c 100644 --- a/mdl/executor/cmd_agenteditor_kbs.go +++ b/mdl/executor/cmd_agenteditor_kbs.go @@ -19,7 +19,7 @@ import ( // showAgentEditorKnowledgeBases handles SHOW KNOWLEDGE BASES [IN module]. func showAgentEditorKnowledgeBases(ctx *ExecContext, moduleName string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -63,8 +63,7 @@ func showAgentEditorKnowledgeBases(ctx *ExecContext, moduleName string) error { // describeAgentEditorKnowledgeBase handles DESCRIBE KNOWLEDGE BASE Module.Name. func describeAgentEditorKnowledgeBase(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_agenteditor_mcpservices.go b/mdl/executor/cmd_agenteditor_mcpservices.go index f22bfbda..5c1ed439 100644 --- a/mdl/executor/cmd_agenteditor_mcpservices.go +++ b/mdl/executor/cmd_agenteditor_mcpservices.go @@ -19,7 +19,7 @@ import ( // showAgentEditorConsumedMCPServices handles SHOW CONSUMED MCP SERVICES [IN module]. func showAgentEditorConsumedMCPServices(ctx *ExecContext, moduleName string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -59,8 +59,7 @@ func showAgentEditorConsumedMCPServices(ctx *ExecContext, moduleName string) err // describeAgentEditorConsumedMCPService handles DESCRIBE CONSUMED MCP SERVICE Module.Name. func describeAgentEditorConsumedMCPService(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_agenteditor_models.go b/mdl/executor/cmd_agenteditor_models.go index 956359d7..18b6ccb6 100644 --- a/mdl/executor/cmd_agenteditor_models.go +++ b/mdl/executor/cmd_agenteditor_models.go @@ -19,7 +19,7 @@ import ( // showAgentEditorModels handles SHOW MODELS [IN module]. func showAgentEditorModels(ctx *ExecContext, moduleName string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -66,8 +66,7 @@ func showAgentEditorModels(ctx *ExecContext, moduleName string) error { // describeAgentEditorModel handles DESCRIBE MODEL Module.Name. // Emits a round-trippable CREATE MODEL statement. func describeAgentEditorModel(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_alter_page.go b/mdl/executor/cmd_alter_page.go index be1b79bc..19fb6aea 100644 --- a/mdl/executor/cmd_alter_page.go +++ b/mdl/executor/cmd_alter_page.go @@ -18,10 +18,10 @@ import ( // execAlterPage handles ALTER PAGE/SNIPPET Module.Name { operations }. func execAlterPage(ctx *ExecContext, s *ast.AlterPageStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_alter_workflow.go b/mdl/executor/cmd_alter_workflow.go index f73f183b..29340ec9 100644 --- a/mdl/executor/cmd_alter_workflow.go +++ b/mdl/executor/cmd_alter_workflow.go @@ -22,10 +22,10 @@ const bsonArrayMarker = int32(3) // execAlterWorkflow handles ALTER WORKFLOW Module.Name { operations }. func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_associations.go b/mdl/executor/cmd_associations.go index 614603ad..80f567b8 100644 --- a/mdl/executor/cmd_associations.go +++ b/mdl/executor/cmd_associations.go @@ -17,7 +17,7 @@ import ( // execCreateAssociation handles CREATE ASSOCIATION statements. func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -146,7 +146,7 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error // execAlterAssociation handles ALTER ASSOCIATION statements. func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -212,7 +212,7 @@ func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { // execDropAssociation handles DROP ASSOCIATION statements. func execDropAssociation(ctx *ExecContext, s *ast.DropAssociationStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_businessevents.go b/mdl/executor/cmd_businessevents.go index f2555208..2ba10c93 100644 --- a/mdl/executor/cmd_businessevents.go +++ b/mdl/executor/cmd_businessevents.go @@ -16,7 +16,7 @@ import ( func showBusinessEventServices(ctx *ExecContext, inModule string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -98,7 +98,7 @@ func showBusinessEventClients(ctx *ExecContext, inModule string) error { func showBusinessEvents(ctx *ExecContext, inModule string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -177,7 +177,7 @@ func showBusinessEvents(ctx *ExecContext, inModule string) error { func describeBusinessEventService(ctx *ExecContext, name ast.QualifiedName) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -269,7 +269,7 @@ func describeBusinessEventService(ctx *ExecContext, name ast.QualifiedName) erro func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -385,7 +385,7 @@ func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventS func dropBusinessEventService(ctx *ExecContext, stmt *ast.DropBusinessEventServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_catalog.go b/mdl/executor/cmd_catalog.go index af2b1f3c..2da8d52e 100644 --- a/mdl/executor/cmd_catalog.go +++ b/mdl/executor/cmd_catalog.go @@ -411,7 +411,7 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error { // execRefreshCatalogStmt handles REFRESH CATALOG [FULL] [SOURCE] [FORCE] [BACKGROUND] command. func execRefreshCatalogStmt(ctx *ExecContext, stmt *ast.RefreshCatalogStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -767,8 +767,7 @@ func preWarmCache(ctx *ExecContext) { // execSearch handles SEARCH 'query' command. func execSearch(ctx *ExecContext, stmt *ast.SearchStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -830,8 +829,7 @@ func escapeFTSQuery(q string) string { // search performs a full-text search with the specified output format. // Format can be: "table" (default), "names" (just qualified names), "json" func search(ctx *ExecContext, query, format string) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_constants.go b/mdl/executor/cmd_constants.go index dbfabbb4..3c08abef 100644 --- a/mdl/executor/cmd_constants.go +++ b/mdl/executor/cmd_constants.go @@ -260,7 +260,7 @@ func formatDefaultValue(dt model.ConstantDataType, value string) string { func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -447,7 +447,7 @@ func showConstantValues(ctx *ExecContext, moduleName string) error { func dropConstant(ctx *ExecContext, stmt *ast.DropConstantStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_contract.go b/mdl/executor/cmd_contract.go index aec5a48f..3fc0df8b 100644 --- a/mdl/executor/cmd_contract.go +++ b/mdl/executor/cmd_contract.go @@ -448,7 +448,7 @@ var reservedEntityAttrNames = map[string]bool{ // what Studio Pro produces. func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_datatransformer.go b/mdl/executor/cmd_datatransformer.go index b497ca68..10b81142 100644 --- a/mdl/executor/cmd_datatransformer.go +++ b/mdl/executor/cmd_datatransformer.go @@ -110,7 +110,7 @@ func describeDataTransformer(ctx *ExecContext, name ast.QualifiedName) error { func execCreateDataTransformer(ctx *ExecContext, s *ast.CreateDataTransformerStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -154,7 +154,7 @@ func execCreateDataTransformer(ctx *ExecContext, s *ast.CreateDataTransformerStm func execDropDataTransformer(ctx *ExecContext, s *ast.DropDataTransformerStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_dbconnection.go b/mdl/executor/cmd_dbconnection.go index 2e0d3c44..8fb91d43 100644 --- a/mdl/executor/cmd_dbconnection.go +++ b/mdl/executor/cmd_dbconnection.go @@ -16,7 +16,7 @@ import ( func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnectionStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_diff.go b/mdl/executor/cmd_diff.go index f05bd4d0..03c130b8 100644 --- a/mdl/executor/cmd_diff.go +++ b/mdl/executor/cmd_diff.go @@ -68,8 +68,7 @@ const ( // DiffProgram compares an MDL program against the current project state func diffProgram(ctx *ExecContext, prog *ast.Program, opts DiffOptions) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_diff_local.go b/mdl/executor/cmd_diff_local.go index b698c8ad..4689e8c6 100644 --- a/mdl/executor/cmd_diff_local.go +++ b/mdl/executor/cmd_diff_local.go @@ -31,7 +31,7 @@ import ( // - A range "base..target" — compares two revisions (no working tree) func diffLocal(ctx *ExecContext, ref string, opts DiffOptions) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -521,7 +521,7 @@ func buildNameLookups(ctx *ExecContext) (map[model.ID]string, map[model.ID]strin e := ctx.executor entityNames := make(map[model.ID]string) microflowNames := make(map[model.ID]string) - if e.reader == nil { + if !ctx.Connected() { return entityNames, microflowNames } h, err := getHierarchy(ctx) diff --git a/mdl/executor/cmd_domainmodel_elk.go b/mdl/executor/cmd_domainmodel_elk.go index c1abb80a..78022f7e 100644 --- a/mdl/executor/cmd_domainmodel_elk.go +++ b/mdl/executor/cmd_domainmodel_elk.go @@ -69,7 +69,7 @@ const ( // If name contains a dot (e.g. "Module.Entity"), it delegates to entityFocusELK for a focused view. func domainModelELK(ctx *ExecContext, name string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -158,7 +158,7 @@ func domainModelELK(ctx *ExecContext, name string) error { // and entities directly connected to it via associations or generalization. func entityFocusELK(ctx *ExecContext, qualifiedName string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_entities.go b/mdl/executor/cmd_entities.go index ffd37705..428e976c 100644 --- a/mdl/executor/cmd_entities.go +++ b/mdl/executor/cmd_entities.go @@ -47,7 +47,7 @@ func buildEventHandlers(ctx *ExecContext, defs []ast.EventHandlerDef) ([]*domain func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -295,7 +295,7 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { // execCreateViewEntity handles CREATE VIEW ENTITY statements. func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -465,7 +465,7 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { // execAlterEntity handles ALTER ENTITY statements. func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -919,7 +919,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { // execDropEntity handles DROP ENTITY statements. func execDropEntity(ctx *ExecContext, s *ast.DropEntityStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_enumerations.go b/mdl/executor/cmd_enumerations.go index a458773a..8acc747b 100644 --- a/mdl/executor/cmd_enumerations.go +++ b/mdl/executor/cmd_enumerations.go @@ -18,7 +18,7 @@ import ( func execCreateEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -115,7 +115,7 @@ func execAlterEnumeration(ctx *ExecContext, s *ast.AlterEnumerationStmt) error { func execDropEnumeration(ctx *ExecContext, s *ast.DropEnumerationStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_export_mappings.go b/mdl/executor/cmd_export_mappings.go index 7fc3419a..2e3e7a16 100644 --- a/mdl/executor/cmd_export_mappings.go +++ b/mdl/executor/cmd_export_mappings.go @@ -17,7 +17,7 @@ import ( // showExportMappings prints a table of all export mapping documents. func showExportMappings(ctx *ExecContext, inModule string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -81,7 +81,7 @@ func showExportMappings(ctx *ExecContext, inModule string) error { // describeExportMapping prints the MDL representation of an export mapping. func describeExportMapping(ctx *ExecContext, name ast.QualifiedName) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -180,7 +180,7 @@ func printExportMappingElement(w io.Writer, elem *model.ExportMappingElement, de // execCreateExportMapping creates a new export mapping. func execCreateExportMapping(ctx *ExecContext, s *ast.CreateExportMappingStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -365,7 +365,7 @@ func buildExportMappingElementModel(moduleName string, def *ast.ExportMappingEle // execDropExportMapping deletes an export mapping. func execDropExportMapping(ctx *ExecContext, s *ast.DropExportMappingStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_features.go b/mdl/executor/cmd_features.go index 02a74fc7..76e7ade0 100644 --- a/mdl/executor/cmd_features.go +++ b/mdl/executor/cmd_features.go @@ -17,7 +17,7 @@ import ( func checkFeature(ctx *ExecContext, area, name, statement, hint string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil // No project connected; skip check } reg, err := versions.Load() @@ -78,7 +78,7 @@ func execShowFeatures(ctx *ExecContext, s *ast.ShowFeaturesStmt) error { default: // SHOW FEATURES [IN area] — requires project connection - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnectedMsg("not connected to a project\n hint: use SHOW FEATURES FOR VERSION x.y without a project connection") } rpv := e.reader.ProjectVersion() diff --git a/mdl/executor/cmd_folders.go b/mdl/executor/cmd_folders.go index af2a630f..9c2dfe45 100644 --- a/mdl/executor/cmd_folders.go +++ b/mdl/executor/cmd_folders.go @@ -52,7 +52,7 @@ func findFolderByPath(ctx *ExecContext, moduleID model.ID, folderPath string, fo // The folder must be empty (no child documents or sub-folders). func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnected() } @@ -83,7 +83,7 @@ func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { // execMoveFolder handles MOVE FOLDER Module.FolderName TO ... statements. func execMoveFolder(ctx *ExecContext, s *ast.MoveFolderStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_fragments.go b/mdl/executor/cmd_fragments.go index f822d9fa..cb698100 100644 --- a/mdl/executor/cmd_fragments.go +++ b/mdl/executor/cmd_fragments.go @@ -73,7 +73,7 @@ func describeFragment(ctx *ExecContext, name ast.QualifiedName) error { // It finds a named widget in a page or snippet and outputs it as MDL. func describeFragmentFrom(ctx *ExecContext, s *ast.DescribeFragmentFromStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_imagecollections.go b/mdl/executor/cmd_imagecollections.go index 97537064..702c0179 100644 --- a/mdl/executor/cmd_imagecollections.go +++ b/mdl/executor/cmd_imagecollections.go @@ -17,7 +17,7 @@ import ( // execCreateImageCollection handles CREATE IMAGE COLLECTION statements. func execCreateImageCollection(ctx *ExecContext, s *ast.CreateImageCollectionStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -77,7 +77,7 @@ func execCreateImageCollection(ctx *ExecContext, s *ast.CreateImageCollectionStm // execDropImageCollection handles DROP IMAGE COLLECTION statements. func execDropImageCollection(ctx *ExecContext, s *ast.DropImageCollectionStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_import.go b/mdl/executor/cmd_import.go index c7b71e7e..fd05b323 100644 --- a/mdl/executor/cmd_import.go +++ b/mdl/executor/cmd_import.go @@ -16,8 +16,7 @@ import ( // execImport handles IMPORT FROM QUERY '' INTO Module.Entity MAP (...) [LINK (...)] [BATCH n] [LIMIT n] func execImport(ctx *ExecContext, s *ast.ImportStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_import_mappings.go b/mdl/executor/cmd_import_mappings.go index d59afdea..cb188241 100644 --- a/mdl/executor/cmd_import_mappings.go +++ b/mdl/executor/cmd_import_mappings.go @@ -17,7 +17,7 @@ import ( // showImportMappings prints a table of all import mapping documents. func showImportMappings(ctx *ExecContext, inModule string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -81,7 +81,7 @@ func showImportMappings(ctx *ExecContext, inModule string) error { // describeImportMapping prints the MDL representation of an import mapping. func describeImportMapping(ctx *ExecContext, name ast.QualifiedName) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -193,7 +193,7 @@ func printImportMappingElement(w io.Writer, elem *model.ImportMappingElement, de // execCreateImportMapping creates a new import mapping. func execCreateImportMapping(ctx *ExecContext, s *ast.CreateImportMappingStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -379,7 +379,7 @@ func resolveAttributeType(entityQN, attrName string, reader *mpr.Reader) string // execDropImportMapping deletes an import mapping. func execDropImportMapping(ctx *ExecContext, s *ast.DropImportMappingStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_javaactions.go b/mdl/executor/cmd_javaactions.go index 854e04dd..4dc4628c 100644 --- a/mdl/executor/cmd_javaactions.go +++ b/mdl/executor/cmd_javaactions.go @@ -250,7 +250,7 @@ func formatJavaActionReturnType(t javaactions.CodeActionReturnType) string { // execDropJavaAction handles DROP JAVA ACTION statements. func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -284,7 +284,7 @@ func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { // execCreateJavaAction handles CREATE JAVA ACTION statements. func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_jsonstructures.go b/mdl/executor/cmd_jsonstructures.go index 21ea7daa..781dc523 100644 --- a/mdl/executor/cmd_jsonstructures.go +++ b/mdl/executor/cmd_jsonstructures.go @@ -175,7 +175,7 @@ func capitalizeFirstRune(s string) string { // execCreateJsonStructure handles CREATE [OR REPLACE] JSON STRUCTURE statements. func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -245,7 +245,7 @@ func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) e // execDropJsonStructure handles DROP JSON STRUCTURE statements. func execDropJsonStructure(ctx *ExecContext, s *ast.DropJsonStructureStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_lint.go b/mdl/executor/cmd_lint.go index 137f2585..7b834cda 100644 --- a/mdl/executor/cmd_lint.go +++ b/mdl/executor/cmd_lint.go @@ -16,7 +16,7 @@ import ( // execLint executes a LINT statement. func execLint(ctx *ExecContext, s *ast.LintStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_mermaid.go b/mdl/executor/cmd_mermaid.go index 3febd039..27e05f0f 100644 --- a/mdl/executor/cmd_mermaid.go +++ b/mdl/executor/cmd_mermaid.go @@ -18,8 +18,7 @@ import ( // describeMermaid generates a Mermaid diagram for the given object type and name. // Supported types: entity (renders full domain model), microflow, page. func describeMermaid(ctx *ExecContext, objectType, name string) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_microflow_elk.go b/mdl/executor/cmd_microflow_elk.go index f8a7ab32..d4b3aafb 100644 --- a/mdl/executor/cmd_microflow_elk.go +++ b/mdl/executor/cmd_microflow_elk.go @@ -63,7 +63,7 @@ type microflowELKEdge struct { // microflowELK generates a JSON graph of a microflow for rendering with ELK.js. func microflowELK(ctx *ExecContext, name string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_microflows_create.go b/mdl/executor/cmd_microflows_create.go index 369321f6..8c54ad10 100644 --- a/mdl/executor/cmd_microflows_create.go +++ b/mdl/executor/cmd_microflows_create.go @@ -26,7 +26,7 @@ func isBuiltinModuleEntity(moduleName string) bool { // loadRestServices returns all consumed REST services, or nil if no reader. func loadRestServices(ctx *ExecContext) ([]*model.ConsumedRestService, error) { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return nil, nil } svcs, err := e.reader.ListConsumedRestServices() @@ -35,7 +35,7 @@ func loadRestServices(ctx *ExecContext) ([]*model.ConsumedRestService, error) { func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_microflows_drop.go b/mdl/executor/cmd_microflows_drop.go index 8e8368fd..5c4bbd7a 100644 --- a/mdl/executor/cmd_microflows_drop.go +++ b/mdl/executor/cmd_microflows_drop.go @@ -13,7 +13,7 @@ import ( // execDropMicroflow handles DROP MICROFLOW statements. func execDropMicroflow(ctx *ExecContext, s *ast.DropMicroflowStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_microflows_format_action.go b/mdl/executor/cmd_microflows_format_action.go index d2667885..5f67bed5 100644 --- a/mdl/executor/cmd_microflows_format_action.go +++ b/mdl/executor/cmd_microflows_format_action.go @@ -498,7 +498,7 @@ func formatAction( case *microflows.ShowPageAction: // Get page name from action (PageName is BY_NAME_REFERENCE, PageID is legacy BY_ID_REFERENCE) pageName := a.PageName - if pageName == "" && a.PageID != "" && e.reader != nil { + if pageName == "" && a.PageID != "" && ctx.Connected() { // Fall back to looking up by ID (legacy format) pages, _ := e.reader.ListPages() for _, p := range pages { diff --git a/mdl/executor/cmd_misc.go b/mdl/executor/cmd_misc.go index f55fb6d4..2684e818 100644 --- a/mdl/executor/cmd_misc.go +++ b/mdl/executor/cmd_misc.go @@ -326,7 +326,7 @@ Statement Terminator: // showVersion displays Mendix project version information. func showVersion(ctx *ExecContext) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_module_overview.go b/mdl/executor/cmd_module_overview.go index 4ca2c2cf..2588b35f 100644 --- a/mdl/executor/cmd_module_overview.go +++ b/mdl/executor/cmd_module_overview.go @@ -48,8 +48,7 @@ var systemModuleNames = map[string]bool{ // ModuleOverview generates a JSON graph of all project modules and their // cross-module dependencies, suitable for rendering with ELK.js. func ModuleOverview(ctx *ExecContext) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_modules.go b/mdl/executor/cmd_modules.go index ffcedb4b..fdfa1283 100644 --- a/mdl/executor/cmd_modules.go +++ b/mdl/executor/cmd_modules.go @@ -17,7 +17,7 @@ import ( // execCreateModule handles CREATE MODULE statements. func execCreateModule(ctx *ExecContext, s *ast.CreateModuleStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -61,7 +61,7 @@ func execCreateModule(ctx *ExecContext, s *ast.CreateModuleStmt) error { // - Constants func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -381,7 +381,7 @@ func getModuleContainers(ctx *ExecContext, moduleID model.ID) map[model.ID]bool // showModules handles SHOW MODULES command. func showModules(ctx *ExecContext) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -575,7 +575,7 @@ func showModules(ctx *ExecContext) error { // describeModule handles DESCRIBE MODULE [WITH ALL] command. func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_move.go b/mdl/executor/cmd_move.go index 76ecdcb3..b8fd679d 100644 --- a/mdl/executor/cmd_move.go +++ b/mdl/executor/cmd_move.go @@ -14,8 +14,7 @@ import ( // execMove handles MOVE PAGE/MICROFLOW/SNIPPET/NANOFLOW/ENTITY/ENUMERATION statements. func execMove(ctx *ExecContext, s *ast.MoveStmt) error { - e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_navigation.go b/mdl/executor/cmd_navigation.go index 5fedf117..ebe9ea53 100644 --- a/mdl/executor/cmd_navigation.go +++ b/mdl/executor/cmd_navigation.go @@ -16,7 +16,7 @@ import ( // It fully replaces the profile's home pages, login page, not-found page, and menu tree. func execAlterNavigation(ctx *ExecContext, s *ast.AlterNavigationStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_odata.go b/mdl/executor/cmd_odata.go index 2e945685..c922adbc 100644 --- a/mdl/executor/cmd_odata.go +++ b/mdl/executor/cmd_odata.go @@ -757,7 +757,7 @@ func outputExternalEntityMDL(ctx *ExecContext, entity *domainmodel.Entity, modul func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -862,7 +862,7 @@ func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1055,7 +1055,7 @@ func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error func alterODataClient(ctx *ExecContext, stmt *ast.AlterODataClientStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1146,7 +1146,7 @@ func alterODataClient(ctx *ExecContext, stmt *ast.AlterODataClientStmt) error { func dropODataClient(ctx *ExecContext, stmt *ast.DropODataClientStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1180,7 +1180,7 @@ func dropODataClient(ctx *ExecContext, stmt *ast.DropODataClientStmt) error { func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1284,7 +1284,7 @@ func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) erro func alterODataService(ctx *ExecContext, stmt *ast.AlterODataServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1341,7 +1341,7 @@ func alterODataService(ctx *ExecContext, stmt *ast.AlterODataServiceStmt) error func dropODataService(ctx *ExecContext, stmt *ast.DropODataServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_page_wireframe.go b/mdl/executor/cmd_page_wireframe.go index 31204839..6744bdbd 100644 --- a/mdl/executor/cmd_page_wireframe.go +++ b/mdl/executor/cmd_page_wireframe.go @@ -91,7 +91,7 @@ func (c *wireframeCounter) next() string { // PageWireframeJSON generates wireframe JSON for a page. func PageWireframeJSON(ctx *ExecContext, name string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -205,7 +205,7 @@ func PageWireframeJSON(ctx *ExecContext, name string) error { // SnippetWireframeJSON generates wireframe JSON for a snippet. func SnippetWireframeJSON(ctx *ExecContext, name string) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_pages_builder.go b/mdl/executor/cmd_pages_builder.go index a88f869e..c4ddd262 100644 --- a/mdl/executor/cmd_pages_builder.go +++ b/mdl/executor/cmd_pages_builder.go @@ -337,7 +337,7 @@ func (pb *pageBuilder) createFolder(name string, containerID model.ID) (model.ID // execDropPage handles DROP PAGE statement. func execDropPage(ctx *ExecContext, s *ast.DropPageStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -364,7 +364,7 @@ func execDropPage(ctx *ExecContext, s *ast.DropPageStmt) error { // execDropSnippet handles DROP SNIPPET statement. func execDropSnippet(ctx *ExecContext, s *ast.DropSnippetStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_pages_create_v3.go b/mdl/executor/cmd_pages_create_v3.go index d515ec2e..addaf11f 100644 --- a/mdl/executor/cmd_pages_create_v3.go +++ b/mdl/executor/cmd_pages_create_v3.go @@ -17,7 +17,7 @@ import ( // execCreatePageV3 handles CREATE PAGE statement with V3 syntax. func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -102,7 +102,7 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { // execCreateSnippetV3 handles CREATE SNIPPET statement with V3 syntax. func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_published_rest.go b/mdl/executor/cmd_published_rest.go index 4e94df21..9483ac90 100644 --- a/mdl/executor/cmd_published_rest.go +++ b/mdl/executor/cmd_published_rest.go @@ -185,7 +185,7 @@ func findPublishedRestService(ctx *ExecContext, moduleName, name string) (*model func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRestServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -262,7 +262,7 @@ func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRest func execDropPublishedRestService(ctx *ExecContext, s *ast.DropPublishedRestServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -313,7 +313,7 @@ func astResourceDefToModel(def *ast.PublishedRestResourceDef) *model.PublishedRe func execAlterPublishedRestService(ctx *ExecContext, s *ast.AlterPublishedRestServiceStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_rename.go b/mdl/executor/cmd_rename.go index 959aeaf6..1a56c070 100644 --- a/mdl/executor/cmd_rename.go +++ b/mdl/executor/cmd_rename.go @@ -14,8 +14,7 @@ import ( // execRename handles RENAME statements for all document types. func execRename(ctx *ExecContext, s *ast.RenameStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_rest_clients.go b/mdl/executor/cmd_rest_clients.go index 22e62cf9..5d47df89 100644 --- a/mdl/executor/cmd_rest_clients.go +++ b/mdl/executor/cmd_rest_clients.go @@ -283,7 +283,7 @@ func writeExportMappings(w io.Writer, mappings []*model.RestResponseMapping, ind func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -464,7 +464,7 @@ func convertMappingEntries(entries []ast.RestMappingEntry, importDirection bool) func dropRestClient(ctx *ExecContext, stmt *ast.DropRestClientStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_security_write.go b/mdl/executor/cmd_security_write.go index 10fb0755..97144d24 100644 --- a/mdl/executor/cmd_security_write.go +++ b/mdl/executor/cmd_security_write.go @@ -17,7 +17,7 @@ import ( // execCreateModuleRole handles CREATE MODULE ROLE Module.RoleName [DESCRIPTION '...']. func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -51,7 +51,7 @@ func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { // allowed roles, and OData service allowed roles before deleting the role itself. func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -164,7 +164,7 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { // execCreateUserRole handles CREATE [OR MODIFY] USER ROLE Name (ModuleRoles) [MANAGE ALL ROLES]. func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -206,7 +206,7 @@ func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { // execAlterUserRole handles ALTER USER ROLE Name ADD/REMOVE MODULE ROLES (...). func execAlterUserRole(ctx *ExecContext, s *ast.AlterUserRoleStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -250,7 +250,7 @@ func execAlterUserRole(ctx *ExecContext, s *ast.AlterUserRoleStmt) error { // execDropUserRole handles DROP USER ROLE Name. func execDropUserRole(ctx *ExecContext, s *ast.DropUserRoleStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -282,7 +282,7 @@ func execDropUserRole(ctx *ExecContext, s *ast.DropUserRoleStmt) error { // execGrantEntityAccess handles GRANT roles ON Module.Entity (rights) [WHERE '...']. func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -439,7 +439,7 @@ func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error // execRevokeEntityAccess handles REVOKE roles ON Module.Entity [(rights...)]. func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -527,7 +527,7 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err // execGrantMicroflowAccess handles GRANT EXECUTE ON MICROFLOW Module.MF TO roles. func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -590,7 +590,7 @@ func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) // execRevokeMicroflowAccess handles REVOKE EXECUTE ON MICROFLOW Module.MF FROM roles. func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -647,7 +647,7 @@ func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStm // execGrantPageAccess handles GRANT VIEW ON PAGE Module.Page TO roles. func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -710,7 +710,7 @@ func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { // execRevokePageAccess handles REVOKE VIEW ON PAGE Module.Page FROM roles. func execRevokePageAccess(ctx *ExecContext, s *ast.RevokePageAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -803,7 +803,7 @@ func validateModuleRole(ctx *ExecContext, role ast.QualifiedName) error { // execAlterProjectSecurity handles ALTER PROJECT SECURITY LEVEL/DEMO USERS. func execAlterProjectSecurity(ctx *ExecContext, s *ast.AlterProjectSecurityStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -849,7 +849,7 @@ func execAlterProjectSecurity(ctx *ExecContext, s *ast.AlterProjectSecurityStmt) // execCreateDemoUser handles CREATE [OR MODIFY] DEMO USER 'name' PASSWORD 'pw' [ENTITY Module.Entity] (Roles). func execCreateDemoUser(ctx *ExecContext, s *ast.CreateDemoUserStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -961,7 +961,7 @@ func joinCandidates(candidates []string) string { // execDropDemoUser handles DROP DEMO USER 'name'. func execDropDemoUser(ctx *ExecContext, s *ast.DropDemoUserStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -997,7 +997,7 @@ func execDropDemoUser(ctx *ExecContext, s *ast.DropDemoUserStmt) error { // execGrantODataServiceAccess handles GRANT ACCESS ON ODATA SERVICE Module.Svc TO roles. func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1060,7 +1060,7 @@ func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAcces // execRevokeODataServiceAccess handles REVOKE ACCESS ON ODATA SERVICE Module.Svc FROM roles. func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1121,7 +1121,7 @@ func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAcc // execGrantPublishedRestServiceAccess handles GRANT ACCESS ON PUBLISHED REST SERVICE Module.Svc TO roles. func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublishedRestServiceAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1189,7 +1189,7 @@ func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublished // execRevokePublishedRestServiceAccess handles REVOKE ACCESS ON PUBLISHED REST SERVICE Module.Svc FROM roles. func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublishedRestServiceAccessStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1245,7 +1245,7 @@ func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublish // execUpdateSecurity handles UPDATE SECURITY [IN Module]. func execUpdateSecurity(ctx *ExecContext, s *ast.UpdateSecurityStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_settings.go b/mdl/executor/cmd_settings.go index 4a2d2b1d..d84d51e4 100644 --- a/mdl/executor/cmd_settings.go +++ b/mdl/executor/cmd_settings.go @@ -15,7 +15,7 @@ import ( // showSettings displays an overview table of all settings parts. func showSettings(ctx *ExecContext) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -86,7 +86,7 @@ func showSettings(ctx *ExecContext) error { // describeSettings outputs the full MDL description of all settings. func describeSettings(ctx *ExecContext) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -172,7 +172,7 @@ func describeSettings(ctx *ExecContext) error { // alterSettings modifies project settings based on ALTER SETTINGS statement. func alterSettings(ctx *ExecContext, stmt *ast.AlterSettingsStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -398,7 +398,7 @@ func alterSettingsConstant(ctx *ExecContext, ps *model.ProjectSettings, stmt *as // createConfiguration handles CREATE CONFIGURATION 'name' [properties...]. func createConfiguration(ctx *ExecContext, stmt *ast.CreateConfigurationStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -468,7 +468,7 @@ func createConfiguration(ctx *ExecContext, stmt *ast.CreateConfigurationStmt) er // dropConfiguration handles DROP CONFIGURATION 'name'. func dropConfiguration(ctx *ExecContext, stmt *ast.DropConfigurationStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_structure.go b/mdl/executor/cmd_structure.go index 8d2466f7..08d692eb 100644 --- a/mdl/executor/cmd_structure.go +++ b/mdl/executor/cmd_structure.go @@ -18,8 +18,7 @@ import ( // execShowStructure handles SHOW STRUCTURE [DEPTH n] [IN module] [ALL]. func execShowStructure(ctx *ExecContext, s *ast.ShowStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/cmd_styling.go b/mdl/executor/cmd_styling.go index 48ecd04e..5f6f8a75 100644 --- a/mdl/executor/cmd_styling.go +++ b/mdl/executor/cmd_styling.go @@ -22,7 +22,7 @@ import ( func execShowDesignProperties(ctx *ExecContext, s *ast.ShowDesignPropertiesStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -115,7 +115,7 @@ func printOneProperty(ctx *ExecContext, p ThemeProperty) { func execDescribeStyling(ctx *ExecContext, s *ast.DescribeStylingStmt) error { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -257,12 +257,11 @@ func collectStyledWidgets(widgets []rawWidget, widgetName string) []rawWidget { // ============================================================================ func execAlterStyling(ctx *ExecContext, s *ast.AlterStylingStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_widgets.go b/mdl/executor/cmd_widgets.go index 0710af9c..42f4c61e 100644 --- a/mdl/executor/cmd_widgets.go +++ b/mdl/executor/cmd_widgets.go @@ -16,9 +16,8 @@ import ( // execShowWidgets handles the SHOW WIDGETS statement. func execShowWidgets(ctx *ExecContext, s *ast.ShowWidgetsStmt) error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -81,12 +80,10 @@ func execShowWidgets(ctx *ExecContext, s *ast.ShowWidgetsStmt) error { // execUpdateWidgets handles the UPDATE WIDGETS statement. func execUpdateWidgets(ctx *ExecContext, s *ast.UpdateWidgetsStmt) error { - e := ctx.executor - - if e.reader == nil { + if !ctx.Connected() { return mdlerrors.NewNotConnected() } - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/cmd_workflows_write.go b/mdl/executor/cmd_workflows_write.go index c0015e04..63994291 100644 --- a/mdl/executor/cmd_workflows_write.go +++ b/mdl/executor/cmd_workflows_write.go @@ -18,7 +18,7 @@ import ( // execCreateWorkflow handles CREATE WORKFLOW statements. func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -131,7 +131,7 @@ func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { // execDropWorkflow handles DROP WORKFLOW statements. func execDropWorkflow(ctx *ExecContext, s *ast.DropWorkflowStmt) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } diff --git a/mdl/executor/executor_connect.go b/mdl/executor/executor_connect.go index ead11057..a219b570 100644 --- a/mdl/executor/executor_connect.go +++ b/mdl/executor/executor_connect.go @@ -13,7 +13,7 @@ import ( func execConnect(ctx *ExecContext, s *ast.ConnectStmt) error { e := ctx.executor - if e.writer != nil { + if ctx.ConnectedForWrite() { e.writer.Close() } @@ -50,7 +50,7 @@ func reconnect(ctx *ExecContext) error { } // Close existing connection - if e.writer != nil { + if ctx.ConnectedForWrite() { e.writer.Close() } @@ -69,7 +69,7 @@ func reconnect(ctx *ExecContext) error { func execDisconnect(ctx *ExecContext) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { fmt.Fprintln(ctx.Output, "Not connected") return nil } @@ -94,7 +94,7 @@ func execDisconnect(ctx *ExecContext) error { func execStatus(ctx *ExecContext) error { e := ctx.executor - if e.writer == nil { + if !ctx.ConnectedForWrite() { fmt.Fprintln(ctx.Output, "Status: Not connected") return nil } diff --git a/mdl/executor/executor_query.go b/mdl/executor/executor_query.go index 705d38a5..6964ee6d 100644 --- a/mdl/executor/executor_query.go +++ b/mdl/executor/executor_query.go @@ -8,8 +8,7 @@ import ( ) func execShow(ctx *ExecContext, s *ast.ShowStmt) error { - e := ctx.executor - if e.reader == nil && s.ObjectType != ast.ShowModules && s.ObjectType != ast.ShowFragments { + if !ctx.Connected() && s.ObjectType != ast.ShowModules && s.ObjectType != ast.ShowFragments { return mdlerrors.NewNotConnected() } @@ -144,8 +143,7 @@ func execShow(ctx *ExecContext, s *ast.ShowStmt) error { } func execDescribe(ctx *ExecContext, s *ast.DescribeStmt) error { - e := ctx.executor - if e.reader == nil && s.ObjectType != ast.DescribeFragment { + if !ctx.Connected() && s.ObjectType != ast.DescribeFragment { return mdlerrors.NewNotConnected() } diff --git a/mdl/executor/helpers.go b/mdl/executor/helpers.go index e1465111..9f6ba397 100644 --- a/mdl/executor/helpers.go +++ b/mdl/executor/helpers.go @@ -68,12 +68,11 @@ func findModule(ctx *ExecContext, name string) (*model.Module, error) { // and the executor has write access. Used by CREATE operations to avoid requiring // manual module creation. func findOrCreateModule(ctx *ExecContext, name string) (*model.Module, error) { - e := ctx.executor m, err := findModule(ctx, name) if err == nil { return m, nil } - if e.writer == nil || name == "" { + if !ctx.ConnectedForWrite() || name == "" { return nil, err } // Auto-create the module @@ -178,7 +177,7 @@ func createFolder(ctx *ExecContext, name string, containerID model.ID) (model.ID // enumerationExists checks if an enumeration exists in the project. func enumerationExists(ctx *ExecContext, qualifiedName string) bool { e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return false } @@ -217,8 +216,7 @@ func enumerationExists(ctx *ExecContext, qualifiedName string) bool { // It checks DataSource (microflow/nanoflow/entity), Action (page/microflow/nanoflow), // and Snippet references. func validateWidgetReferences(ctx *ExecContext, widgets []*ast.WidgetV3, sc *scriptContext) []string { - e := ctx.executor - if e.reader == nil || len(widgets) == 0 { + if !ctx.Connected() || len(widgets) == 0 { return nil } diff --git a/mdl/executor/hierarchy.go b/mdl/executor/hierarchy.go index cde6bee3..628febfb 100644 --- a/mdl/executor/hierarchy.go +++ b/mdl/executor/hierarchy.go @@ -114,7 +114,7 @@ func (h *ContainerHierarchy) GetQualifiedName(containerID model.ID, name string) func getHierarchy(ctx *ExecContext) (*ContainerHierarchy, error) { e := ctx.executor // Ensure cache exists - if e.reader == nil { + if !ctx.Connected() { return nil, nil } if ctx.Cache == nil { diff --git a/mdl/executor/validate.go b/mdl/executor/validate.go index 11591068..9bd82e06 100644 --- a/mdl/executor/validate.go +++ b/mdl/executor/validate.go @@ -154,8 +154,7 @@ func (sc *scriptContext) has(name string) bool { // validateProgram validates all statements in a program, skipping references // to objects that are defined within the script itself. func validateProgram(ctx *ExecContext, prog *ast.Program) []error { - e := ctx.executor - if e.reader == nil { + if !ctx.Connected() { return []error{mdlerrors.NewNotConnected()} } @@ -396,8 +395,7 @@ func (e *Executor) Validate(stmt ast.Statement) error { // validateMicroflowReferences validates that all qualified name references in a // microflow body (pages, microflows, java actions, entities) point to existing objects. func validateMicroflowReferences(ctx *ExecContext, s *ast.CreateMicroflowStmt, sc *scriptContext) []string { - e := ctx.executor - if e.reader == nil || len(s.Body) == 0 { + if !ctx.Connected() || len(s.Body) == 0 { return nil } From 643a262acd5e8f59bb306b173dd4ce66b061ca2c Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 19:45:17 +0200 Subject: [PATCH 3/6] refactor: replace e.reader/e.writer with ctx.Backend and e.field with ctx.Field in free functions --- mdl/executor/autocomplete.go | 45 ++---- mdl/executor/cmd_agenteditor_agents.go | 6 +- mdl/executor/cmd_agenteditor_kbs.go | 6 +- mdl/executor/cmd_agenteditor_mcpservices.go | 6 +- mdl/executor/cmd_agenteditor_models.go | 6 +- mdl/executor/cmd_alter_page.go | 9 +- mdl/executor/cmd_alter_workflow.go | 6 +- mdl/executor/cmd_associations.go | 37 ++--- mdl/executor/cmd_businessevents.go | 26 +-- mdl/executor/cmd_catalog.go | 28 ++-- mdl/executor/cmd_constants.go | 28 ++-- mdl/executor/cmd_contract.go | 25 ++- mdl/executor/cmd_datatransformer.go | 22 +-- mdl/executor/cmd_dbconnection.go | 20 +-- mdl/executor/cmd_diff.go | 18 +-- mdl/executor/cmd_diff_local.go | 10 +- mdl/executor/cmd_domainmodel_elk.go | 11 +- mdl/executor/cmd_entities.go | 75 +++++---- mdl/executor/cmd_entities_access.go | 3 +- mdl/executor/cmd_entities_describe.go | 21 +-- mdl/executor/cmd_enumerations.go | 19 +-- mdl/executor/cmd_export_mappings.go | 15 +- mdl/executor/cmd_features.go | 6 +- mdl/executor/cmd_folders.go | 10 +- mdl/executor/cmd_fragments.go | 4 +- mdl/executor/cmd_imagecollections.go | 12 +- mdl/executor/cmd_import.go | 6 +- mdl/executor/cmd_import_mappings.go | 15 +- mdl/executor/cmd_javaactions.go | 24 ++- mdl/executor/cmd_javascript_actions.go | 8 +- mdl/executor/cmd_jsonstructures.go | 14 +- mdl/executor/cmd_layouts.go | 3 +- mdl/executor/cmd_lint.go | 7 +- mdl/executor/cmd_mermaid.go | 13 +- mdl/executor/cmd_microflow_elk.go | 5 +- mdl/executor/cmd_microflows_create.go | 13 +- mdl/executor/cmd_microflows_drop.go | 9 +- mdl/executor/cmd_microflows_format_action.go | 3 +- mdl/executor/cmd_microflows_show.go | 20 +-- mdl/executor/cmd_misc.go | 11 +- mdl/executor/cmd_modules.go | 129 ++++++++------- mdl/executor/cmd_move.go | 49 +++--- mdl/executor/cmd_navigation.go | 17 +- mdl/executor/cmd_odata.go | 64 +++----- mdl/executor/cmd_page_wireframe.go | 8 +- mdl/executor/cmd_pages_builder.go | 10 +- mdl/executor/cmd_pages_create_v3.go | 22 +-- mdl/executor/cmd_pages_describe.go | 25 ++- mdl/executor/cmd_pages_describe_output.go | 3 +- mdl/executor/cmd_pages_describe_parse.go | 5 +- mdl/executor/cmd_pages_show.go | 3 +- mdl/executor/cmd_published_rest.go | 31 ++-- mdl/executor/cmd_rename.go | 43 +++-- mdl/executor/cmd_rest_clients.go | 18 +-- mdl/executor/cmd_security.go | 50 +++--- mdl/executor/cmd_security_write.go | 157 +++++++++---------- mdl/executor/cmd_settings.go | 29 ++-- mdl/executor/cmd_snippets.go | 3 +- mdl/executor/cmd_structure.go | 39 +++-- mdl/executor/cmd_styling.go | 24 ++- mdl/executor/cmd_widgets.go | 10 +- mdl/executor/cmd_workflows.go | 6 +- mdl/executor/cmd_workflows_write.go | 11 +- mdl/executor/format.go | 4 +- mdl/executor/helpers.go | 30 ++-- mdl/executor/oql_type_inference.go | 3 +- 66 files changed, 577 insertions(+), 841 deletions(-) diff --git a/mdl/executor/autocomplete.go b/mdl/executor/autocomplete.go index ab5aba06..9877d1e8 100644 --- a/mdl/executor/autocomplete.go +++ b/mdl/executor/autocomplete.go @@ -8,11 +8,10 @@ import "context" // getModuleNames returns a list of all module names for autocomplete. func getModuleNames(ctx *ExecContext) []string { - e := ctx.executor if !ctx.Connected() { return nil } - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return nil } @@ -25,7 +24,6 @@ func getModuleNames(ctx *ExecContext) []string { // getMicroflowNamesAC returns qualified microflow names, optionally filtered by module. func getMicroflowNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -33,7 +31,7 @@ func getMicroflowNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return nil } @@ -50,7 +48,6 @@ func getMicroflowNamesAC(ctx *ExecContext, moduleFilter string) []string { // getEntityNamesAC returns qualified entity names, optionally filtered by module. func getEntityNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -58,7 +55,7 @@ func getEntityNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return nil } @@ -77,7 +74,6 @@ func getEntityNamesAC(ctx *ExecContext, moduleFilter string) []string { // getPageNamesAC returns qualified page names, optionally filtered by module. func getPageNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -85,7 +81,7 @@ func getPageNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return nil } @@ -102,7 +98,6 @@ func getPageNamesAC(ctx *ExecContext, moduleFilter string) []string { // getSnippetNamesAC returns qualified snippet names, optionally filtered by module. func getSnippetNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -110,7 +105,7 @@ func getSnippetNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err != nil { return nil } @@ -127,7 +122,6 @@ func getSnippetNamesAC(ctx *ExecContext, moduleFilter string) []string { // getAssociationNamesAC returns qualified association names, optionally filtered by module. func getAssociationNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -135,7 +129,7 @@ func getAssociationNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return nil } @@ -154,7 +148,6 @@ func getAssociationNamesAC(ctx *ExecContext, moduleFilter string) []string { // getEnumerationNamesAC returns qualified enumeration names, optionally filtered by module. func getEnumerationNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -162,7 +155,7 @@ func getEnumerationNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return nil } @@ -179,7 +172,6 @@ func getEnumerationNamesAC(ctx *ExecContext, moduleFilter string) []string { // getLayoutNamesAC returns qualified layout names, optionally filtered by module. func getLayoutNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -187,7 +179,7 @@ func getLayoutNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - layouts, err := e.reader.ListLayouts() + layouts, err := ctx.Backend.ListLayouts() if err != nil { return nil } @@ -204,7 +196,6 @@ func getLayoutNamesAC(ctx *ExecContext, moduleFilter string) []string { // getJavaActionNamesAC returns qualified Java action names, optionally filtered by module. func getJavaActionNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -212,7 +203,7 @@ func getJavaActionNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - actions, err := e.reader.ListJavaActions() + actions, err := ctx.Backend.ListJavaActions() if err != nil { return nil } @@ -229,7 +220,6 @@ func getJavaActionNamesAC(ctx *ExecContext, moduleFilter string) []string { // getODataClientNamesAC returns qualified consumed OData service names, optionally filtered by module. func getODataClientNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -237,7 +227,7 @@ func getODataClientNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return nil } @@ -254,7 +244,6 @@ func getODataClientNamesAC(ctx *ExecContext, moduleFilter string) []string { // getODataServiceNamesAC returns qualified published OData service names, optionally filtered by module. func getODataServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -262,7 +251,7 @@ func getODataServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return nil } @@ -279,7 +268,6 @@ func getODataServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { // getRestClientNamesAC returns qualified consumed REST service names, optionally filtered by module. func getRestClientNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -287,7 +275,7 @@ func getRestClientNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - services, err := e.reader.ListConsumedRestServices() + services, err := ctx.Backend.ListConsumedRestServices() if err != nil { return nil } @@ -304,7 +292,6 @@ func getRestClientNamesAC(ctx *ExecContext, moduleFilter string) []string { // getDatabaseConnectionNamesAC returns qualified database connection names, optionally filtered by module. func getDatabaseConnectionNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -312,7 +299,7 @@ func getDatabaseConnectionNamesAC(ctx *ExecContext, moduleFilter string) []strin if err != nil { return nil } - connections, err := e.reader.ListDatabaseConnections() + connections, err := ctx.Backend.ListDatabaseConnections() if err != nil { return nil } @@ -329,7 +316,6 @@ func getDatabaseConnectionNamesAC(ctx *ExecContext, moduleFilter string) []strin // getBusinessEventServiceNamesAC returns qualified business event service names, optionally filtered by module. func getBusinessEventServiceNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -337,7 +323,7 @@ func getBusinessEventServiceNamesAC(ctx *ExecContext, moduleFilter string) []str if err != nil { return nil } - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return nil } @@ -354,7 +340,6 @@ func getBusinessEventServiceNamesAC(ctx *ExecContext, moduleFilter string) []str // getJsonStructureNamesAC returns qualified JSON structure names, optionally filtered by module. func getJsonStructureNamesAC(ctx *ExecContext, moduleFilter string) []string { - e := ctx.executor if !ctx.Connected() { return nil } @@ -362,7 +347,7 @@ func getJsonStructureNamesAC(ctx *ExecContext, moduleFilter string) []string { if err != nil { return nil } - structures, err := e.reader.ListJsonStructures() + structures, err := ctx.Backend.ListJsonStructures() if err != nil { return nil } diff --git a/mdl/executor/cmd_agenteditor_agents.go b/mdl/executor/cmd_agenteditor_agents.go index f8172874..9851ed88 100644 --- a/mdl/executor/cmd_agenteditor_agents.go +++ b/mdl/executor/cmd_agenteditor_agents.go @@ -18,12 +18,11 @@ import ( // showAgentEditorAgents handles SHOW AGENTS [IN module]. func showAgentEditorAgents(ctx *ExecContext, moduleName string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - agents, err := e.reader.ListAgentEditorAgents() + agents, err := ctx.Backend.ListAgentEditorAgents() if err != nil { return mdlerrors.NewBackend("list agents", err) } @@ -231,8 +230,7 @@ func emitKBBlock(ctx *ExecContext, kb agenteditor.AgentKBTool) { // findAgentEditorAgent looks up an agent by module and name. func findAgentEditorAgent(ctx *ExecContext, moduleName, agentName string) *agenteditor.Agent { - e := ctx.executor - agents, err := e.reader.ListAgentEditorAgents() + agents, err := ctx.Backend.ListAgentEditorAgents() if err != nil { return nil } diff --git a/mdl/executor/cmd_agenteditor_kbs.go b/mdl/executor/cmd_agenteditor_kbs.go index 78e6047c..f6c64001 100644 --- a/mdl/executor/cmd_agenteditor_kbs.go +++ b/mdl/executor/cmd_agenteditor_kbs.go @@ -18,12 +18,11 @@ import ( // showAgentEditorKnowledgeBases handles SHOW KNOWLEDGE BASES [IN module]. func showAgentEditorKnowledgeBases(ctx *ExecContext, moduleName string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - kbs, err := e.reader.ListAgentEditorKnowledgeBases() + kbs, err := ctx.Backend.ListAgentEditorKnowledgeBases() if err != nil { return mdlerrors.NewBackend("list knowledge bases", err) } @@ -127,8 +126,7 @@ func describeAgentEditorKnowledgeBase(ctx *ExecContext, name ast.QualifiedName) // findAgentEditorKnowledgeBase looks up a KB by module and name. func findAgentEditorKnowledgeBase(ctx *ExecContext, moduleName, kbName string) *agenteditor.KnowledgeBase { - e := ctx.executor - kbs, err := e.reader.ListAgentEditorKnowledgeBases() + kbs, err := ctx.Backend.ListAgentEditorKnowledgeBases() if err != nil { return nil } diff --git a/mdl/executor/cmd_agenteditor_mcpservices.go b/mdl/executor/cmd_agenteditor_mcpservices.go index 5c1ed439..4ee0c3af 100644 --- a/mdl/executor/cmd_agenteditor_mcpservices.go +++ b/mdl/executor/cmd_agenteditor_mcpservices.go @@ -18,12 +18,11 @@ import ( // showAgentEditorConsumedMCPServices handles SHOW CONSUMED MCP SERVICES [IN module]. func showAgentEditorConsumedMCPServices(ctx *ExecContext, moduleName string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - svcs, err := e.reader.ListAgentEditorConsumedMCPServices() + svcs, err := ctx.Backend.ListAgentEditorConsumedMCPServices() if err != nil { return mdlerrors.NewBackend("list consumed MCP services", err) } @@ -111,8 +110,7 @@ func describeAgentEditorConsumedMCPService(ctx *ExecContext, name ast.QualifiedN // findAgentEditorConsumedMCPService looks up an MCP service by module and name. func findAgentEditorConsumedMCPService(ctx *ExecContext, moduleName, svcName string) *agenteditor.ConsumedMCPService { - e := ctx.executor - svcs, err := e.reader.ListAgentEditorConsumedMCPServices() + svcs, err := ctx.Backend.ListAgentEditorConsumedMCPServices() if err != nil { return nil } diff --git a/mdl/executor/cmd_agenteditor_models.go b/mdl/executor/cmd_agenteditor_models.go index 18b6ccb6..8aec7e84 100644 --- a/mdl/executor/cmd_agenteditor_models.go +++ b/mdl/executor/cmd_agenteditor_models.go @@ -18,12 +18,11 @@ import ( // showAgentEditorModels handles SHOW MODELS [IN module]. func showAgentEditorModels(ctx *ExecContext, moduleName string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - models, err := e.reader.ListAgentEditorModels() + models, err := ctx.Backend.ListAgentEditorModels() if err != nil { return mdlerrors.NewBackend("list models", err) } @@ -133,8 +132,7 @@ func describeAgentEditorModel(ctx *ExecContext, name ast.QualifiedName) error { // findAgentEditorModel looks up a model by module and name. func findAgentEditorModel(ctx *ExecContext, moduleName, modelName string) *agenteditor.Model { - e := ctx.executor - models, err := e.reader.ListAgentEditorModels() + models, err := ctx.Backend.ListAgentEditorModels() if err != nil { return nil } diff --git a/mdl/executor/cmd_alter_page.go b/mdl/executor/cmd_alter_page.go index 19fb6aea..26ed1245 100644 --- a/mdl/executor/cmd_alter_page.go +++ b/mdl/executor/cmd_alter_page.go @@ -17,7 +17,6 @@ import ( // execAlterPage handles ALTER PAGE/SNIPPET Module.Name { operations }. func execAlterPage(ctx *ExecContext, s *ast.AlterPageStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -55,7 +54,7 @@ func execAlterPage(ctx *ExecContext, s *ast.AlterPageStmt) error { // Load raw BSON as ordered document (bson.D preserves field ordering, // which is required by Mendix Studio Pro). - rawBytes, err := e.reader.GetRawUnitBytes(unitID) + rawBytes, err := ctx.Backend.GetRawUnitBytes(unitID) if err != nil { return mdlerrors.NewBackend("load raw "+strings.ToLower(containerType)+" data", err) } @@ -118,7 +117,7 @@ func execAlterPage(ctx *ExecContext, s *ast.AlterPageStmt) error { } // Save - if err := e.writer.UpdateRawUnit(string(unitID), outBytes); err != nil { + if err := ctx.Backend.UpdateRawUnit(string(unitID), outBytes); err != nil { return mdlerrors.NewBackend("save modified "+strings.ToLower(containerType), err) } @@ -1547,8 +1546,8 @@ func buildWidgetsBson(ctx *ExecContext, widgets []*ast.WidgetV3, moduleName stri widgetScope: widgetScope, paramScope: paramScope, paramEntityNames: paramEntityNames, - execCache: e.cache, - fragments: e.fragments, + execCache: ctx.Cache, + fragments: ctx.Fragments, themeRegistry: e.getThemeRegistry(), } diff --git a/mdl/executor/cmd_alter_workflow.go b/mdl/executor/cmd_alter_workflow.go index 29340ec9..872a6180 100644 --- a/mdl/executor/cmd_alter_workflow.go +++ b/mdl/executor/cmd_alter_workflow.go @@ -42,7 +42,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { } // Find workflow by qualified name - allWorkflows, err := e.reader.ListWorkflows() + allWorkflows, err := ctx.Backend.ListWorkflows() if err != nil { return mdlerrors.NewBackend("list workflows", err) } @@ -61,7 +61,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { } // Load raw BSON as ordered document - rawBytes, err := e.reader.GetRawUnitBytes(wfID) + rawBytes, err := ctx.Backend.GetRawUnitBytes(wfID) if err != nil { return mdlerrors.NewBackend("load raw workflow data", err) } @@ -137,7 +137,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { } // Save - if err := e.writer.UpdateRawUnit(string(wfID), outBytes); err != nil { + if err := ctx.Backend.UpdateRawUnit(string(wfID), outBytes); err != nil { return mdlerrors.NewBackend("save modified workflow", err) } diff --git a/mdl/executor/cmd_associations.go b/mdl/executor/cmd_associations.go index 80f567b8..c0de68f4 100644 --- a/mdl/executor/cmd_associations.go +++ b/mdl/executor/cmd_associations.go @@ -27,7 +27,7 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -106,7 +106,7 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error Type: deleteBehavior, }, } - if err := e.writer.CreateCrossAssociation(dm.ID, ca); err != nil { + if err := ctx.Backend.CreateCrossAssociation(dm.ID, ca); err != nil { return mdlerrors.NewBackend("create cross-module association", err) } } else { @@ -121,7 +121,7 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error Type: deleteBehavior, }, } - if err := e.writer.CreateAssociation(dm.ID, assoc); err != nil { + if err := ctx.Backend.CreateAssociation(dm.ID, assoc); err != nil { return mdlerrors.NewBackend("create association", err) } } @@ -132,8 +132,8 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error // Reconcile MemberAccesses immediately — existing access rules on entities // in this DM need MemberAccess entries for the new association (CE0066). - if freshDM, err := e.reader.GetDomainModel(module.ID); err == nil { - if count, err := e.writer.ReconcileMemberAccesses(freshDM.ID, module.Name); err == nil && count > 0 { + if freshDM, err := ctx.Backend.GetDomainModel(module.ID); err == nil { + if count, err := ctx.Backend.ReconcileMemberAccesses(freshDM.ID, module.Name); err == nil && count > 0 { fmt.Fprintf(ctx.Output, "Reconciled %d access rule(s) for new association\n", count) } } @@ -145,7 +145,6 @@ func execCreateAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) error // execAlterAssociation handles ALTER ASSOCIATION statements. func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -155,7 +154,7 @@ func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -175,7 +174,7 @@ func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { case ast.AlterAssociationSetComment: assoc.Documentation = s.Comment } - if err := e.writer.UpdateDomainModel(dm); err != nil { + if err := ctx.Backend.UpdateDomainModel(dm); err != nil { return mdlerrors.NewBackend("update association", err) } fmt.Fprintf(ctx.Output, "Altered association: %s\n", s.Name) @@ -198,7 +197,7 @@ func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { case ast.AlterAssociationSetComment: ca.Documentation = s.Comment } - if err := e.writer.UpdateDomainModel(dm); err != nil { + if err := ctx.Backend.UpdateDomainModel(dm); err != nil { return mdlerrors.NewBackend("update cross-module association", err) } fmt.Fprintf(ctx.Output, "Altered association: %s\n", s.Name) @@ -211,7 +210,6 @@ func execAlterAssociation(ctx *ExecContext, s *ast.AlterAssociationStmt) error { // execDropAssociation handles DROP ASSOCIATION statements. func execDropAssociation(ctx *ExecContext, s *ast.DropAssociationStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -222,14 +220,14 @@ func execDropAssociation(ctx *ExecContext, s *ast.DropAssociationStmt) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } for _, assoc := range dm.Associations { if assoc.Name == s.Name.Name { - if err := e.writer.DeleteAssociation(dm.ID, assoc.ID); err != nil { + if err := ctx.Backend.DeleteAssociation(dm.ID, assoc.ID); err != nil { return mdlerrors.NewBackend("delete association", err) } fmt.Fprintf(ctx.Output, "Dropped association: %s\n", s.Name) @@ -238,7 +236,7 @@ func execDropAssociation(ctx *ExecContext, s *ast.DropAssociationStmt) error { } for _, ca := range dm.CrossAssociations { if ca.Name == s.Name.Name { - if err := e.writer.DeleteCrossAssociation(dm.ID, ca.ID); err != nil { + if err := ctx.Backend.DeleteCrossAssociation(dm.ID, ca.ID); err != nil { return mdlerrors.NewBackend("delete cross-module association", err) } fmt.Fprintf(ctx.Output, "Dropped cross-module association: %s\n", s.Name) @@ -251,9 +249,8 @@ func execDropAssociation(ctx *ExecContext, s *ast.DropAssociationStmt) error { // showAssociations handles SHOW ASSOCIATIONS command. func showAssociations(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Build module ID -> name map (single query) - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("list modules", err) } @@ -263,7 +260,7 @@ func showAssociations(ctx *ExecContext, moduleName string) error { } // Get all domain models in a single query (avoids O(n²) behavior) - domainModels, err := e.reader.ListDomainModels() + domainModels, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } @@ -338,7 +335,6 @@ func showAssociations(ctx *ExecContext, moduleName string) error { // showAssociation handles SHOW ASSOCIATION command. func showAssociation(ctx *ExecContext, name *ast.QualifiedName) error { - e := ctx.executor if name == nil { return mdlerrors.NewValidation("association name required") } @@ -348,7 +344,7 @@ func showAssociation(ctx *ExecContext, name *ast.QualifiedName) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -378,20 +374,19 @@ func showAssociation(ctx *ExecContext, name *ast.QualifiedName) error { // describeAssociation handles DESCRIBE ASSOCIATION command. func describeAssociation(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor module, err := findModule(ctx, name.Module) if err != nil { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } // Build entity ID -> qualified name map across all modules entityNames := make(map[model.ID]string) - allDomainModels, err := e.reader.ListDomainModels() + allDomainModels, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } diff --git a/mdl/executor/cmd_businessevents.go b/mdl/executor/cmd_businessevents.go index 2ba10c93..294c8908 100644 --- a/mdl/executor/cmd_businessevents.go +++ b/mdl/executor/cmd_businessevents.go @@ -14,13 +14,11 @@ import ( // showBusinessEventServices displays a table of all business event service documents. func showBusinessEventServices(ctx *ExecContext, inModule string) error { - e := ctx.executor - if !ctx.Connected() { return mdlerrors.NewNotConnected() } - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return mdlerrors.NewBackend("list business event services", err) } @@ -96,13 +94,11 @@ func showBusinessEventClients(ctx *ExecContext, inModule string) error { // showBusinessEvents displays a table of individual messages across all business event services. func showBusinessEvents(ctx *ExecContext, inModule string) error { - e := ctx.executor - if !ctx.Connected() { return mdlerrors.NewNotConnected() } - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return mdlerrors.NewBackend("list business event services", err) } @@ -175,13 +171,11 @@ func showBusinessEvents(ctx *ExecContext, inModule string) error { // describeBusinessEventService outputs the full MDL description of a business event service. func describeBusinessEventService(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - if !ctx.Connected() { return mdlerrors.NewNotConnected() } - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return mdlerrors.NewBackend("list business event services", err) } @@ -267,8 +261,6 @@ func describeBusinessEventService(ctx *ExecContext, name ast.QualifiedName) erro // createBusinessEventService creates a new business event service from an AST statement. func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventServiceStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -280,7 +272,7 @@ func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventS } // Check for existing service with same name (if not CREATE OR REPLACE) - existingServices, _ := e.reader.ListBusinessEventServices() + existingServices, _ := ctx.Backend.ListBusinessEventServices() h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) @@ -292,7 +284,7 @@ func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventS if strings.EqualFold(existModName, moduleName) && strings.EqualFold(existing.Name, stmt.Name.Name) { if stmt.CreateOrReplace { // Delete existing - if err := e.writer.DeleteBusinessEventService(existing.ID); err != nil { + if err := ctx.Backend.DeleteBusinessEventService(existing.ID); err != nil { return mdlerrors.NewBackend("delete existing service", err) } } else { @@ -373,7 +365,7 @@ func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventS svc.Definition = def // Write to project - if err := e.writer.CreateBusinessEventService(svc); err != nil { + if err := ctx.Backend.CreateBusinessEventService(svc); err != nil { return mdlerrors.NewBackend("create business event service", err) } @@ -383,13 +375,11 @@ func createBusinessEventService(ctx *ExecContext, stmt *ast.CreateBusinessEventS // dropBusinessEventService deletes a business event service. func dropBusinessEventService(ctx *ExecContext, stmt *ast.DropBusinessEventServiceStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return mdlerrors.NewBackend("list business event services", err) } @@ -403,7 +393,7 @@ func dropBusinessEventService(ctx *ExecContext, stmt *ast.DropBusinessEventServi modID := h.FindModuleID(svc.ContainerID) moduleName := h.GetModuleName(modID) if strings.EqualFold(moduleName, stmt.Name.Module) && strings.EqualFold(svc.Name, stmt.Name.Name) { - if err := e.writer.DeleteBusinessEventService(svc.ID); err != nil { + if err := ctx.Backend.DeleteBusinessEventService(svc.ID); err != nil { return mdlerrors.NewBackend("delete business event service", err) } fmt.Fprintf(ctx.Output, "Dropped business event service: %s.%s\n", moduleName, svc.Name) diff --git a/mdl/executor/cmd_catalog.go b/mdl/executor/cmd_catalog.go index 2da8d52e..f6e71f09 100644 --- a/mdl/executor/cmd_catalog.go +++ b/mdl/executor/cmd_catalog.go @@ -20,7 +20,6 @@ import ( // execShowCatalogTables handles SHOW CATALOG TABLES. func execShowCatalogTables(ctx *ExecContext) error { - e := ctx.executor // Build catalog if not already built (fast mode by default) if ctx.Catalog == nil || !ctx.Catalog.IsBuilt() { if err := ensureCatalog(ctx, false); err != nil { @@ -57,7 +56,6 @@ func execShowCatalogTables(ctx *ExecContext) error { for _, info := range infos { tr.Rows = append(tr.Rows, []any{info.name, info.count}) } - _ = e // suppress unused return writeResult(ctx, tr) } @@ -199,7 +197,6 @@ func execDescribeCatalogTable(ctx *ExecContext, stmt *ast.DescribeCatalogTableSt // ensureCatalog ensures a catalog is available, using cache if possible. func ensureCatalog(ctx *ExecContext, full bool) error { - e := ctx.executor requiredMode := "fast" if full { requiredMode = "full" @@ -215,28 +212,25 @@ func ensureCatalog(ctx *ExecContext, full bool) error { } // Build fresh catalog - _ = e // suppress unused return buildCatalog(ctx, full) } // getCachePath returns the path to the catalog cache file for the current project. func getCachePath(ctx *ExecContext) string { - e := ctx.executor - if e.mprPath == "" { + if ctx.MprPath == "" { return "" } - dir := filepath.Dir(e.mprPath) + dir := filepath.Dir(ctx.MprPath) cacheDir := filepath.Join(dir, ".mxcli") return filepath.Join(cacheDir, "catalog.db") } // getMprModTime returns the modification time of the current MPR file. func getMprModTime(ctx *ExecContext) time.Time { - e := ctx.executor - if e.mprPath == "" { + if ctx.MprPath == "" { return time.Time{} } - info, err := os.Stat(e.mprPath) + info, err := os.Stat(ctx.MprPath) if err != nil { return time.Time{} } @@ -245,7 +239,6 @@ func getMprModTime(ctx *ExecContext) time.Time { // isCacheValid checks if the cached catalog is still valid. func isCacheValid(ctx *ExecContext, cachePath string, requiredMode string) (bool, string) { - e := ctx.executor // Check if cache file exists if _, err := os.Stat(cachePath); os.IsNotExist(err) { return false, "cache file does not exist" @@ -264,7 +257,7 @@ func isCacheValid(ctx *ExecContext, cachePath string, requiredMode string) (bool } // Check MPR path matches - if info.MprPath != e.mprPath { + if info.MprPath != ctx.MprPath { return false, "MPR path changed" } @@ -350,7 +343,7 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error { } // Set project metadata - version, _ := e.reader.GetMendixVersion() + version, _ := ctx.Backend.GetMendixVersion() cat.SetProject("default", "Current Project", version) // Build catalog @@ -380,7 +373,7 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error { } else if full { buildMode = "full" } - cat.SetCacheInfo(e.mprPath, getMprModTime(ctx), version, buildMode, elapsed) + cat.SetCacheInfo(ctx.MprPath, getMprModTime(ctx), version, buildMode, elapsed) cat.SetBuilt(true) ctx.Catalog = cat @@ -734,7 +727,6 @@ func captureDescribeParallel(ctx *ExecContext, objectType string, qualifiedName // This avoids O(n²) re-parsing in describe functions by building name lookup // maps once and sharing them across all goroutines. func preWarmCache(ctx *ExecContext) { - e := ctx.executor h, _ := getHierarchy(ctx) if h == nil || ctx.Cache == nil { return @@ -742,7 +734,7 @@ func preWarmCache(ctx *ExecContext) { // Build entity name lookup ctx.Cache.entityNames = make(map[model.ID]string) - dms, _ := e.reader.ListDomainModels() + dms, _ := ctx.Backend.ListDomainModels() for _, dm := range dms { modName := h.GetModuleName(dm.ContainerID) for _, ent := range dm.Entities { @@ -752,14 +744,14 @@ func preWarmCache(ctx *ExecContext) { // Build microflow name lookup ctx.Cache.microflowNames = make(map[model.ID]string) - mfs, _ := e.reader.ListMicroflows() + mfs, _ := ctx.Backend.ListMicroflows() for _, mf := range mfs { ctx.Cache.microflowNames[mf.ID] = h.GetQualifiedName(mf.ContainerID, mf.Name) } // Build page name lookup ctx.Cache.pageNames = make(map[model.ID]string) - pgs, _ := e.reader.ListPages() + pgs, _ := ctx.Backend.ListPages() for _, pg := range pgs { ctx.Cache.pageNames[pg.ID] = h.GetQualifiedName(pg.ContainerID, pg.Name) } diff --git a/mdl/executor/cmd_constants.go b/mdl/executor/cmd_constants.go index 3c08abef..e1f6be3f 100644 --- a/mdl/executor/cmd_constants.go +++ b/mdl/executor/cmd_constants.go @@ -15,9 +15,7 @@ import ( // showConstants handles SHOW CONSTANTS command. func showConstants(ctx *ExecContext, moduleName string) error { - e := ctx.executor - - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return mdlerrors.NewBackend("list constants", err) } @@ -84,9 +82,7 @@ func showConstants(ctx *ExecContext, moduleName string) error { // describeConstant handles DESCRIBE CONSTANT command. func describeConstant(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return mdlerrors.NewBackend("list constants", err) } @@ -258,8 +254,6 @@ func formatDefaultValue(dt model.ConstantDataType, value string) string { // createConstant handles CREATE CONSTANT command. func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -285,7 +279,7 @@ func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { } // Check if constant already exists in this module - existingConstants, err := e.reader.ListConstants() + existingConstants, err := ctx.Backend.ListConstants() if err == nil { h, _ := getHierarchy(ctx) for _, c := range existingConstants { @@ -302,7 +296,7 @@ func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { c.Type = constType c.DefaultValue = defaultValue c.ExposedToClient = stmt.ExposedToClient - if err := e.writer.UpdateConstant(c); err != nil { + if err := ctx.Backend.UpdateConstant(c); err != nil { return mdlerrors.NewBackend("update constant", err) } invalidateHierarchy(ctx) @@ -338,7 +332,7 @@ func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { ExposedToClient: stmt.ExposedToClient, } - if err := e.writer.CreateConstant(constant); err != nil { + if err := ctx.Backend.CreateConstant(constant); err != nil { return mdlerrors.NewBackend("create constant", err) } invalidateHierarchy(ctx) @@ -349,14 +343,12 @@ func createConstant(ctx *ExecContext, stmt *ast.CreateConstantStmt) error { // showConstantValues handles SHOW CONSTANT VALUES command. // Displays one row per constant per configuration for easy comparison. func showConstantValues(ctx *ExecContext, moduleName string) error { - e := ctx.executor - - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return mdlerrors.NewBackend("list constants", err) } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -445,13 +437,11 @@ func showConstantValues(ctx *ExecContext, moduleName string) error { // dropConstant handles DROP CONSTANT command. func dropConstant(ctx *ExecContext, stmt *ast.DropConstantStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return mdlerrors.NewBackend("list constants", err) } @@ -467,7 +457,7 @@ func dropConstant(ctx *ExecContext, stmt *ast.DropConstantStmt) error { modID := h.FindModuleID(c.ContainerID) modName := h.GetModuleName(modID) if strings.EqualFold(modName, stmt.Name.Module) && strings.EqualFold(c.Name, stmt.Name.Name) { - if err := e.writer.DeleteConstant(c.ID); err != nil { + if err := ctx.Backend.DeleteConstant(c.ID); err != nil { return mdlerrors.NewBackend("drop constant", err) } invalidateHierarchy(ctx) diff --git a/mdl/executor/cmd_contract.go b/mdl/executor/cmd_contract.go index 3fc0df8b..e8185c49 100644 --- a/mdl/executor/cmd_contract.go +++ b/mdl/executor/cmd_contract.go @@ -316,8 +316,7 @@ func outputContractEntityMDL(ctx *ExecContext, et *mpr.EdmEntityType, svcQN stri // parseServiceContract finds a consumed OData service by name and parses its cached $metadata. func parseServiceContract(ctx *ExecContext, name ast.QualifiedName) (*mpr.EdmxDocument, string, error) { - e := ctx.executor - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return nil, "", mdlerrors.NewBackend("list consumed OData services", err) } @@ -447,7 +446,6 @@ var reservedEntityAttrNames = map[string]bool{ // populating Source, Key, and per-attribute RemoteName/RemoteType fields so the resulting BSON matches // what Studio Pro produces. func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -481,7 +479,7 @@ func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) if err != nil { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -634,7 +632,7 @@ func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) continue } applyExternalEntityFields(existingEntity, et, isTopLevel, serviceRef, entitySet, keyParts, attrs) - if err := e.writer.UpdateEntity(dm.ID, existingEntity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, existingEntity); err != nil { fmt.Fprintf(ctx.Output, " FAILED: %s.%s — %v\n", targetModule, mendixName, err) failed++ continue @@ -650,7 +648,7 @@ func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) } newEntity.ID = model.ID(mpr.GenerateID()) applyExternalEntityFields(newEntity, et, isTopLevel, serviceRef, entitySet, keyParts, attrs) - if err := e.writer.CreateEntity(dm.ID, newEntity); err != nil { + if err := ctx.Backend.CreateEntity(dm.ID, newEntity); err != nil { fmt.Fprintf(ctx.Output, " FAILED: %s.%s — %v\n", targetModule, mendixName, err) failed++ continue @@ -662,7 +660,7 @@ func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) // Second pass: create primitive-collection NPEs (e.g. TripTag for // Trip.Tags = Collection(Edm.String)) and the association from the // parent entity to each NPE. - dm, err = e.reader.GetDomainModel(module.ID) + dm, err = ctx.Backend.GetDomainModel(module.ID) if err == nil { npesCreated := createPrimitiveCollectionNPEs(ctx, dm, doc, typeByQualified, esMap, serviceRef) if npesCreated > 0 { @@ -673,7 +671,7 @@ func createExternalEntities(ctx *ExecContext, s *ast.CreateExternalEntitiesStmt) // Third pass: walk navigation properties and create associations between // the entities we just created. Re-read the domain model so the NPEs // from the previous pass are visible. - dm, err = e.reader.GetDomainModel(module.ID) + dm, err = ctx.Backend.GetDomainModel(module.ID) if err == nil { assocsCreated := createNavigationAssociations(ctx, dm, doc, typeByQualified, esMap, serviceRef) if assocsCreated > 0 { @@ -706,7 +704,6 @@ func createPrimitiveCollectionNPEs( esMap map[string]string, serviceRef string, ) int { - e := ctx.executor // Lookup parent Mendix entity by EDMX type qualified name. parentByQN := make(map[string]*domainmodel.Entity) for qn, et := range typeByQualified { @@ -788,7 +785,7 @@ func createPrimitiveCollectionNPEs( } npe.ID = model.ID(mpr.GenerateID()) - if err := e.writer.CreateEntity(dm.ID, npe); err != nil { + if err := ctx.Backend.CreateEntity(dm.ID, npe); err != nil { fmt.Fprintf(ctx.Output, " NPE FAILED: %s — %v\n", npeName, err) continue } @@ -809,7 +806,7 @@ func createPrimitiveCollectionNPEs( Source: "Rest$ODataPrimitiveCollectionAssociationSource", } assoc.ID = model.ID(mpr.GenerateID()) - if err := e.writer.CreateAssociation(dm.ID, assoc); err != nil { + if err := ctx.Backend.CreateAssociation(dm.ID, assoc); err != nil { fmt.Fprintf(ctx.Output, " NPE ASSOC FAILED: %s — %v\n", assocName, err) } } @@ -890,7 +887,6 @@ func createNavigationAssociations( esMap map[string]string, serviceRef string, ) int { - e := ctx.executor // Build per-entity-type lookup of nav property name → restricted flags, // plus a direct entity-set lookup so we can read the base Insertable / // Updatable defaults for the FROM entity. @@ -1033,7 +1029,7 @@ func createNavigationAssociations( } assoc.ID = model.ID(mpr.GenerateID()) - if err := e.writer.CreateAssociation(dm.ID, assoc); err != nil { + if err := ctx.Backend.CreateAssociation(dm.ID, assoc); err != nil { fmt.Fprintf(ctx.Output, " ASSOC FAILED: %s.%s — %v\n", parentEnt.Name, assocName, err) continue } @@ -1385,8 +1381,7 @@ func describeContractMessage(ctx *ExecContext, name ast.QualifiedName) error { // parseAsyncAPIContract finds a business event service by name and parses its cached AsyncAPI document. func parseAsyncAPIContract(ctx *ExecContext, name ast.QualifiedName) (*mpr.AsyncAPIDocument, string, error) { - e := ctx.executor - services, err := e.reader.ListBusinessEventServices() + services, err := ctx.Backend.ListBusinessEventServices() if err != nil { return nil, "", mdlerrors.NewBackend("list business event services", err) } diff --git a/mdl/executor/cmd_datatransformer.go b/mdl/executor/cmd_datatransformer.go index 10b81142..24df3492 100644 --- a/mdl/executor/cmd_datatransformer.go +++ b/mdl/executor/cmd_datatransformer.go @@ -13,9 +13,7 @@ import ( // listDataTransformers handles LIST DATA TRANSFORMERS [IN module]. func listDataTransformers(ctx *ExecContext, moduleName string) error { - e := ctx.executor - - transformers, err := e.reader.ListDataTransformers() + transformers, err := ctx.Backend.ListDataTransformers() if err != nil { return mdlerrors.NewBackend("list data transformers", err) } @@ -58,9 +56,7 @@ func listDataTransformers(ctx *ExecContext, moduleName string) error { // describeDataTransformer handles DESCRIBE DATA TRANSFORMER Module.Name. func describeDataTransformer(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - - transformers, err := e.reader.ListDataTransformers() + transformers, err := ctx.Backend.ListDataTransformers() if err != nil { return mdlerrors.NewBackend("list data transformers", err) } @@ -108,8 +104,6 @@ func describeDataTransformer(ctx *ExecContext, name ast.QualifiedName) error { // execCreateDataTransformer creates a new data transformer. func execCreateDataTransformer(ctx *ExecContext, s *ast.CreateDataTransformerStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -139,11 +133,11 @@ func execCreateDataTransformer(ctx *ExecContext, s *ast.CreateDataTransformerStm }) } - if err := e.writer.CreateDataTransformer(dt); err != nil { + if err := ctx.Backend.CreateDataTransformer(dt); err != nil { return mdlerrors.NewBackend("create data transformer", err) } - if !e.quiet { + if !ctx.Quiet { fmt.Fprintf(ctx.Output, "Created data transformer: %s.%s (%d steps)\n", s.Name.Module, s.Name.Name, len(dt.Steps)) } @@ -152,13 +146,11 @@ func execCreateDataTransformer(ctx *ExecContext, s *ast.CreateDataTransformerStm // execDropDataTransformer deletes a data transformer. func execDropDataTransformer(ctx *ExecContext, s *ast.DropDataTransformerStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - transformers, err := e.reader.ListDataTransformers() + transformers, err := ctx.Backend.ListDataTransformers() if err != nil { return mdlerrors.NewBackend("list data transformers", err) } @@ -172,10 +164,10 @@ func execDropDataTransformer(ctx *ExecContext, s *ast.DropDataTransformerStmt) e modID := h.FindModuleID(dt.ContainerID) modName := h.GetModuleName(modID) if modName == s.Name.Module && dt.Name == s.Name.Name { - if err := e.writer.DeleteDataTransformer(dt.ID); err != nil { + if err := ctx.Backend.DeleteDataTransformer(dt.ID); err != nil { return mdlerrors.NewBackend("drop data transformer", err) } - if !e.quiet { + if !ctx.Quiet { fmt.Fprintf(ctx.Output, "Dropped data transformer: %s.%s\n", s.Name.Module, s.Name.Name) } return nil diff --git a/mdl/executor/cmd_dbconnection.go b/mdl/executor/cmd_dbconnection.go index 8fb91d43..5d14c549 100644 --- a/mdl/executor/cmd_dbconnection.go +++ b/mdl/executor/cmd_dbconnection.go @@ -14,8 +14,6 @@ import ( // createDatabaseConnection handles CREATE DATABASE CONNECTION command. func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnectionStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -30,7 +28,7 @@ func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnecti } // Check for existing connection - existing, _ := e.reader.ListDatabaseConnections() + existing, _ := ctx.Backend.ListDatabaseConnections() h, _ := getHierarchy(ctx) for _, ex := range existing { @@ -38,7 +36,7 @@ func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnecti modName := h.GetModuleName(modID) if strings.EqualFold(modName, stmt.Name.Module) && strings.EqualFold(ex.Name, stmt.Name.Name) { if stmt.CreateOrModify { - if err := e.writer.DeleteDatabaseConnection(ex.ID); err != nil { + if err := ctx.Backend.DeleteDatabaseConnection(ex.ID); err != nil { return mdlerrors.NewBackend("delete existing connection", err) } } else { @@ -113,7 +111,7 @@ func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnecti conn.Queries = append(conn.Queries, q) } - if err := e.writer.CreateDatabaseConnection(conn); err != nil { + if err := ctx.Backend.CreateDatabaseConnection(conn); err != nil { return mdlerrors.NewBackend("create database connection", err) } @@ -124,9 +122,7 @@ func createDatabaseConnection(ctx *ExecContext, stmt *ast.CreateDatabaseConnecti // showDatabaseConnections handles SHOW DATABASE CONNECTIONS command. func showDatabaseConnections(ctx *ExecContext, moduleName string) error { - e := ctx.executor - - connections, err := e.reader.ListDatabaseConnections() + connections, err := ctx.Backend.ListDatabaseConnections() if err != nil { return mdlerrors.NewBackend("list database connections", err) } @@ -180,9 +176,7 @@ func showDatabaseConnections(ctx *ExecContext, moduleName string) error { // describeDatabaseConnection handles DESCRIBE DATABASE CONNECTION command. func describeDatabaseConnection(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - - connections, err := e.reader.ListDatabaseConnections() + connections, err := ctx.Backend.ListDatabaseConnections() if err != nil { return mdlerrors.NewBackend("list database connections", err) } @@ -278,9 +272,7 @@ func outputDatabaseConnectionMDL(ctx *ExecContext, conn *model.DatabaseConnectio // resolveConstantDefault looks up a constant by qualified name and returns its default value. func resolveConstantDefault(ctx *ExecContext, qualifiedName string) string { - e := ctx.executor - - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return "" } diff --git a/mdl/executor/cmd_diff.go b/mdl/executor/cmd_diff.go index 03c130b8..77a3644d 100644 --- a/mdl/executor/cmd_diff.go +++ b/mdl/executor/cmd_diff.go @@ -164,7 +164,6 @@ func diffStatement(ctx *ExecContext, stmt ast.Statement) (*DiffResult, error) { // diffEntity compares a CREATE ENTITY statement against the project func diffEntity(ctx *ExecContext, s *ast.CreateEntityStmt) (*DiffResult, error) { - e := ctx.executor result := &DiffResult{ ObjectType: "Entity", ObjectName: s.Name, @@ -178,7 +177,7 @@ func diffEntity(ctx *ExecContext, s *ast.CreateEntityStmt) (*DiffResult, error) return result, nil } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { result.IsNew = true return result, nil @@ -199,7 +198,6 @@ func diffEntity(ctx *ExecContext, s *ast.CreateEntityStmt) (*DiffResult, error) // diffViewEntity compares a CREATE VIEW ENTITY statement against the project func diffViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) (*DiffResult, error) { - e := ctx.executor result := &DiffResult{ ObjectType: "View Entity", ObjectName: s.Name, @@ -212,7 +210,7 @@ func diffViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) (*DiffResult, return result, nil } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { result.IsNew = true return result, nil @@ -254,7 +252,6 @@ func diffEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) (*DiffResul // diffAssociation compares a CREATE ASSOCIATION statement against the project func diffAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) (*DiffResult, error) { - e := ctx.executor result := &DiffResult{ ObjectType: "Association", ObjectName: s.Name, @@ -267,7 +264,7 @@ func diffAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) (*DiffResul return result, nil } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { result.IsNew = true return result, nil @@ -286,7 +283,6 @@ func diffAssociation(ctx *ExecContext, s *ast.CreateAssociationStmt) (*DiffResul // diffMicroflow compares a CREATE MICROFLOW statement against the project func diffMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) (*DiffResult, error) { - e := ctx.executor result := &DiffResult{ ObjectType: "Microflow", ObjectName: s.Name, @@ -300,7 +296,7 @@ func diffMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) (*DiffResult, e return result, nil } - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { result.IsNew = true return result, nil @@ -312,10 +308,10 @@ func diffMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) (*DiffResult, e if modName == s.Name.Module && mf.Name == s.Name.Name { // Capture current MDL representation var buf bytes.Buffer - oldOutput := e.output - e.output = &buf + oldOutput := ctx.Output + ctx.Output = &buf describeMicroflow(ctx, s.Name) - e.output = oldOutput + ctx.Output = oldOutput result.Current = strings.TrimSuffix(buf.String(), "\n") result.Changes = compareMicroflows(ctx, result.Current, result.Proposed) return result, nil diff --git a/mdl/executor/cmd_diff_local.go b/mdl/executor/cmd_diff_local.go index 4689e8c6..12ff4285 100644 --- a/mdl/executor/cmd_diff_local.go +++ b/mdl/executor/cmd_diff_local.go @@ -30,17 +30,16 @@ import ( // - A single ref (e.g., "HEAD", "main") — compares working tree vs ref // - A range "base..target" — compares two revisions (no working tree) func diffLocal(ctx *ExecContext, ref string, opts DiffOptions) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } // Check MPR version - if e.reader.Version() != 2 { + if ctx.Backend.Version() != 2 { return mdlerrors.NewUnsupported("diff-local only supports MPR v2 format (Mendix 10.18+)") } - contentsDir := e.reader.ContentsDir() + contentsDir := ctx.Backend.ContentsDir() if contentsDir == "" { return mdlerrors.NewValidation("mprcontents directory not found") } @@ -518,7 +517,6 @@ func splitQualifiedName(qualifiedName string) ast.QualifiedName { // receive IDs (e.g. entity references) and want to resolve them against // the working-tree model. Returns empty maps if the reader is unavailable. func buildNameLookups(ctx *ExecContext) (map[model.ID]string, map[model.ID]string) { - e := ctx.executor entityNames := make(map[model.ID]string) microflowNames := make(map[model.ID]string) if !ctx.Connected() { @@ -528,7 +526,7 @@ func buildNameLookups(ctx *ExecContext) (map[model.ID]string, map[model.ID]strin if err != nil { return entityNames, microflowNames } - if domainModels, err := e.reader.ListDomainModels(); err == nil { + if domainModels, err := ctx.Backend.ListDomainModels(); err == nil { for _, dm := range domainModels { modName := h.GetModuleName(dm.ContainerID) for _, entity := range dm.Entities { @@ -536,7 +534,7 @@ func buildNameLookups(ctx *ExecContext) (map[model.ID]string, map[model.ID]strin } } } - if microflows, err := e.reader.ListMicroflows(); err == nil { + if microflows, err := ctx.Backend.ListMicroflows(); err == nil { for _, mf := range microflows { microflowNames[mf.ID] = h.GetQualifiedName(mf.ContainerID, mf.Name) } diff --git a/mdl/executor/cmd_domainmodel_elk.go b/mdl/executor/cmd_domainmodel_elk.go index 78022f7e..d149e450 100644 --- a/mdl/executor/cmd_domainmodel_elk.go +++ b/mdl/executor/cmd_domainmodel_elk.go @@ -68,7 +68,6 @@ const ( // domainModelELK generates a JSON graph of a module's domain model for rendering with ELK.js. // If name contains a dot (e.g. "Module.Entity"), it delegates to entityFocusELK for a focused view. func domainModelELK(ctx *ExecContext, name string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -84,7 +83,7 @@ func domainModelELK(ctx *ExecContext, name string) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -157,7 +156,6 @@ func domainModelELK(ctx *ExecContext, name string) error { // entityFocusELK generates a focused ELK diagram showing only the selected entity // and entities directly connected to it via associations or generalization. func entityFocusELK(ctx *ExecContext, qualifiedName string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -173,7 +171,7 @@ func entityFocusELK(ctx *ExecContext, qualifiedName string) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -218,7 +216,7 @@ func entityFocusELK(ctx *ExecContext, qualifiedName string) error { } // Also scan all domain models for cross-module associations referencing this entity - allDMs, _ := e.reader.ListDomainModels() + allDMs, _ := ctx.Backend.ListDomainModels() for _, otherDM := range allDMs { if otherDM.ID == dm.ID { continue @@ -325,14 +323,13 @@ func entityFocusELK(ctx *ExecContext, qualifiedName string) error { // buildAllEntityNames loads all entities across all modules. // Returns ID -> "Module.Entity" map and ID -> module name map. func buildAllEntityNames(ctx *ExecContext) (map[model.ID]string, map[model.ID]string) { - e := ctx.executor allEntityNames := make(map[model.ID]string) allEntityModules := make(map[model.ID]string) h, err := getHierarchy(ctx) if err != nil { return allEntityNames, allEntityModules } - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, otherDM := range domainModels { modName := h.GetModuleName(otherDM.ContainerID) for _, entity := range otherDM.Entities { diff --git a/mdl/executor/cmd_entities.go b/mdl/executor/cmd_entities.go index 428e976c..8a231239 100644 --- a/mdl/executor/cmd_entities.go +++ b/mdl/executor/cmd_entities.go @@ -58,7 +58,7 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { } // Get domain model - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -270,7 +270,7 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { if s.CreateOrModify && existingEntity != nil { // Update existing entity entity.ID = existingEntity.ID - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("update entity", err) } // Invalidate caches so updated entity is visible @@ -279,7 +279,7 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { fmt.Fprintf(ctx.Output, "Modified entity: %s\n", s.Name) } else { // Create new entity - if err := e.writer.CreateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.CreateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("create entity", err) } // Invalidate caches so new entity is visible @@ -294,7 +294,6 @@ func execCreateEntity(ctx *ExecContext, s *ast.CreateEntityStmt) error { // execCreateViewEntity handles CREATE VIEW ENTITY statements. func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -326,7 +325,7 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { } // Get domain model - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -352,16 +351,16 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { s.Position = &ast.Position{X: existingEntity.Location.X, Y: existingEntity.Location.Y} } // Delete ViewEntitySourceDocument - if err := e.writer.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { + if err := ctx.Backend.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { return mdlerrors.NewBackend("delete existing ViewEntitySourceDocument", err) } // Delete the entity itself - if err := e.writer.DeleteEntity(dm.ID, existingEntity.ID); err != nil { + if err := ctx.Backend.DeleteEntity(dm.ID, existingEntity.ID); err != nil { return mdlerrors.NewBackend("delete existing entity for replace", err) } existingEntity = nil // Re-fetch domain model after deletion so entity count is correct for positioning - dm, err = e.reader.GetDomainModel(module.ID) + dm, err = ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model after delete", err) } @@ -382,10 +381,10 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { // Always delete any existing ViewEntitySourceDocument before creating a new one. // This prevents duplicate OQL documents from accumulating (e.g., from re-running // scripts or after a previous DROP that didn't clean up properly). - if err := e.writer.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { + if err := ctx.Backend.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { return mdlerrors.NewBackend("delete existing ViewEntitySourceDocument", err) } - _, err = e.writer.CreateViewEntitySourceDocument( + _, err = ctx.Backend.CreateViewEntitySourceDocument( module.ID, s.Name.Module, s.Name.Name, @@ -441,7 +440,7 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { // Update existing entity — preserve Source object ID to avoid CE-6770 entity.ID = existingEntity.ID entity.SourceObjectID = existingEntity.SourceObjectID - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("update view entity", err) } // Invalidate caches so updated entity is visible @@ -450,7 +449,7 @@ func execCreateViewEntity(ctx *ExecContext, s *ast.CreateViewEntityStmt) error { fmt.Fprintf(ctx.Output, "Modified view entity: %s\n", s.Name) } else { // Create new entity - if err := e.writer.CreateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.CreateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("create view entity", err) } // Invalidate caches so new entity is visible @@ -476,7 +475,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { } // Get domain model - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -503,16 +502,16 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { switch a.Type.Kind { case ast.TypeAutoOwner: entity.HasOwner = true - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) case ast.TypeAutoChangedBy: entity.HasChangedBy = true - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) case ast.TypeAutoCreatedDate: entity.HasCreatedDate = true - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) case ast.TypeAutoChangedDate: entity.HasChangedDate = true - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) } // CALCULATED attributes are only supported on persistent entities if a.Calculated && !entity.Persistable { @@ -594,7 +593,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { entity.ValidationRules = append(entity.ValidationRules, vr) } - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("add attribute", err) } invalidateHierarchy(ctx) @@ -613,7 +612,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { if !found { return mdlerrors.NewNotFoundMsg("attribute", s.AttributeName, fmt.Sprintf("attribute '%s' not found on entity %s", s.AttributeName, s.Name)) } - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("rename attribute", err) } invalidateHierarchy(ctx) @@ -650,7 +649,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { if !found { return mdlerrors.NewNotFoundMsg("attribute", s.AttributeName, fmt.Sprintf("attribute '%s' not found on entity %s", s.AttributeName, s.Name)) } - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("modify attribute", err) } invalidateHierarchy(ctx) @@ -663,22 +662,22 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { case "owner": if entity.HasOwner { entity.HasOwner = false - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) } case "changedby": if entity.HasChangedBy { entity.HasChangedBy = false - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) } case "createddate": if entity.HasCreatedDate { entity.HasCreatedDate = false - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) } case "changeddate": if entity.HasChangedDate { entity.HasChangedDate = false - return e.writer.UpdateEntity(dm.ID, entity) + return ctx.Backend.UpdateEntity(dm.ID, entity) } } @@ -749,7 +748,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { // Remove the attribute entity.Attributes = append(entity.Attributes[:idx], entity.Attributes[idx+1:]...) - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("drop attribute", err) } invalidateHierarchy(ctx) @@ -774,7 +773,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { case ast.AlterEntitySetDocumentation: entity.Documentation = s.Documentation - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("set documentation", err) } invalidateDomainModelsCache(ctx) @@ -783,7 +782,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { case ast.AlterEntitySetComment: // Comments are stored as documentation in the Mendix model entity.Documentation = s.Comment - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("set comment", err) } invalidateDomainModelsCache(ctx) @@ -794,7 +793,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { return mdlerrors.NewValidation("no position provided") } entity.Location = model.Point{X: s.Position.X, Y: s.Position.Y} - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("set position", err) } invalidateDomainModelsCache(ctx) @@ -828,7 +827,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { } index.ID = idxID entity.Indexes = append(entity.Indexes, index) - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("add index", err) } invalidateDomainModelsCache(ctx) @@ -852,7 +851,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { return mdlerrors.NewNotFoundMsg("index", s.IndexName, fmt.Sprintf("index '%s' not found on entity %s", s.IndexName, s.Name)) } entity.Indexes = append(entity.Indexes[:idx], entity.Indexes[idx+1:]...) - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("drop index", err) } invalidateDomainModelsCache(ctx) @@ -875,7 +874,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { } } entity.EventHandlers = append(entity.EventHandlers, ehs[0]) - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("add event handler", err) } invalidateDomainModelsCache(ctx) @@ -901,7 +900,7 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { fmt.Sprintf("event handler %s %s not found on %s", s.EventHandler.Moment, s.EventHandler.Event, s.Name)) } entity.EventHandlers = append(entity.EventHandlers[:idx], entity.EventHandlers[idx+1:]...) - if err := e.writer.UpdateEntity(dm.ID, entity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, entity); err != nil { return mdlerrors.NewBackend("drop event handler", err) } invalidateDomainModelsCache(ctx) @@ -918,7 +917,6 @@ func execAlterEntity(ctx *ExecContext, s *ast.AlterEntityStmt) error { // execDropEntity handles DROP ENTITY statements. func execDropEntity(ctx *ExecContext, s *ast.DropEntityStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -929,7 +927,7 @@ func execDropEntity(ctx *ExecContext, s *ast.DropEntityStmt) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -941,11 +939,11 @@ func execDropEntity(ctx *ExecContext, s *ast.DropEntityStmt) error { // If this is a view entity, also delete the associated ViewEntitySourceDocument if entity.Source == "DomainModels$OqlViewEntitySource" { - if err := e.writer.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { + if err := ctx.Backend.DeleteViewEntitySourceDocumentByName(s.Name.Module, s.Name.Name); err != nil { return mdlerrors.NewBackend("delete view entity source document", err) } } - if err := e.writer.DeleteEntity(dm.ID, entity.ID); err != nil { + if err := ctx.Backend.DeleteEntity(dm.ID, entity.ID); err != nil { return mdlerrors.NewBackend("delete entity", err) } invalidateDomainModelsCache(ctx) @@ -960,8 +958,7 @@ func execDropEntity(ctx *ExecContext, s *ast.DropEntityStmt) error { // warnEntityReferences prints a warning if the entity is referenced by other elements. // Uses the catalog if available; silently skips if catalog is not built. func warnEntityReferences(ctx *ExecContext, entityName string) { - e := ctx.executor - if e.catalog == nil || !e.catalog.IsBuilt() { + if ctx.Catalog == nil || !ctx.Catalog.IsBuilt() { return } @@ -969,7 +966,7 @@ func warnEntityReferences(ctx *ExecContext, entityName string) { "SELECT SourceType, SourceName, RefKind FROM refs WHERE TargetName = '%s'", strings.ReplaceAll(entityName, "'", "''"), ) - result, err := e.catalog.Query(query) + result, err := ctx.Catalog.Query(query) if err != nil || result.Count == 0 { return } diff --git a/mdl/executor/cmd_entities_access.go b/mdl/executor/cmd_entities_access.go index 3b113e84..612720ad 100644 --- a/mdl/executor/cmd_entities_access.go +++ b/mdl/executor/cmd_entities_access.go @@ -164,7 +164,6 @@ func formatAccessRuleRights(ctx *ExecContext, rule *domainmodel.AccessRule, attr // formatAccessRuleResult re-reads the entity and formats the resulting access state // for the given roles. Returns a string like " Result: CREATE, READ (Name, Price)\n". func formatAccessRuleResult(ctx *ExecContext, moduleName, entityName string, roleNames []string) string { - e := ctx.executor invalidateDomainModelsCache(ctx) module, err := findModule(ctx, moduleName) @@ -172,7 +171,7 @@ func formatAccessRuleResult(ctx *ExecContext, moduleName, entityName string, rol return "" } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return "" } diff --git a/mdl/executor/cmd_entities_describe.go b/mdl/executor/cmd_entities_describe.go index dab7dd4f..1de25849 100644 --- a/mdl/executor/cmd_entities_describe.go +++ b/mdl/executor/cmd_entities_describe.go @@ -16,9 +16,8 @@ import ( // showEntities handles SHOW ENTITIES command. func showEntities(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Build module ID -> name map (single query) - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("list modules", err) } @@ -28,7 +27,7 @@ func showEntities(ctx *ExecContext, moduleName string) error { } // Get all domain models in a single query (avoids O(n²) behavior) - domainModels, err := e.reader.ListDomainModels() + domainModels, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } @@ -159,7 +158,6 @@ func showEntities(ctx *ExecContext, moduleName string) error { // showEntity handles SHOW ENTITY command. func showEntity(ctx *ExecContext, name *ast.QualifiedName) error { - e := ctx.executor if name == nil { return mdlerrors.NewValidation("entity name required") } @@ -169,7 +167,7 @@ func showEntity(ctx *ExecContext, name *ast.QualifiedName) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -217,13 +215,12 @@ func showEntity(ctx *ExecContext, name *ast.QualifiedName) error { // describeEntity handles DESCRIBE ENTITY command. func describeEntity(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor module, err := findModule(ctx, name.Module) if err != nil { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -465,7 +462,6 @@ func extractAttrNameFromQualified(qualifiedName string) string { // resolveMicroflowByName resolves a qualified microflow name to its ID. // It checks both microflows created during this session and existing microflows in the project. func resolveMicroflowByName(ctx *ExecContext, qualifiedName string) (model.ID, error) { - e := ctx.executor parts := strings.Split(qualifiedName, ".") if len(parts) < 2 { return "", mdlerrors.NewValidationf("invalid microflow name: %s (expected Module.Name)", qualifiedName) @@ -474,14 +470,14 @@ func resolveMicroflowByName(ctx *ExecContext, qualifiedName string) (model.ID, e mfName := strings.Join(parts[1:], ".") // Check microflows created during this session - if e.cache != nil && e.cache.createdMicroflows != nil { - if info, ok := e.cache.createdMicroflows[qualifiedName]; ok { + if ctx.Cache != nil && ctx.Cache.createdMicroflows != nil { + if info, ok := ctx.Cache.createdMicroflows[qualifiedName]; ok { return info.ID, nil } } // Search existing microflows - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return "", mdlerrors.NewBackend("list microflows", err) } @@ -504,8 +500,7 @@ func resolveMicroflowByName(ctx *ExecContext, qualifiedName string) (model.ID, e // lookupMicroflowName reverse-looks up a microflow ID to its qualified name. func lookupMicroflowName(ctx *ExecContext, mfID model.ID) string { - e := ctx.executor - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return "" } diff --git a/mdl/executor/cmd_enumerations.go b/mdl/executor/cmd_enumerations.go index 8acc747b..1506be35 100644 --- a/mdl/executor/cmd_enumerations.go +++ b/mdl/executor/cmd_enumerations.go @@ -16,7 +16,6 @@ import ( // execCreateEnumeration handles CREATE ENUMERATION statements. func execCreateEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() @@ -57,7 +56,7 @@ func execCreateEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) error // If enumeration exists and CREATE OR MODIFY, delete it first if existingEnum != nil && s.CreateOrModify { - if err := e.writer.DeleteEnumeration(existingEnum.ID); err != nil { + if err := ctx.Backend.DeleteEnumeration(existingEnum.ID); err != nil { return mdlerrors.NewBackend("delete existing enumeration", err) } } @@ -70,7 +69,7 @@ func execCreateEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) error Values: values, } - if err := e.writer.CreateEnumeration(enum); err != nil { + if err := ctx.Backend.CreateEnumeration(enum); err != nil { return mdlerrors.NewBackend("create enumeration", err) } @@ -83,9 +82,8 @@ func execCreateEnumeration(ctx *ExecContext, s *ast.CreateEnumerationStmt) error // findEnumeration finds an enumeration by module and name. func findEnumeration(ctx *ExecContext, moduleName, enumName string) *model.Enumeration { - e := ctx.executor - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return nil } @@ -113,14 +111,13 @@ func execAlterEnumeration(ctx *ExecContext, s *ast.AlterEnumerationStmt) error { // execDropEnumeration handles DROP ENUMERATION statements. func execDropEnumeration(ctx *ExecContext, s *ast.DropEnumerationStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } // Find enumeration - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return mdlerrors.NewBackend("list enumerations", err) } @@ -130,7 +127,7 @@ func execDropEnumeration(ctx *ExecContext, s *ast.DropEnumerationStmt) error { // Check module matches module, err := findModuleByID(ctx, enum.ContainerID) if err == nil && (s.Name.Module == "" || module.Name == s.Name.Module) { - if err := e.writer.DeleteEnumeration(enum.ID); err != nil { + if err := ctx.Backend.DeleteEnumeration(enum.ID); err != nil { return mdlerrors.NewBackend("delete enumeration", err) } fmt.Fprintf(ctx.Output, "Dropped enumeration: %s\n", s.Name) @@ -144,9 +141,8 @@ func execDropEnumeration(ctx *ExecContext, s *ast.DropEnumerationStmt) error { // showEnumerations handles SHOW ENUMERATIONS command. func showEnumerations(ctx *ExecContext, moduleName string) error { - e := ctx.executor - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return mdlerrors.NewBackend("list enumerations", err) } @@ -196,9 +192,8 @@ func showEnumerations(ctx *ExecContext, moduleName string) error { // describeEnumeration handles DESCRIBE ENUMERATION command. func describeEnumeration(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return mdlerrors.NewBackend("list enumerations", err) } diff --git a/mdl/executor/cmd_export_mappings.go b/mdl/executor/cmd_export_mappings.go index 2e3e7a16..0745cad6 100644 --- a/mdl/executor/cmd_export_mappings.go +++ b/mdl/executor/cmd_export_mappings.go @@ -16,12 +16,11 @@ import ( // showExportMappings prints a table of all export mapping documents. func showExportMappings(ctx *ExecContext, inModule string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - all, err := e.reader.ListExportMappings() + all, err := ctx.Backend.ListExportMappings() if err != nil { return mdlerrors.NewBackend("list export mappings", err) } @@ -80,12 +79,11 @@ func showExportMappings(ctx *ExecContext, inModule string) error { // describeExportMapping prints the MDL representation of an export mapping. func describeExportMapping(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - em, err := e.reader.GetExportMappingByQualifiedName(name.Module, name.Name) + em, err := ctx.Backend.GetExportMappingByQualifiedName(name.Module, name.Name) if err != nil { if strings.Contains(err.Error(), "not found") { return mdlerrors.NewNotFound("export mapping", name.String()) @@ -211,7 +209,7 @@ func execCreateExportMapping(ctx *ExecContext, s *ast.CreateExportMappingStmt) e // Build a path→element info map from the JSON structure for schema alignment. jsElems := map[string]*mpr.JsonElement{} if s.SchemaKind == "JSON_STRUCTURE" && s.SchemaRef.Module != "" { - if js, err2 := e.reader.GetJsonStructureByQualifiedName(s.SchemaRef.Module, s.SchemaRef.Name); err2 == nil { + if js, err2 := ctx.Backend.GetJsonStructureByQualifiedName(s.SchemaRef.Module, s.SchemaRef.Name); err2 == nil { buildJsonElementPathMap(js.Elements, jsElems) } } @@ -222,7 +220,7 @@ func execCreateExportMapping(ctx *ExecContext, s *ast.CreateExportMappingStmt) e em.Elements = append(em.Elements, root) } - if err := e.writer.CreateExportMapping(em); err != nil { + if err := ctx.Backend.CreateExportMapping(em); err != nil { return mdlerrors.NewBackend("create export mapping", err) } @@ -364,12 +362,11 @@ func buildExportMappingElementModel(moduleName string, def *ast.ExportMappingEle // execDropExportMapping deletes an export mapping. func execDropExportMapping(ctx *ExecContext, s *ast.DropExportMappingStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - em, err := e.reader.GetExportMappingByQualifiedName(s.Name.Module, s.Name.Name) + em, err := ctx.Backend.GetExportMappingByQualifiedName(s.Name.Module, s.Name.Name) if err != nil { if strings.Contains(err.Error(), "not found") { return mdlerrors.NewNotFound("export mapping", s.Name.String()) @@ -377,7 +374,7 @@ func execDropExportMapping(ctx *ExecContext, s *ast.DropExportMappingStmt) error return mdlerrors.NewBackend("get export mapping", err) } - if err := e.writer.DeleteExportMapping(em.ID); err != nil { + if err := ctx.Backend.DeleteExportMapping(em.ID); err != nil { return mdlerrors.NewBackend("drop export mapping", err) } diff --git a/mdl/executor/cmd_features.go b/mdl/executor/cmd_features.go index 76e7ade0..ccf58809 100644 --- a/mdl/executor/cmd_features.go +++ b/mdl/executor/cmd_features.go @@ -15,7 +15,6 @@ import ( // version. Returns nil if available, or an actionable error with the version // requirement and a hint. Safe to call when e.reader is nil (returns nil). func checkFeature(ctx *ExecContext, area, name, statement, hint string) error { - e := ctx.executor if !ctx.Connected() { return nil // No project connected; skip check @@ -24,7 +23,7 @@ func checkFeature(ctx *ExecContext, area, name, statement, hint string) error { if err != nil { return nil // Registry unavailable; don't block execution } - rpv := e.reader.ProjectVersion() + rpv := ctx.Backend.ProjectVersion() pv := versions.SemVer{Major: rpv.MajorVersion, Minor: rpv.MinorVersion, Patch: rpv.PatchVersion} if reg.IsAvailable(area, name, pv) { return nil @@ -50,7 +49,6 @@ func checkFeature(ctx *ExecContext, area, name, statement, hint string) error { // execShowFeatures handles SHOW FEATURES, SHOW FEATURES FOR VERSION, and // SHOW FEATURES ADDED SINCE commands. func execShowFeatures(ctx *ExecContext, s *ast.ShowFeaturesStmt) error { - e := ctx.executor reg, err := versions.Load() if err != nil { @@ -81,7 +79,7 @@ func execShowFeatures(ctx *ExecContext, s *ast.ShowFeaturesStmt) error { if !ctx.Connected() { return mdlerrors.NewNotConnectedMsg("not connected to a project\n hint: use SHOW FEATURES FOR VERSION x.y without a project connection") } - rpv := e.reader.ProjectVersion() + rpv := ctx.Backend.ProjectVersion() pv = versions.SemVer{Major: rpv.MajorVersion, Minor: rpv.MinorVersion, Patch: rpv.PatchVersion} } diff --git a/mdl/executor/cmd_folders.go b/mdl/executor/cmd_folders.go index 9c2dfe45..5dafe8f8 100644 --- a/mdl/executor/cmd_folders.go +++ b/mdl/executor/cmd_folders.go @@ -51,7 +51,6 @@ func findFolderByPath(ctx *ExecContext, moduleID model.ID, folderPath string, fo // execDropFolder handles DROP FOLDER 'path' IN Module statements. // The folder must be empty (no child documents or sub-folders). func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnected() } @@ -61,7 +60,7 @@ func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { return mdlerrors.NewNotFound("module", s.Module) } - folders, err := e.reader.ListFolders() + folders, err := ctx.Backend.ListFolders() if err != nil { return mdlerrors.NewBackend("list folders", err) } @@ -71,7 +70,7 @@ func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { return fmt.Errorf("%w in %s", err, s.Module) } - if err := e.writer.DeleteFolder(folderID); err != nil { + if err := ctx.Backend.DeleteFolder(folderID); err != nil { return mdlerrors.NewBackend(fmt.Sprintf("delete folder '%s'", s.FolderPath), err) } @@ -82,7 +81,6 @@ func execDropFolder(ctx *ExecContext, s *ast.DropFolderStmt) error { // execMoveFolder handles MOVE FOLDER Module.FolderName TO ... statements. func execMoveFolder(ctx *ExecContext, s *ast.MoveFolderStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnected() } @@ -94,7 +92,7 @@ func execMoveFolder(ctx *ExecContext, s *ast.MoveFolderStmt) error { } // Find the source folder - folders, err := e.reader.ListFolders() + folders, err := ctx.Backend.ListFolders() if err != nil { return mdlerrors.NewBackend("list folders", err) } @@ -127,7 +125,7 @@ func execMoveFolder(ctx *ExecContext, s *ast.MoveFolderStmt) error { } // Move the folder - if err := e.writer.MoveFolder(folderID, targetContainerID); err != nil { + if err := ctx.Backend.MoveFolder(folderID, targetContainerID); err != nil { return mdlerrors.NewBackend("move folder", err) } diff --git a/mdl/executor/cmd_fragments.go b/mdl/executor/cmd_fragments.go index cb698100..1ca098c5 100644 --- a/mdl/executor/cmd_fragments.go +++ b/mdl/executor/cmd_fragments.go @@ -86,7 +86,7 @@ func describeFragmentFrom(ctx *ExecContext, s *ast.DescribeFragmentFromStmt) err switch s.ContainerType { case "PAGE": - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -105,7 +105,7 @@ func describeFragmentFrom(ctx *ExecContext, s *ast.DescribeFragmentFromStmt) err rawWidgets = getPageWidgetsFromRaw(ctx, foundPage.ID) case "SNIPPET": - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } diff --git a/mdl/executor/cmd_imagecollections.go b/mdl/executor/cmd_imagecollections.go index 702c0179..fdafb88a 100644 --- a/mdl/executor/cmd_imagecollections.go +++ b/mdl/executor/cmd_imagecollections.go @@ -16,7 +16,6 @@ import ( // execCreateImageCollection handles CREATE IMAGE COLLECTION statements. func execCreateImageCollection(ctx *ExecContext, s *ast.CreateImageCollectionStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -63,7 +62,7 @@ func execCreateImageCollection(ctx *ExecContext, s *ast.CreateImageCollectionStm }) } - if err := e.writer.CreateImageCollection(ic); err != nil { + if err := ctx.Backend.CreateImageCollection(ic); err != nil { return mdlerrors.NewBackend("create image collection", err) } @@ -76,7 +75,6 @@ func execCreateImageCollection(ctx *ExecContext, s *ast.CreateImageCollectionStm // execDropImageCollection handles DROP IMAGE COLLECTION statements. func execDropImageCollection(ctx *ExecContext, s *ast.DropImageCollectionStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -86,7 +84,7 @@ func execDropImageCollection(ctx *ExecContext, s *ast.DropImageCollectionStmt) e return mdlerrors.NewNotFound("image collection", s.Name.String()) } - if err := e.writer.DeleteImageCollection(string(ic.ID)); err != nil { + if err := ctx.Backend.DeleteImageCollection(string(ic.ID)); err != nil { return mdlerrors.NewBackend("delete image collection", err) } @@ -200,8 +198,7 @@ func extToImageFormat(ext string) string { // showImageCollections handles SHOW IMAGE COLLECTION [IN module]. func showImageCollections(ctx *ExecContext, moduleName string) error { - e := ctx.executor - collections, err := e.reader.ListImageCollections() + collections, err := ctx.Backend.ListImageCollections() if err != nil { return mdlerrors.NewBackend("list image collections", err) } @@ -236,8 +233,7 @@ func showImageCollections(ctx *ExecContext, moduleName string) error { // findImageCollection finds an image collection by module and name. func findImageCollection(ctx *ExecContext, moduleName, collectionName string) *mpr.ImageCollection { - e := ctx.executor - collections, err := e.reader.ListImageCollections() + collections, err := ctx.Backend.ListImageCollections() if err != nil { return nil } diff --git a/mdl/executor/cmd_import.go b/mdl/executor/cmd_import.go index fd05b323..5caaf083 100644 --- a/mdl/executor/cmd_import.go +++ b/mdl/executor/cmd_import.go @@ -99,7 +99,6 @@ func execImport(ctx *ExecContext, s *ast.ImportStmt) error { // resolveImportLinks resolves LINK mappings from the AST into AssocInfo structs // by looking up association metadata from the MPR and the Mendix system tables. func resolveImportLinks(ctx *ExecContext, goCtx context.Context, mendixConn *sqllib.Connection, s *ast.ImportStmt) ([]*sqllib.AssocInfo, error) { - e := ctx.executor if len(s.Links) == 0 { return nil, nil } @@ -114,7 +113,7 @@ func resolveImportLinks(ctx *ExecContext, goCtx context.Context, mendixConn *sql targetModule := targetParts[0] // Load domain models to find associations - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return nil, mdlerrors.NewBackend("list domain models", err) } @@ -317,7 +316,6 @@ func getParentEntityName(a *domainmodel.Association, ca *domainmodel.CrossModule // ensureMendixDBConnection reads the project settings and auto-connects to the Mendix app DB. func ensureMendixDBConnection(ctx *ExecContext) (*sqllib.Connection, error) { - e := ctx.executor mgr := ensureSQLManager(ctx) // Check if already connected @@ -326,7 +324,7 @@ func ensureMendixDBConnection(ctx *ExecContext) (*sqllib.Connection, error) { } // Read project settings to get DB configuration - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return nil, mdlerrors.NewBackend("read project settings", err) } diff --git a/mdl/executor/cmd_import_mappings.go b/mdl/executor/cmd_import_mappings.go index cb188241..a750fc3f 100644 --- a/mdl/executor/cmd_import_mappings.go +++ b/mdl/executor/cmd_import_mappings.go @@ -16,12 +16,11 @@ import ( // showImportMappings prints a table of all import mapping documents. func showImportMappings(ctx *ExecContext, inModule string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - all, err := e.reader.ListImportMappings() + all, err := ctx.Backend.ListImportMappings() if err != nil { return mdlerrors.NewBackend("list import mappings", err) } @@ -80,12 +79,11 @@ func showImportMappings(ctx *ExecContext, inModule string) error { // describeImportMapping prints the MDL representation of an import mapping. func describeImportMapping(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - im, err := e.reader.GetImportMappingByQualifiedName(name.Module, name.Name) + im, err := ctx.Backend.GetImportMappingByQualifiedName(name.Module, name.Name) if err != nil { if strings.Contains(err.Error(), "not found") { return mdlerrors.NewNotFound("import mapping", name.String()) @@ -220,7 +218,7 @@ func execCreateImportMapping(ctx *ExecContext, s *ast.CreateImportMappingStmt) e // Build path→JsonElement map from JSON structure — mapping elements clone from this jsElementsByPath := map[string]*mpr.JsonElement{} if s.SchemaKind == "JSON_STRUCTURE" && s.SchemaRef.Module != "" { - if js, err2 := e.reader.GetJsonStructureByQualifiedName(s.SchemaRef.Module, s.SchemaRef.Name); err2 == nil { + if js, err2 := ctx.Backend.GetJsonStructureByQualifiedName(s.SchemaRef.Module, s.SchemaRef.Name); err2 == nil { buildJsonElementPathMap(js.Elements, jsElementsByPath) } } @@ -231,7 +229,7 @@ func execCreateImportMapping(ctx *ExecContext, s *ast.CreateImportMappingStmt) e im.Elements = append(im.Elements, root) } - if err := e.writer.CreateImportMapping(im); err != nil { + if err := ctx.Backend.CreateImportMapping(im); err != nil { return mdlerrors.NewBackend("create import mapping", err) } @@ -378,12 +376,11 @@ func resolveAttributeType(entityQN, attrName string, reader *mpr.Reader) string // execDropImportMapping deletes an import mapping. func execDropImportMapping(ctx *ExecContext, s *ast.DropImportMappingStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - im, err := e.reader.GetImportMappingByQualifiedName(s.Name.Module, s.Name.Name) + im, err := ctx.Backend.GetImportMappingByQualifiedName(s.Name.Module, s.Name.Name) if err != nil { if strings.Contains(err.Error(), "not found") { return mdlerrors.NewNotFound("import mapping", s.Name.String()) @@ -391,7 +388,7 @@ func execDropImportMapping(ctx *ExecContext, s *ast.DropImportMappingStmt) error return mdlerrors.NewBackend("get import mapping", err) } - if err := e.writer.DeleteImportMapping(im.ID); err != nil { + if err := ctx.Backend.DeleteImportMapping(im.ID); err != nil { return mdlerrors.NewBackend("drop import mapping", err) } diff --git a/mdl/executor/cmd_javaactions.go b/mdl/executor/cmd_javaactions.go index 4dc4628c..6928b911 100644 --- a/mdl/executor/cmd_javaactions.go +++ b/mdl/executor/cmd_javaactions.go @@ -19,7 +19,6 @@ import ( // showJavaActions handles SHOW JAVA ACTIONS command. func showJavaActions(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -27,7 +26,7 @@ func showJavaActions(ctx *ExecContext, moduleName string) error { } // Get all Java actions - javaActions, err := e.reader.ListJavaActions() + javaActions, err := ctx.Backend.ListJavaActions() if err != nil { return mdlerrors.NewBackend("list java actions", err) } @@ -68,9 +67,8 @@ func showJavaActions(ctx *ExecContext, moduleName string) error { // describeJavaAction handles DESCRIBE JAVA ACTION command - outputs MDL-style representation. func describeJavaAction(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor qualifiedName := name.Module + "." + name.Name - ja, err := e.reader.ReadJavaActionByName(qualifiedName) + ja, err := ctx.Backend.ReadJavaActionByName(qualifiedName) if err != nil { return mdlerrors.NewNotFound("java action", qualifiedName) } @@ -162,7 +160,7 @@ func describeJavaAction(ctx *ExecContext, name ast.QualifiedName) error { } // Try to read and include Java source code - javaCode := readJavaActionUserCode(e.mprPath, name.Module, name.Name) + javaCode := readJavaActionUserCode(ctx.MprPath, name.Module, name.Name) if javaCode != "" { sb.WriteString("\nAS $$\n") sb.WriteString(javaCode) @@ -249,7 +247,6 @@ func formatJavaActionReturnType(t javaactions.CodeActionReturnType) string { // execDropJavaAction handles DROP JAVA ACTION statements. func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -261,7 +258,7 @@ func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { } // Find and delete the Java action - jas, err := e.reader.ListJavaActions() + jas, err := ctx.Backend.ListJavaActions() if err != nil { return mdlerrors.NewBackend("list java actions", err) } @@ -270,7 +267,7 @@ func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { modID := h.FindModuleID(ja.ContainerID) modName := h.GetModuleName(modID) if modName == s.Name.Module && ja.Name == s.Name.Name { - if err := e.writer.DeleteJavaAction(ja.ID); err != nil { + if err := ctx.Backend.DeleteJavaAction(ja.ID); err != nil { return mdlerrors.NewBackend("delete java action", err) } fmt.Fprintf(ctx.Output, "Dropped java action: %s.%s\n", s.Name.Module, s.Name.Name) @@ -283,7 +280,6 @@ func execDropJavaAction(ctx *ExecContext, s *ast.DropJavaActionStmt) error { // execCreateJavaAction handles CREATE JAVA ACTION statements. func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -295,7 +291,7 @@ func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { } // Find the module - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("get modules", err) } @@ -314,7 +310,7 @@ func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { } // Check if Java action already exists - jas, err := e.reader.ListJavaActions() + jas, err := ctx.Backend.ListJavaActions() if err != nil { return mdlerrors.NewBackend("list java actions", err) } @@ -414,19 +410,19 @@ func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { } // Create in MPR - if err := e.writer.CreateJavaAction(ja); err != nil { + if err := ctx.Backend.CreateJavaAction(ja); err != nil { return mdlerrors.NewBackend("create java action", err) } // Write Java source file if code is provided if s.JavaCode != "" { - if err := e.writer.WriteJavaSourceFile(moduleName, s.Name.Name, s.JavaCode, ja.Parameters, ja.ReturnType); err != nil { + if err := ctx.Backend.WriteJavaSourceFile(moduleName, s.Name.Name, s.JavaCode, ja.Parameters, ja.ReturnType); err != nil { return mdlerrors.NewBackend("write java source file", err) } } // Clear cache - e.cache = nil + ctx.Cache = nil fmt.Fprintf(ctx.Output, "Created java action: %s.%s\n", s.Name.Module, s.Name.Name) return nil diff --git a/mdl/executor/cmd_javascript_actions.go b/mdl/executor/cmd_javascript_actions.go index 4898391c..251aba0c 100644 --- a/mdl/executor/cmd_javascript_actions.go +++ b/mdl/executor/cmd_javascript_actions.go @@ -17,13 +17,12 @@ import ( // showJavaScriptActions handles SHOW JAVASCRIPT ACTIONS command. func showJavaScriptActions(ctx *ExecContext, moduleName string) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - jsActions, err := e.reader.ListJavaScriptActions() + jsActions, err := ctx.Backend.ListJavaScriptActions() if err != nil { return mdlerrors.NewBackend("list javascript actions", err) } @@ -67,9 +66,8 @@ func showJavaScriptActions(ctx *ExecContext, moduleName string) error { // describeJavaScriptAction handles DESCRIBE JAVASCRIPT ACTION command. func describeJavaScriptAction(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor qualifiedName := name.Module + "." + name.Name - jsa, err := e.reader.ReadJavaScriptActionByName(qualifiedName) + jsa, err := ctx.Backend.ReadJavaScriptActionByName(qualifiedName) if err != nil { return mdlerrors.NewNotFound("javascript action", qualifiedName) } @@ -180,7 +178,7 @@ func describeJavaScriptAction(ctx *ExecContext, name ast.QualifiedName) error { } // JavaScript source code - userCode, extraCode := readJavaScriptActionSource(e.mprPath, name.Module, name.Name) + userCode, extraCode := readJavaScriptActionSource(ctx.MprPath, name.Module, name.Name) if userCode != "" { sb.WriteString("\nAS $$\n") sb.WriteString(userCode) diff --git a/mdl/executor/cmd_jsonstructures.go b/mdl/executor/cmd_jsonstructures.go index 781dc523..57a0d9d3 100644 --- a/mdl/executor/cmd_jsonstructures.go +++ b/mdl/executor/cmd_jsonstructures.go @@ -16,8 +16,7 @@ import ( // showJsonStructures handles SHOW JSON STRUCTURES [IN module]. func showJsonStructures(ctx *ExecContext, moduleName string) error { - e := ctx.executor - structures, err := e.reader.ListJsonStructures() + structures, err := ctx.Backend.ListJsonStructures() if err != nil { return mdlerrors.NewBackend("list JSON structures", err) } @@ -174,7 +173,6 @@ func capitalizeFirstRune(s string) string { // execCreateJsonStructure handles CREATE [OR REPLACE] JSON STRUCTURE statements. func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -200,7 +198,7 @@ func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) e if existing != nil { if s.CreateOrReplace { // Delete existing before recreating - if err := e.writer.DeleteJsonStructure(string(existing.ID)); err != nil { + if err := ctx.Backend.DeleteJsonStructure(string(existing.ID)); err != nil { return mdlerrors.NewBackend("delete existing JSON structure", err) } } else { @@ -227,7 +225,7 @@ func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) e Elements: elements, } - if err := e.writer.CreateJsonStructure(js); err != nil { + if err := ctx.Backend.CreateJsonStructure(js); err != nil { return mdlerrors.NewBackend("create JSON structure", err) } @@ -244,7 +242,6 @@ func execCreateJsonStructure(ctx *ExecContext, s *ast.CreateJsonStructureStmt) e // execDropJsonStructure handles DROP JSON STRUCTURE statements. func execDropJsonStructure(ctx *ExecContext, s *ast.DropJsonStructureStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -254,7 +251,7 @@ func execDropJsonStructure(ctx *ExecContext, s *ast.DropJsonStructureStmt) error return mdlerrors.NewNotFound("JSON structure", s.Name.String()) } - if err := e.writer.DeleteJsonStructure(string(js.ID)); err != nil { + if err := ctx.Backend.DeleteJsonStructure(string(js.ID)); err != nil { return mdlerrors.NewBackend("delete JSON structure", err) } @@ -264,8 +261,7 @@ func execDropJsonStructure(ctx *ExecContext, s *ast.DropJsonStructureStmt) error // findJsonStructure finds a JSON structure by module and name. func findJsonStructure(ctx *ExecContext, moduleName, structName string) *mpr.JsonStructure { - e := ctx.executor - structures, err := e.reader.ListJsonStructures() + structures, err := ctx.Backend.ListJsonStructures() if err != nil { return nil } diff --git a/mdl/executor/cmd_layouts.go b/mdl/executor/cmd_layouts.go index af2b8aa5..4f59afa2 100644 --- a/mdl/executor/cmd_layouts.go +++ b/mdl/executor/cmd_layouts.go @@ -13,7 +13,6 @@ import ( // showLayouts handles SHOW LAYOUTS command. func showLayouts(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -21,7 +20,7 @@ func showLayouts(ctx *ExecContext, moduleName string) error { } // Get all layouts - layouts, err := e.reader.ListLayouts() + layouts, err := ctx.Backend.ListLayouts() if err != nil { return mdlerrors.NewBackend("list layouts", err) } diff --git a/mdl/executor/cmd_lint.go b/mdl/executor/cmd_lint.go index 7b834cda..7b7ca383 100644 --- a/mdl/executor/cmd_lint.go +++ b/mdl/executor/cmd_lint.go @@ -38,7 +38,7 @@ func execLint(ctx *ExecContext, s *ast.LintStmt) error { lintCtx.SetReader(e.reader) // Load configuration - projectDir := filepath.Dir(e.mprPath) + projectDir := filepath.Dir(ctx.MprPath) configPath := linter.FindConfigFile(projectDir) config, err := linter.LoadConfig(configPath) if err != nil { @@ -115,7 +115,6 @@ func execLint(ctx *ExecContext, s *ast.LintStmt) error { // showLintRules displays available lint rules. func showLintRules(ctx *ExecContext) error { - e := ctx.executor fmt.Fprintln(ctx.Output, "Built-in rules:") fmt.Fprintln(ctx.Output) @@ -136,8 +135,8 @@ func showLintRules(ctx *ExecContext) error { } // Show custom Starlark rules if connected - if e.mprPath != "" { - projectDir := filepath.Dir(e.mprPath) + if ctx.MprPath != "" { + projectDir := filepath.Dir(ctx.MprPath) rulesDir := filepath.Join(projectDir, ".claude", "lint-rules") starlarkRules, err := linter.LoadStarlarkRulesFromDir(rulesDir) if err == nil && len(starlarkRules) > 0 { diff --git a/mdl/executor/cmd_mermaid.go b/mdl/executor/cmd_mermaid.go index 27e05f0f..f1e14ca5 100644 --- a/mdl/executor/cmd_mermaid.go +++ b/mdl/executor/cmd_mermaid.go @@ -49,13 +49,12 @@ func (e *Executor) DescribeMermaid(objectType, name string) error { // domainModelToMermaid generates a Mermaid erDiagram for a module's domain model. func domainModelToMermaid(ctx *ExecContext, moduleName string) error { - e := ctx.executor module, err := findModule(ctx, moduleName) if err != nil { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -70,7 +69,7 @@ func domainModelToMermaid(ctx *ExecContext, moduleName string) error { allEntityNames := make(map[model.ID]string) h, err := getHierarchy(ctx) if err == nil { - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, otherDM := range domainModels { modName := h.GetModuleName(otherDM.ContainerID) for _, entity := range otherDM.Entities { @@ -189,7 +188,6 @@ func domainModelToMermaid(ctx *ExecContext, moduleName string) error { // microflowToMermaid generates a Mermaid flowchart for a microflow. func microflowToMermaid(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) @@ -197,7 +195,7 @@ func microflowToMermaid(ctx *ExecContext, name ast.QualifiedName) error { // Build entity name lookup entityNames := make(map[model.ID]string) - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, dm := range domainModels { modName := h.GetModuleName(dm.ContainerID) for _, entity := range dm.Entities { @@ -206,7 +204,7 @@ func microflowToMermaid(ctx *ExecContext, name ast.QualifiedName) error { } // Find the microflow - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -368,13 +366,12 @@ func renderMicroflowMermaid(ctx *ExecContext, mf *microflows.Microflow, entityNa // pageToMermaid generates a Mermaid block diagram for a page's widget structure. func pageToMermaid(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } diff --git a/mdl/executor/cmd_microflow_elk.go b/mdl/executor/cmd_microflow_elk.go index d4b3aafb..2039ab24 100644 --- a/mdl/executor/cmd_microflow_elk.go +++ b/mdl/executor/cmd_microflow_elk.go @@ -62,7 +62,6 @@ type microflowELKEdge struct { // microflowELK generates a JSON graph of a microflow for rendering with ELK.js. func microflowELK(ctx *ExecContext, name string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -81,7 +80,7 @@ func microflowELK(ctx *ExecContext, name string) error { // Build entity name lookup entityNames := make(map[model.ID]string) - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, dm := range domainModels { modName := h.GetModuleName(dm.ContainerID) for _, entity := range dm.Entities { @@ -90,7 +89,7 @@ func microflowELK(ctx *ExecContext, name string) error { } // Find the microflow - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } diff --git a/mdl/executor/cmd_microflows_create.go b/mdl/executor/cmd_microflows_create.go index 8c54ad10..350e701e 100644 --- a/mdl/executor/cmd_microflows_create.go +++ b/mdl/executor/cmd_microflows_create.go @@ -25,11 +25,10 @@ func isBuiltinModuleEntity(moduleName string) bool { // execCreateMicroflow handles CREATE MICROFLOW statements. // loadRestServices returns all consumed REST services, or nil if no reader. func loadRestServices(ctx *ExecContext) ([]*model.ConsumedRestService, error) { - e := ctx.executor if !ctx.Connected() { return nil, nil } - svcs, err := e.reader.ListConsumedRestServices() + svcs, err := ctx.Backend.ListConsumedRestServices() return svcs, err } @@ -58,7 +57,7 @@ func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error { // Check if microflow with same name already exists in this module var existingID model.ID var existingContainerID model.ID - existingMicroflows, err := e.reader.ListMicroflows() + existingMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("check existing microflows", err) } @@ -99,11 +98,11 @@ func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error { // Build entity resolver function for parameter/return types entityResolver := func(qn ast.QualifiedName) model.ID { // Get all domain models and build module name map - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return "" } - modules, _ := e.reader.ListModules() + modules, _ := ctx.Backend.ListModules() moduleNames := make(map[model.ID]string) for _, m := range modules { moduleNames[m.ID] = m.Name @@ -234,12 +233,12 @@ func execCreateMicroflow(ctx *ExecContext, s *ast.CreateMicroflowStmt) error { // Create or update the microflow if existingID != "" { - if err := e.writer.UpdateMicroflow(mf); err != nil { + if err := ctx.Backend.UpdateMicroflow(mf); err != nil { return mdlerrors.NewBackend("update microflow", err) } fmt.Fprintf(ctx.Output, "Replaced microflow: %s.%s\n", s.Name.Module, s.Name.Name) } else { - if err := e.writer.CreateMicroflow(mf); err != nil { + if err := ctx.Backend.CreateMicroflow(mf); err != nil { return mdlerrors.NewBackend("create microflow", err) } fmt.Fprintf(ctx.Output, "Created microflow: %s.%s\n", s.Name.Module, s.Name.Name) diff --git a/mdl/executor/cmd_microflows_drop.go b/mdl/executor/cmd_microflows_drop.go index 5c4bbd7a..a0d74fe9 100644 --- a/mdl/executor/cmd_microflows_drop.go +++ b/mdl/executor/cmd_microflows_drop.go @@ -12,7 +12,6 @@ import ( // execDropMicroflow handles DROP MICROFLOW statements. func execDropMicroflow(ctx *ExecContext, s *ast.DropMicroflowStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -24,7 +23,7 @@ func execDropMicroflow(ctx *ExecContext, s *ast.DropMicroflowStmt) error { } // Find and delete the microflow - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -33,13 +32,13 @@ func execDropMicroflow(ctx *ExecContext, s *ast.DropMicroflowStmt) error { modID := h.FindModuleID(mf.ContainerID) modName := h.GetModuleName(modID) if modName == s.Name.Module && mf.Name == s.Name.Name { - if err := e.writer.DeleteMicroflow(mf.ID); err != nil { + if err := ctx.Backend.DeleteMicroflow(mf.ID); err != nil { return mdlerrors.NewBackend("delete microflow", err) } // Clear executor-level caches so subsequent CREATE sees fresh state qualifiedName := s.Name.Module + "." + s.Name.Name - if e.cache != nil && e.cache.createdMicroflows != nil { - delete(e.cache.createdMicroflows, qualifiedName) + if ctx.Cache != nil && ctx.Cache.createdMicroflows != nil { + delete(ctx.Cache.createdMicroflows, qualifiedName) } invalidateHierarchy(ctx) fmt.Fprintf(ctx.Output, "Dropped microflow: %s.%s\n", s.Name.Module, s.Name.Name) diff --git a/mdl/executor/cmd_microflows_format_action.go b/mdl/executor/cmd_microflows_format_action.go index 5f67bed5..fcc1179d 100644 --- a/mdl/executor/cmd_microflows_format_action.go +++ b/mdl/executor/cmd_microflows_format_action.go @@ -92,7 +92,6 @@ func formatAction( entityNames map[model.ID]string, microflowNames map[model.ID]string, ) string { - e := ctx.executor if action == nil { return "-- Empty action" } @@ -500,7 +499,7 @@ func formatAction( pageName := a.PageName if pageName == "" && a.PageID != "" && ctx.Connected() { // Fall back to looking up by ID (legacy format) - pages, _ := e.reader.ListPages() + pages, _ := ctx.Backend.ListPages() for _, p := range pages { if p.ID == a.PageID { h, _ := getHierarchy(ctx) diff --git a/mdl/executor/cmd_microflows_show.go b/mdl/executor/cmd_microflows_show.go index f2bd58d7..04109465 100644 --- a/mdl/executor/cmd_microflows_show.go +++ b/mdl/executor/cmd_microflows_show.go @@ -16,7 +16,6 @@ import ( // showMicroflows handles SHOW MICROFLOWS command. func showMicroflows(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -24,7 +23,7 @@ func showMicroflows(ctx *ExecContext, moduleName string) error { } // Get all microflows - microflows, err := e.reader.ListMicroflows() + microflows, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -81,7 +80,6 @@ func showMicroflows(ctx *ExecContext, moduleName string) error { // showNanoflows handles SHOW NANOFLOWS command. func showNanoflows(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -89,7 +87,7 @@ func showNanoflows(ctx *ExecContext, moduleName string) error { } // Get all nanoflows - nanoflows, err := e.reader.ListNanoflows() + nanoflows, err := ctx.Backend.ListNanoflows() if err != nil { return mdlerrors.NewBackend("list nanoflows", err) } @@ -192,7 +190,7 @@ func describeMicroflow(ctx *ExecContext, name ast.QualifiedName) error { microflowNames := e.getMicroflowNames(h) // Find the microflow - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -309,7 +307,6 @@ func describeMicroflow(ctx *ExecContext, name ast.QualifiedName) error { // describeNanoflow generates re-executable CREATE OR MODIFY NANOFLOW MDL output // with activities and control flows listed as comments. func describeNanoflow(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) @@ -317,7 +314,7 @@ func describeNanoflow(ctx *ExecContext, name ast.QualifiedName) error { // Build entity name lookup entityNames := make(map[model.ID]string) - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, dm := range domainModels { modName := h.GetModuleName(dm.ContainerID) for _, entity := range dm.Entities { @@ -327,13 +324,13 @@ func describeNanoflow(ctx *ExecContext, name ast.QualifiedName) error { // Build microflow/nanoflow name lookup (used for call actions) microflowNames := make(map[model.ID]string) - allMicroflows, _ := e.reader.ListMicroflows() + allMicroflows, _ := ctx.Backend.ListMicroflows() for _, mf := range allMicroflows { microflowNames[mf.ID] = h.GetQualifiedName(mf.ContainerID, mf.Name) } // Find the nanoflow - allNanoflows, err := e.reader.ListNanoflows() + allNanoflows, err := ctx.Backend.ListNanoflows() if err != nil { return mdlerrors.NewBackend("list nanoflows", err) } @@ -426,14 +423,13 @@ func describeNanoflow(ctx *ExecContext, name ast.QualifiedName) error { // describeMicroflowToString generates MDL source for a microflow and returns it as a string // along with a source map mapping node IDs to line ranges. func describeMicroflowToString(ctx *ExecContext, name ast.QualifiedName) (string, map[string]elkSourceRange, error) { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return "", nil, mdlerrors.NewBackend("build hierarchy", err) } entityNames := make(map[model.ID]string) - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() for _, dm := range domainModels { modName := h.GetModuleName(dm.ContainerID) for _, entity := range dm.Entities { @@ -442,7 +438,7 @@ func describeMicroflowToString(ctx *ExecContext, name ast.QualifiedName) (string } microflowNames := make(map[model.ID]string) - allMicroflows, err := e.reader.ListMicroflows() + allMicroflows, err := ctx.Backend.ListMicroflows() if err != nil { return "", nil, mdlerrors.NewBackend("list microflows", err) } diff --git a/mdl/executor/cmd_misc.go b/mdl/executor/cmd_misc.go index 2684e818..c8ed29c0 100644 --- a/mdl/executor/cmd_misc.go +++ b/mdl/executor/cmd_misc.go @@ -21,13 +21,12 @@ var ErrExit = mdlerrors.ErrExit // execUpdate handles UPDATE statements (refresh from disk). func execUpdate(ctx *ExecContext) error { - e := ctx.executor - if e.mprPath == "" { + if ctx.MprPath == "" { return mdlerrors.NewNotConnected() } // Reconnect to refresh - path := e.mprPath + path := ctx.MprPath execDisconnect(ctx) return execConnect(ctx, &ast.ConnectStmt{Path: path}) } @@ -39,8 +38,7 @@ func execRefresh(ctx *ExecContext) error { // execSet handles SET statements. func execSet(ctx *ExecContext, s *ast.SetStmt) error { - e := ctx.executor - e.settings[s.Key] = s.Value + ctx.Settings[s.Key] = s.Value fmt.Fprintf(ctx.Output, "Set %s = %v\n", s.Key, s.Value) return nil } @@ -325,12 +323,11 @@ Statement Terminator: // showVersion displays Mendix project version information. func showVersion(ctx *ExecContext) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - pv := e.reader.ProjectVersion() + pv := ctx.Backend.ProjectVersion() fmt.Fprintf(ctx.Output, "Mendix Version: %s\n", pv.ProductVersion) fmt.Fprintf(ctx.Output, "Build Version: %s\n", pv.BuildVersion) fmt.Fprintf(ctx.Output, "MPR Format: v%d\n", pv.FormatVersion) diff --git a/mdl/executor/cmd_modules.go b/mdl/executor/cmd_modules.go index fdfa1283..87bc86ec 100644 --- a/mdl/executor/cmd_modules.go +++ b/mdl/executor/cmd_modules.go @@ -16,13 +16,12 @@ import ( // execCreateModule handles CREATE MODULE statements. func execCreateModule(ctx *ExecContext, s *ast.CreateModuleStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } // Check if module already exists - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("list modules", err) } @@ -39,7 +38,7 @@ func execCreateModule(ctx *ExecContext, s *ast.CreateModuleStmt) error { Name: s.Name, } - if err := e.writer.CreateModule(module); err != nil { + if err := ctx.Backend.CreateModule(module); err != nil { return mdlerrors.NewBackend("create module", err) } @@ -60,13 +59,12 @@ func execCreateModule(ctx *ExecContext, s *ast.CreateModuleStmt) error { // - Snippets // - Constants func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } // Find the module - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("list modules", err) } @@ -90,10 +88,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { var nEnums, nEntities, nAssocs, nMicroflows, nNanoflows, nPages, nSnippets, nLayouts, nConstants, nJavaActions, nServices, nBizEvents, nDbConns int // Delete enumerations in this module - if enums, err := e.reader.ListEnumerations(); err == nil { + if enums, err := ctx.Backend.ListEnumerations(); err == nil { for _, enum := range enums { if moduleContainers[enum.ContainerID] { - if err := e.writer.DeleteEnumeration(enum.ID); err != nil { + if err := ctx.Backend.DeleteEnumeration(enum.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete enumeration %s: %v\n", enum.Name, err) } else { nEnums++ @@ -103,12 +101,12 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete entities in domain models belonging to this module - if dms, err := e.reader.ListDomainModels(); err == nil { + if dms, err := ctx.Backend.ListDomainModels(); err == nil { for _, dm := range dms { if moduleContainers[dm.ContainerID] { // Delete all associations in this domain model first (they reference entities) for _, assoc := range dm.Associations { - if err := e.writer.DeleteAssociation(dm.ID, assoc.ID); err != nil { + if err := ctx.Backend.DeleteAssociation(dm.ID, assoc.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete association %s: %v\n", assoc.Name, err) } else { nAssocs++ @@ -116,7 +114,7 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete all entities in this domain model for _, entity := range dm.Entities { - if err := e.writer.DeleteEntity(dm.ID, entity.ID); err != nil { + if err := ctx.Backend.DeleteEntity(dm.ID, entity.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete entity %s: %v\n", entity.Name, err) } else { nEntities++ @@ -127,10 +125,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete microflows in this module - if mfs, err := e.reader.ListMicroflows(); err == nil { + if mfs, err := ctx.Backend.ListMicroflows(); err == nil { for _, mf := range mfs { if moduleContainers[mf.ContainerID] { - if err := e.writer.DeleteMicroflow(mf.ID); err != nil { + if err := ctx.Backend.DeleteMicroflow(mf.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete microflow %s: %v\n", mf.Name, err) } else { nMicroflows++ @@ -140,10 +138,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete nanoflows in this module - if nfs, err := e.reader.ListNanoflows(); err == nil { + if nfs, err := ctx.Backend.ListNanoflows(); err == nil { for _, nf := range nfs { if moduleContainers[nf.ContainerID] { - if err := e.writer.DeleteNanoflow(nf.ID); err != nil { + if err := ctx.Backend.DeleteNanoflow(nf.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete nanoflow %s: %v\n", nf.Name, err) } else { nNanoflows++ @@ -153,10 +151,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete pages in this module - if pages, err := e.reader.ListPages(); err == nil { + if pages, err := ctx.Backend.ListPages(); err == nil { for _, page := range pages { if moduleContainers[page.ContainerID] { - if err := e.writer.DeletePage(page.ID); err != nil { + if err := ctx.Backend.DeletePage(page.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete page %s: %v\n", page.Name, err) } else { nPages++ @@ -166,10 +164,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete snippets in this module - if snippets, err := e.reader.ListSnippets(); err == nil { + if snippets, err := ctx.Backend.ListSnippets(); err == nil { for _, snippet := range snippets { if moduleContainers[snippet.ContainerID] { - if err := e.writer.DeleteSnippet(snippet.ID); err != nil { + if err := ctx.Backend.DeleteSnippet(snippet.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete snippet %s: %v\n", snippet.Name, err) } else { nSnippets++ @@ -179,10 +177,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete constants in this module - if constants, err := e.reader.ListConstants(); err == nil { + if constants, err := ctx.Backend.ListConstants(); err == nil { for _, c := range constants { if moduleContainers[c.ContainerID] { - if err := e.writer.DeleteConstant(c.ID); err != nil { + if err := ctx.Backend.DeleteConstant(c.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete constant %s: %v\n", c.Name, err) } else { nConstants++ @@ -192,10 +190,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete layouts in this module - if layouts, err := e.reader.ListLayouts(); err == nil { + if layouts, err := ctx.Backend.ListLayouts(); err == nil { for _, l := range layouts { if moduleContainers[l.ContainerID] { - if err := e.writer.DeleteLayout(l.ID); err != nil { + if err := ctx.Backend.DeleteLayout(l.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete layout %s: %v\n", l.Name, err) } else { nLayouts++ @@ -205,10 +203,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete Java actions in this module - if jas, err := e.reader.ListJavaActions(); err == nil { + if jas, err := ctx.Backend.ListJavaActions(); err == nil { for _, ja := range jas { if moduleContainers[ja.ContainerID] { - if err := e.writer.DeleteJavaAction(ja.ID); err != nil { + if err := ctx.Backend.DeleteJavaAction(ja.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete Java action %s: %v\n", ja.Name, err) } else { nJavaActions++ @@ -218,10 +216,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete business event services in this module - if services, err := e.reader.ListBusinessEventServices(); err == nil { + if services, err := ctx.Backend.ListBusinessEventServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { - if err := e.writer.DeleteBusinessEventService(svc.ID); err != nil { + if err := ctx.Backend.DeleteBusinessEventService(svc.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete business event service %s: %v\n", svc.Name, err) } else { nBizEvents++ @@ -231,10 +229,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete database connections in this module - if conns, err := e.reader.ListDatabaseConnections(); err == nil { + if conns, err := ctx.Backend.ListDatabaseConnections(); err == nil { for _, conn := range conns { if moduleContainers[conn.ContainerID] { - if err := e.writer.DeleteDatabaseConnection(conn.ID); err != nil { + if err := ctx.Backend.DeleteDatabaseConnection(conn.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete database connection %s: %v\n", conn.Name, err) } else { nDbConns++ @@ -244,10 +242,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete consumed OData services (clients) in this module - if services, err := e.reader.ListConsumedODataServices(); err == nil { + if services, err := ctx.Backend.ListConsumedODataServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { - if err := e.writer.DeleteConsumedODataService(svc.ID); err != nil { + if err := ctx.Backend.DeleteConsumedODataService(svc.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete OData client %s: %v\n", svc.Name, err) } else { nServices++ @@ -257,10 +255,10 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete published OData services in this module - if services, err := e.reader.ListPublishedODataServices(); err == nil { + if services, err := ctx.Backend.ListPublishedODataServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { - if err := e.writer.DeletePublishedODataService(svc.ID); err != nil { + if err := ctx.Backend.DeletePublishedODataService(svc.ID); err != nil { fmt.Fprintf(ctx.Output, "Warning: failed to delete OData service %s: %v\n", svc.Name, err) } else { nServices++ @@ -270,11 +268,11 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Remove module roles from user roles in ProjectSecurity - if ms, err := e.reader.GetModuleSecurity(targetModule.ID); err == nil { - if ps, err := e.reader.GetProjectSecurity(); err == nil { + if ms, err := ctx.Backend.GetModuleSecurity(targetModule.ID); err == nil { + if ps, err := ctx.Backend.GetProjectSecurity(); err == nil { for _, mr := range ms.ModuleRoles { qualifiedRole := s.Name + "." + mr.Name - if n, err := e.writer.RemoveModuleRoleFromAllUserRoles(ps.ID, qualifiedRole); err == nil && n > 0 { + if n, err := ctx.Backend.RemoveModuleRoleFromAllUserRoles(ps.ID, qualifiedRole); err == nil && n > 0 { fmt.Fprintf(ctx.Output, "Removed %s from %d user role(s)\n", qualifiedRole, n) } } @@ -282,7 +280,7 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { } // Delete the module itself (and clean up themesource directory) - if err := e.writer.DeleteModuleWithCleanup(targetModule.ID, s.Name); err != nil { + if err := ctx.Backend.DeleteModuleWithCleanup(targetModule.ID, s.Name); err != nil { return mdlerrors.NewBackend("delete module", err) } @@ -341,12 +339,11 @@ func execDropModule(ctx *ExecContext, s *ast.DropModuleStmt) error { // getModuleContainers returns a set of all container IDs that belong to a module // (including nested folders). func getModuleContainers(ctx *ExecContext, moduleID model.ID) map[model.ID]bool { - e := ctx.executor containers := make(map[model.ID]bool) containers[moduleID] = true // Build parent -> children map from units - units, err := e.reader.ListUnits() + units, err := ctx.Backend.ListUnits() if err != nil { return containers } @@ -357,7 +354,7 @@ func getModuleContainers(ctx *ExecContext, moduleID model.ID) map[model.ID]bool } // Also include folders - folders, _ := e.reader.ListFolders() + folders, _ := ctx.Backend.ListFolders() for _, f := range folders { childrenOf[f.ContainerID] = append(childrenOf[f.ContainerID], f.ID) } @@ -380,7 +377,6 @@ func getModuleContainers(ctx *ExecContext, moduleID model.ID) map[model.ID]bool // showModules handles SHOW MODULES command. func showModules(ctx *ExecContext) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -399,7 +395,7 @@ func showModules(ctx *ExecContext) error { } // Get units for type-based counting - units, err := e.reader.ListUnits() + units, err := ctx.Backend.ListUnits() if err != nil { return mdlerrors.NewBackend("list units", err) } @@ -440,7 +436,7 @@ func showModules(ctx *ExecContext) error { } // Count entities from domain models - if dms, err := e.reader.ListDomainModels(); err == nil { + if dms, err := ctx.Backend.ListDomainModels(); err == nil { for _, dm := range dms { modID := h.FindModuleID(dm.ContainerID) entityCounts[modID] += len(dm.Entities) @@ -448,7 +444,7 @@ func showModules(ctx *ExecContext) error { } // Count enumerations - if enums, err := e.reader.ListEnumerations(); err == nil { + if enums, err := ctx.Backend.ListEnumerations(); err == nil { for _, enum := range enums { modID := h.FindModuleID(enum.ContainerID) enumCounts[modID]++ @@ -456,7 +452,7 @@ func showModules(ctx *ExecContext) error { } // Count pages - if pages, err := e.reader.ListPages(); err == nil { + if pages, err := ctx.Backend.ListPages(); err == nil { for _, p := range pages { modID := h.FindModuleID(p.ContainerID) pageCounts[modID]++ @@ -464,7 +460,7 @@ func showModules(ctx *ExecContext) error { } // Count snippets - if snippets, err := e.reader.ListSnippets(); err == nil { + if snippets, err := ctx.Backend.ListSnippets(); err == nil { for _, s := range snippets { modID := h.FindModuleID(s.ContainerID) snippetCounts[modID]++ @@ -472,7 +468,7 @@ func showModules(ctx *ExecContext) error { } // Count microflows - if mfs, err := e.reader.ListMicroflows(); err == nil { + if mfs, err := ctx.Backend.ListMicroflows(); err == nil { for _, mf := range mfs { modID := h.FindModuleID(mf.ContainerID) microflowCounts[modID]++ @@ -480,7 +476,7 @@ func showModules(ctx *ExecContext) error { } // Count nanoflows - if nfs, err := e.reader.ListNanoflows(); err == nil { + if nfs, err := ctx.Backend.ListNanoflows(); err == nil { for _, nf := range nfs { modID := h.FindModuleID(nf.ContainerID) nanoflowCounts[modID]++ @@ -488,7 +484,7 @@ func showModules(ctx *ExecContext) error { } // Count constants - if constants, err := e.reader.ListConstants(); err == nil { + if constants, err := ctx.Backend.ListConstants(); err == nil { for _, c := range constants { modID := h.FindModuleID(c.ContainerID) constantCounts[modID]++ @@ -496,7 +492,7 @@ func showModules(ctx *ExecContext) error { } // Count Java actions - if jas, err := e.reader.ListJavaActions(); err == nil { + if jas, err := ctx.Backend.ListJavaActions(); err == nil { for _, ja := range jas { modID := h.FindModuleID(ja.ContainerID) javaActionCounts[modID]++ @@ -574,13 +570,12 @@ func showModules(ctx *ExecContext) error { // describeModule handles DESCRIBE MODULE [WITH ALL] command. func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } // Find the module - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return mdlerrors.NewBackend("list modules", err) } @@ -612,7 +607,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { fmt.Fprintln(ctx.Output) // Output enumerations in this module (no dependencies between enums) - if enums, err := e.reader.ListEnumerations(); err == nil { + if enums, err := ctx.Backend.ListEnumerations(); err == nil { for _, enum := range enums { if moduleContainers[enum.ContainerID] { if err := describeEnumeration(ctx, ast.QualifiedName{Module: moduleName, Name: enum.Name}); err == nil { @@ -623,7 +618,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output constants (may reference enumerations) - if constants, err := e.reader.ListConstants(); err == nil { + if constants, err := ctx.Backend.ListConstants(); err == nil { for _, c := range constants { if moduleContainers[c.ContainerID] { if err := outputConstantMDL(ctx, c, moduleName); err == nil { @@ -635,7 +630,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { // Output entities in dependency order (base entities before derived entities) // and associations after all entities - if dm, err := e.reader.GetDomainModel(targetModule.ID); err == nil { + if dm, err := ctx.Backend.GetDomainModel(targetModule.ID); err == nil { // Topologically sort entities by generalization (inheritance) sortedEntities := sortEntitiesByGeneralization(dm.Entities, moduleName) @@ -655,7 +650,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output microflows - if mfs, err := e.reader.ListMicroflows(); err == nil { + if mfs, err := ctx.Backend.ListMicroflows(); err == nil { for _, mf := range mfs { if moduleContainers[mf.ContainerID] { if err := describeMicroflow(ctx, ast.QualifiedName{Module: moduleName, Name: mf.Name}); err == nil { @@ -666,7 +661,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output java actions - if jaList, err := e.reader.ListJavaActions(); err == nil { + if jaList, err := ctx.Backend.ListJavaActions(); err == nil { for _, ja := range jaList { if moduleContainers[ja.ContainerID] { if err := describeJavaAction(ctx, ast.QualifiedName{Module: moduleName, Name: ja.Name}); err == nil { @@ -677,7 +672,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output pages - if pageList, err := e.reader.ListPages(); err == nil { + if pageList, err := ctx.Backend.ListPages(); err == nil { for _, p := range pageList { if moduleContainers[p.ContainerID] { if err := describePage(ctx, ast.QualifiedName{Module: moduleName, Name: p.Name}); err == nil { @@ -688,7 +683,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output snippets - if snippets, err := e.reader.ListSnippets(); err == nil { + if snippets, err := ctx.Backend.ListSnippets(); err == nil { for _, s := range snippets { if moduleContainers[s.ContainerID] { if err := describeSnippet(ctx, ast.QualifiedName{Module: moduleName, Name: s.Name}); err == nil { @@ -699,7 +694,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output layouts - if layouts, err := e.reader.ListLayouts(); err == nil { + if layouts, err := ctx.Backend.ListLayouts(); err == nil { for _, l := range layouts { if moduleContainers[l.ContainerID] { if err := describeLayout(ctx, ast.QualifiedName{Module: moduleName, Name: l.Name}); err == nil { @@ -710,7 +705,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output database connections - if conns, err := e.reader.ListDatabaseConnections(); err == nil { + if conns, err := ctx.Backend.ListDatabaseConnections(); err == nil { for _, conn := range conns { if moduleContainers[conn.ContainerID] { if err := outputDatabaseConnectionMDL(ctx, conn, moduleName); err == nil { @@ -721,7 +716,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output business event services - if services, err := e.reader.ListBusinessEventServices(); err == nil { + if services, err := ctx.Backend.ListBusinessEventServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { if err := describeBusinessEventService(ctx, ast.QualifiedName{Module: moduleName, Name: svc.Name}); err == nil { @@ -735,7 +730,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { h, _ := getHierarchy(ctx) // Output consumed OData services (clients) - if services, err := e.reader.ListConsumedODataServices(); err == nil { + if services, err := ctx.Backend.ListConsumedODataServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { folderPath := "" @@ -750,7 +745,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } // Output published OData services - if services, err := e.reader.ListPublishedODataServices(); err == nil { + if services, err := ctx.Backend.ListPublishedODataServices(); err == nil { for _, svc := range services { if moduleContainers[svc.ContainerID] { folderPath := "" @@ -768,7 +763,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { // Model / KnowledgeBase / ConsumedMCPService first (leaves), Agent last // (may reference the others via its TOOL / KNOWLEDGE BASE / MCP SERVICE // blocks). - if models, err := e.reader.ListAgentEditorModels(); err == nil { + if models, err := ctx.Backend.ListAgentEditorModels(); err == nil { for _, m := range models { if moduleContainers[m.ContainerID] { if err := describeAgentEditorModel(ctx, ast.QualifiedName{Module: moduleName, Name: m.Name}); err == nil { @@ -777,7 +772,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } } } - if kbs, err := e.reader.ListAgentEditorKnowledgeBases(); err == nil { + if kbs, err := ctx.Backend.ListAgentEditorKnowledgeBases(); err == nil { for _, kb := range kbs { if moduleContainers[kb.ContainerID] { if err := describeAgentEditorKnowledgeBase(ctx, ast.QualifiedName{Module: moduleName, Name: kb.Name}); err == nil { @@ -786,7 +781,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } } } - if svcs, err := e.reader.ListAgentEditorConsumedMCPServices(); err == nil { + if svcs, err := ctx.Backend.ListAgentEditorConsumedMCPServices(); err == nil { for _, svc := range svcs { if moduleContainers[svc.ContainerID] { if err := describeAgentEditorConsumedMCPService(ctx, ast.QualifiedName{Module: moduleName, Name: svc.Name}); err == nil { @@ -795,7 +790,7 @@ func describeModule(ctx *ExecContext, moduleName string, withAll bool) error { } } } - if agents, err := e.reader.ListAgentEditorAgents(); err == nil { + if agents, err := ctx.Backend.ListAgentEditorAgents(); err == nil { for _, a := range agents { if moduleContainers[a.ContainerID] { if err := describeAgentEditorAgent(ctx, ast.QualifiedName{Module: moduleName, Name: a.Name}); err == nil { diff --git a/mdl/executor/cmd_move.go b/mdl/executor/cmd_move.go index b8fd679d..8183b14b 100644 --- a/mdl/executor/cmd_move.go +++ b/mdl/executor/cmd_move.go @@ -97,10 +97,9 @@ func execMove(ctx *ExecContext, s *ast.MoveStmt) error { // updateQualifiedNameRefs updates all BY_NAME references to an element after a cross-module move. func updateQualifiedNameRefs(ctx *ExecContext, name ast.QualifiedName, newModule string) error { - e := ctx.executor oldQN := name.String() // "OldModule.ElementName" newQN := newModule + "." + name.Name // "NewModule.ElementName" - updated, err := e.writer.UpdateQualifiedNameInAllUnits(oldQN, newQN) + updated, err := ctx.Backend.UpdateQualifiedNameInAllUnits(oldQN, newQN) if err != nil { return mdlerrors.NewBackend("update references", err) } @@ -112,9 +111,8 @@ func updateQualifiedNameRefs(ctx *ExecContext, name ast.QualifiedName, newModule // movePage moves a page to a new container. func movePage(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor // Find the page - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -130,7 +128,7 @@ func movePage(ctx *ExecContext, name ast.QualifiedName, targetContainerID model. if modName == name.Module && p.Name == name.Name { // Update container ID and move the unit p.ContainerID = targetContainerID - if err := e.writer.MovePage(p); err != nil { + if err := ctx.Backend.MovePage(p); err != nil { return mdlerrors.NewBackend("move page", err) } fmt.Fprintf(ctx.Output, "Moved page %s to new location\n", name.String()) @@ -143,9 +141,8 @@ func movePage(ctx *ExecContext, name ast.QualifiedName, targetContainerID model. // moveMicroflow moves a microflow to a new container. func moveMicroflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor // Find the microflow - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -161,7 +158,7 @@ func moveMicroflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID m if modName == name.Module && mf.Name == name.Name { // Update container ID and move the unit mf.ContainerID = targetContainerID - if err := e.writer.MoveMicroflow(mf); err != nil { + if err := ctx.Backend.MoveMicroflow(mf); err != nil { return mdlerrors.NewBackend("move microflow", err) } fmt.Fprintf(ctx.Output, "Moved microflow %s to new location\n", name.String()) @@ -174,9 +171,8 @@ func moveMicroflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID m // moveSnippet moves a snippet to a new container. func moveSnippet(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor // Find the snippet - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } @@ -192,7 +188,7 @@ func moveSnippet(ctx *ExecContext, name ast.QualifiedName, targetContainerID mod if modName == name.Module && s.Name == name.Name { // Update container ID and move the unit s.ContainerID = targetContainerID - if err := e.writer.MoveSnippet(s); err != nil { + if err := ctx.Backend.MoveSnippet(s); err != nil { return mdlerrors.NewBackend("move snippet", err) } fmt.Fprintf(ctx.Output, "Moved snippet %s to new location\n", name.String()) @@ -205,9 +201,8 @@ func moveSnippet(ctx *ExecContext, name ast.QualifiedName, targetContainerID mod // moveNanoflow moves a nanoflow to a new container. func moveNanoflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor // Find the nanoflow - nfs, err := e.reader.ListNanoflows() + nfs, err := ctx.Backend.ListNanoflows() if err != nil { return mdlerrors.NewBackend("list nanoflows", err) } @@ -223,7 +218,7 @@ func moveNanoflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID mo if modName == name.Module && nf.Name == name.Name { // Update container ID and move the unit nf.ContainerID = targetContainerID - if err := e.writer.MoveNanoflow(nf); err != nil { + if err := ctx.Backend.MoveNanoflow(nf); err != nil { return mdlerrors.NewBackend("move nanoflow", err) } fmt.Fprintf(ctx.Output, "Moved nanoflow %s to new location\n", name.String()) @@ -239,9 +234,8 @@ func moveNanoflow(ctx *ExecContext, name ast.QualifiedName, targetContainerID mo // Associations referencing the entity are converted to CrossAssociations. // ViewEntitySourceDocuments for view entities are also moved. func moveEntity(ctx *ExecContext, name ast.QualifiedName, sourceModule, targetModule *model.Module) error { - e := ctx.executor // Get source domain model - sourceDM, err := e.reader.GetDomainModel(sourceModule.ID) + sourceDM, err := ctx.Backend.GetDomainModel(sourceModule.ID) if err != nil { return mdlerrors.NewBackend("get source domain model", err) } @@ -259,13 +253,13 @@ func moveEntity(ctx *ExecContext, name ast.QualifiedName, sourceModule, targetMo } // Get target domain model - targetDM, err := e.reader.GetDomainModel(targetModule.ID) + targetDM, err := ctx.Backend.GetDomainModel(targetModule.ID) if err != nil { return mdlerrors.NewBackend("get target domain model", err) } // Move entity via writer (converts associations to CrossAssociations, updates validation rule refs) - convertedAssocs, err := e.writer.MoveEntity(entity, sourceDM.ID, targetDM.ID, sourceModule.Name, targetModule.Name) + convertedAssocs, err := ctx.Backend.MoveEntity(entity, sourceDM.ID, targetDM.ID, sourceModule.Name, targetModule.Name) if err != nil { return mdlerrors.NewBackend("move entity", err) } @@ -275,7 +269,7 @@ func moveEntity(ctx *ExecContext, name ast.QualifiedName, sourceModule, targetMo // The SourceDocumentRef was already updated by MoveEntity to use the new module name. // Extract the original doc name (before the module prefix was changed). docName := name.Name // ViewEntitySourceDocument name matches the entity name - if err := e.writer.MoveViewEntitySourceDocument(sourceModule.Name, targetModule.ID, docName); err != nil { + if err := ctx.Backend.MoveViewEntitySourceDocument(sourceModule.Name, targetModule.ID, docName); err != nil { fmt.Fprintf(ctx.Output, "Warning: Could not move ViewEntitySourceDocument: %v\n", err) } } @@ -283,7 +277,7 @@ func moveEntity(ctx *ExecContext, name ast.QualifiedName, sourceModule, targetMo // Update OQL queries in all ViewEntitySourceDocuments that reference the moved entity oldQualifiedName := name.String() // e.g., "DmTest.Customer" newQualifiedName := targetModule.Name + "." + name.Name // e.g., "DmTest2.Customer" - if oqlUpdated, err := e.writer.UpdateOqlQueriesForMovedEntity(oldQualifiedName, newQualifiedName); err != nil { + if oqlUpdated, err := ctx.Backend.UpdateOqlQueriesForMovedEntity(oldQualifiedName, newQualifiedName); err != nil { fmt.Fprintf(ctx.Output, "Warning: Could not update OQL queries: %v\n", err) } else if oqlUpdated > 0 { fmt.Fprintf(ctx.Output, "Updated %d OQL query(ies) referencing %s\n", oqlUpdated, oldQualifiedName) @@ -302,7 +296,6 @@ func moveEntity(ctx *ExecContext, name ast.QualifiedName, sourceModule, targetMo // moveEnumeration moves an enumeration to a new container. // For cross-module moves, updates all EnumerationAttributeType references across all domain models. func moveEnumeration(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID, targetModuleName string) error { - e := ctx.executor enum := findEnumeration(ctx, name.Module, name.Name) if enum == nil { return mdlerrors.NewNotFound("enumeration", name.String()) @@ -310,14 +303,14 @@ func moveEnumeration(ctx *ExecContext, name ast.QualifiedName, targetContainerID oldQualifiedName := name.String() // e.g., "DmTest.Country" enum.ContainerID = targetContainerID - if err := e.writer.MoveEnumeration(enum); err != nil { + if err := ctx.Backend.MoveEnumeration(enum); err != nil { return mdlerrors.NewBackend("move enumeration", err) } // For cross-module moves, update enumeration references in all domain models if targetModuleName != "" && targetModuleName != name.Module { newQualifiedName := targetModuleName + "." + name.Name - if err := e.writer.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName); err != nil { + if err := ctx.Backend.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName); err != nil { fmt.Fprintf(ctx.Output, "Warning: Could not update enumeration references: %v\n", err) } else { fmt.Fprintf(ctx.Output, "Updated enumeration references: %s -> %s\n", oldQualifiedName, newQualifiedName) @@ -330,8 +323,7 @@ func moveEnumeration(ctx *ExecContext, name ast.QualifiedName, targetContainerID // moveConstant moves a constant to a new container. func moveConstant(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor - constants, err := e.reader.ListConstants() + constants, err := ctx.Backend.ListConstants() if err != nil { return mdlerrors.NewBackend("list constants", err) } @@ -346,7 +338,7 @@ func moveConstant(ctx *ExecContext, name ast.QualifiedName, targetContainerID mo modName := h.GetModuleName(modID) if modName == name.Module && c.Name == name.Name { c.ContainerID = targetContainerID - if err := e.writer.MoveConstant(c); err != nil { + if err := ctx.Backend.MoveConstant(c); err != nil { return mdlerrors.NewBackend("move constant", err) } fmt.Fprintf(ctx.Output, "Moved constant %s to new location\n", name.String()) @@ -359,8 +351,7 @@ func moveConstant(ctx *ExecContext, name ast.QualifiedName, targetContainerID mo // moveDatabaseConnection moves a database connection to a new container. func moveDatabaseConnection(ctx *ExecContext, name ast.QualifiedName, targetContainerID model.ID) error { - e := ctx.executor - connections, err := e.reader.ListDatabaseConnections() + connections, err := ctx.Backend.ListDatabaseConnections() if err != nil { return mdlerrors.NewBackend("list database connections", err) } @@ -375,7 +366,7 @@ func moveDatabaseConnection(ctx *ExecContext, name ast.QualifiedName, targetCont modName := h.GetModuleName(modID) if modName == name.Module && conn.Name == name.Name { conn.ContainerID = targetContainerID - if err := e.writer.MoveDatabaseConnection(conn); err != nil { + if err := ctx.Backend.MoveDatabaseConnection(conn); err != nil { return mdlerrors.NewBackend("move database connection", err) } fmt.Fprintf(ctx.Output, "Moved database connection %s to new location\n", name.String()) diff --git a/mdl/executor/cmd_navigation.go b/mdl/executor/cmd_navigation.go index ebe9ea53..912db18f 100644 --- a/mdl/executor/cmd_navigation.go +++ b/mdl/executor/cmd_navigation.go @@ -15,12 +15,11 @@ import ( // execAlterNavigation handles CREATE [OR REPLACE] NAVIGATION command. // It fully replaces the profile's home pages, login page, not-found page, and menu tree. func execAlterNavigation(ctx *ExecContext, s *ast.AlterNavigationStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - nav, err := e.reader.GetNavigation() + nav, err := ctx.Backend.GetNavigation() if err != nil { return mdlerrors.NewBackend("get navigation", err) } @@ -65,7 +64,7 @@ func execAlterNavigation(ctx *ExecContext, s *ast.AlterNavigationStmt) error { spec.MenuItems = append(spec.MenuItems, convertMenuItemDef(mi)) } - if err := e.writer.UpdateNavigationProfile(nav.ID, s.ProfileName, spec); err != nil { + if err := ctx.Backend.UpdateNavigationProfile(nav.ID, s.ProfileName, spec); err != nil { return mdlerrors.NewBackend("update navigation profile", err) } @@ -102,8 +101,7 @@ func profileNames(nav *mpr.NavigationDocument) string { // showNavigation handles SHOW NAVIGATION command. // Displays an overview of all navigation profiles with their home pages and menu item counts. func showNavigation(ctx *ExecContext) error { - e := ctx.executor - nav, err := e.reader.GetNavigation() + nav, err := ctx.Backend.GetNavigation() if err != nil { return mdlerrors.NewBackend("get navigation", err) } @@ -161,8 +159,7 @@ func showNavigation(ctx *ExecContext) error { // showNavigationMenu handles SHOW NAVIGATION MENU [profile] command. // Displays the menu tree for a specific profile, or all profiles if none specified. func showNavigationMenu(ctx *ExecContext, profileName *ast.QualifiedName) error { - e := ctx.executor - nav, err := e.reader.GetNavigation() + nav, err := ctx.Backend.GetNavigation() if err != nil { return mdlerrors.NewBackend("get navigation", err) } @@ -187,8 +184,7 @@ func showNavigationMenu(ctx *ExecContext, profileName *ast.QualifiedName) error // showNavigationHomes handles SHOW NAVIGATION HOMES command. // Displays all home page configurations including role-based overrides. func showNavigationHomes(ctx *ExecContext) error { - e := ctx.executor - nav, err := e.reader.GetNavigation() + nav, err := ctx.Backend.GetNavigation() if err != nil { return mdlerrors.NewBackend("get navigation", err) } @@ -230,8 +226,7 @@ func showNavigationHomes(ctx *ExecContext) error { // describeNavigation handles DESCRIBE NAVIGATION [profile] command. // Outputs a complete MDL-style description of a navigation profile. func describeNavigation(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - nav, err := e.reader.GetNavigation() + nav, err := ctx.Backend.GetNavigation() if err != nil { return mdlerrors.NewBackend("get navigation", err) } diff --git a/mdl/executor/cmd_odata.go b/mdl/executor/cmd_odata.go index c922adbc..69c22c78 100644 --- a/mdl/executor/cmd_odata.go +++ b/mdl/executor/cmd_odata.go @@ -36,9 +36,8 @@ func outputJavadocIndented(w io.Writer, text string, indent string) { // showODataClients handles SHOW ODATA CLIENTS [IN module] command. func showODataClients(ctx *ExecContext, moduleName string) error { - e := ctx.executor - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return mdlerrors.NewBackend("list consumed OData services", err) } @@ -101,9 +100,8 @@ func showODataClients(ctx *ExecContext, moduleName string) error { // describeODataClient handles DESCRIBE ODATA CLIENT command. func describeODataClient(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return mdlerrors.NewBackend("list consumed OData services", err) } @@ -220,9 +218,8 @@ func outputConsumedODataServiceMDL(ctx *ExecContext, svc *model.ConsumedODataSer // showODataServices handles SHOW ODATA SERVICES [IN module] command. func showODataServices(ctx *ExecContext, moduleName string) error { - e := ctx.executor - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -282,9 +279,8 @@ func showODataServices(ctx *ExecContext, moduleName string) error { // describeODataService handles DESCRIBE ODATA SERVICE command. func describeODataService(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -460,9 +456,8 @@ func outputPublishedODataServiceMDL(ctx *ExecContext, svc *model.PublishedODataS // showExternalEntities handles SHOW EXTERNAL ENTITIES [IN module] command. func showExternalEntities(ctx *ExecContext, moduleName string) error { - e := ctx.executor - domainModels, err := e.reader.ListDomainModels() + domainModels, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } @@ -528,13 +523,12 @@ func showExternalEntities(ctx *ExecContext, moduleName string) error { // It scans all microflows and nanoflows for CallExternalAction activities // and displays the unique actions grouped by consumed OData service. func showExternalActions(ctx *ExecContext, moduleName string) error { - e := ctx.executor - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } - nfs, err := e.reader.ListNanoflows() + nfs, err := ctx.Backend.ListNanoflows() if err != nil { return mdlerrors.NewBackend("list nanoflows", err) } @@ -661,9 +655,8 @@ func showExternalActions(ctx *ExecContext, moduleName string) error { // describeExternalEntity handles DESCRIBE EXTERNAL ENTITY command. func describeExternalEntity(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - domainModels, err := e.reader.ListDomainModels() + domainModels, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } @@ -755,7 +748,6 @@ func outputExternalEntityMDL(ctx *ExecContext, entity *domainmodel.Entity, modul // execCreateExternalEntity handles CREATE [OR MODIFY] EXTERNAL ENTITY statements. func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() @@ -772,7 +764,7 @@ func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) } // Get domain model - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -820,7 +812,7 @@ func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) if s.Documentation != "" { existingEntity.Documentation = s.Documentation } - if err := e.writer.UpdateEntity(dm.ID, existingEntity); err != nil { + if err := ctx.Backend.UpdateEntity(dm.ID, existingEntity); err != nil { return mdlerrors.NewBackend("update external entity", err) } fmt.Fprintf(ctx.Output, "Modified external entity: %s.%s\n", s.Name.Module, s.Name.Name) @@ -847,7 +839,7 @@ func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) } newEntity.ID = model.ID(mpr.GenerateID()) - if err := e.writer.CreateEntity(dm.ID, newEntity); err != nil { + if err := ctx.Backend.CreateEntity(dm.ID, newEntity); err != nil { return mdlerrors.NewBackend("create external entity", err) } fmt.Fprintf(ctx.Output, "Created external entity: %s.%s\n", s.Name.Module, s.Name.Name) @@ -860,7 +852,6 @@ func execCreateExternalEntity(ctx *ExecContext, s *ast.CreateExternalEntityStmt) // createODataClient handles CREATE ODATA CLIENT command. func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() @@ -876,7 +867,7 @@ func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error } // Check if client already exists - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err == nil { h, _ := getHierarchy(ctx) for _, svc := range services { @@ -951,7 +942,7 @@ func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error } } } - if err := e.writer.UpdateConsumedODataService(svc); err != nil { + if err := ctx.Backend.UpdateConsumedODataService(svc); err != nil { return mdlerrors.NewBackend("update OData client", err) } invalidateHierarchy(ctx) @@ -1031,7 +1022,7 @@ func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error } } - if err := e.writer.CreateConsumedODataService(newSvc); err != nil { + if err := ctx.Backend.CreateConsumedODataService(newSvc); err != nil { return mdlerrors.NewBackend("create OData client", err) } invalidateHierarchy(ctx) @@ -1053,13 +1044,12 @@ func createODataClient(ctx *ExecContext, stmt *ast.CreateODataClientStmt) error // alterODataClient handles ALTER ODATA CLIENT command. func alterODataClient(ctx *ExecContext, stmt *ast.AlterODataClientStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return mdlerrors.NewBackend("list consumed OData services", err) } @@ -1130,7 +1120,7 @@ func alterODataClient(ctx *ExecContext, stmt *ast.AlterODataClientStmt) error { return mdlerrors.NewUnsupported(fmt.Sprintf("unknown OData client property: %s", key)) } } - if err := e.writer.UpdateConsumedODataService(svc); err != nil { + if err := ctx.Backend.UpdateConsumedODataService(svc); err != nil { return mdlerrors.NewBackend("alter OData client", err) } invalidateHierarchy(ctx) @@ -1144,13 +1134,12 @@ func alterODataClient(ctx *ExecContext, stmt *ast.AlterODataClientStmt) error { // dropODataClient handles DROP ODATA CLIENT command. func dropODataClient(ctx *ExecContext, stmt *ast.DropODataClientStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListConsumedODataServices() + services, err := ctx.Backend.ListConsumedODataServices() if err != nil { return mdlerrors.NewBackend("list consumed OData services", err) } @@ -1164,7 +1153,7 @@ func dropODataClient(ctx *ExecContext, stmt *ast.DropODataClientStmt) error { modID := h.FindModuleID(svc.ContainerID) modName := h.GetModuleName(modID) if strings.EqualFold(modName, stmt.Name.Module) && strings.EqualFold(svc.Name, stmt.Name.Name) { - if err := e.writer.DeleteConsumedODataService(svc.ID); err != nil { + if err := ctx.Backend.DeleteConsumedODataService(svc.ID); err != nil { return mdlerrors.NewBackend("drop OData client", err) } invalidateHierarchy(ctx) @@ -1178,7 +1167,6 @@ func dropODataClient(ctx *ExecContext, stmt *ast.DropODataClientStmt) error { // createODataService handles CREATE ODATA SERVICE command. func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() @@ -1194,7 +1182,7 @@ func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) erro } // Check if service already exists - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err == nil { h, _ := getHierarchy(ctx) for _, svc := range services { @@ -1228,7 +1216,7 @@ func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) erro if len(stmt.AuthenticationTypes) > 0 { svc.AuthenticationTypes = stmt.AuthenticationTypes } - if err := e.writer.UpdatePublishedODataService(svc); err != nil { + if err := ctx.Backend.UpdatePublishedODataService(svc); err != nil { return mdlerrors.NewBackend("update OData service", err) } invalidateHierarchy(ctx) @@ -1272,7 +1260,7 @@ func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) erro newSvc.EntitySets = append(newSvc.EntitySets, entitySet) } - if err := e.writer.CreatePublishedODataService(newSvc); err != nil { + if err := ctx.Backend.CreatePublishedODataService(newSvc); err != nil { return mdlerrors.NewBackend("create OData service", err) } invalidateHierarchy(ctx) @@ -1282,13 +1270,12 @@ func createODataService(ctx *ExecContext, stmt *ast.CreateODataServiceStmt) erro // alterODataService handles ALTER ODATA SERVICE command. func alterODataService(ctx *ExecContext, stmt *ast.AlterODataServiceStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -1325,7 +1312,7 @@ func alterODataService(ctx *ExecContext, stmt *ast.AlterODataServiceStmt) error return mdlerrors.NewUnsupported(fmt.Sprintf("unknown OData service property: %s", key)) } } - if err := e.writer.UpdatePublishedODataService(svc); err != nil { + if err := ctx.Backend.UpdatePublishedODataService(svc); err != nil { return mdlerrors.NewBackend("alter OData service", err) } invalidateHierarchy(ctx) @@ -1339,13 +1326,12 @@ func alterODataService(ctx *ExecContext, stmt *ast.AlterODataServiceStmt) error // dropODataService handles DROP ODATA SERVICE command. func dropODataService(ctx *ExecContext, stmt *ast.DropODataServiceStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -1359,7 +1345,7 @@ func dropODataService(ctx *ExecContext, stmt *ast.DropODataServiceStmt) error { modID := h.FindModuleID(svc.ContainerID) modName := h.GetModuleName(modID) if strings.EqualFold(modName, stmt.Name.Module) && strings.EqualFold(svc.Name, stmt.Name.Name) { - if err := e.writer.DeletePublishedODataService(svc.ID); err != nil { + if err := ctx.Backend.DeletePublishedODataService(svc.ID); err != nil { return mdlerrors.NewBackend("drop OData service", err) } invalidateHierarchy(ctx) diff --git a/mdl/executor/cmd_page_wireframe.go b/mdl/executor/cmd_page_wireframe.go index 6744bdbd..6b88fa4a 100644 --- a/mdl/executor/cmd_page_wireframe.go +++ b/mdl/executor/cmd_page_wireframe.go @@ -90,7 +90,6 @@ func (c *wireframeCounter) next() string { // PageWireframeJSON generates wireframe JSON for a page. func PageWireframeJSON(ctx *ExecContext, name string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -108,7 +107,7 @@ func PageWireframeJSON(ctx *ExecContext, name string) error { } // Find the page - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -144,7 +143,7 @@ func PageWireframeJSON(ctx *ExecContext, name string) error { } layoutName := "" - rawData, _ := e.reader.GetRawUnit(foundPage.ID) + rawData, _ := ctx.Backend.GetRawUnit(foundPage.ID) if rawData != nil { if formCall, ok := rawData["FormCall"].(map[string]any); ok { if layoutID := extractBinaryID(formCall["Layout"]); layoutID != "" { @@ -204,7 +203,6 @@ func PageWireframeJSON(ctx *ExecContext, name string) error { // SnippetWireframeJSON generates wireframe JSON for a snippet. func SnippetWireframeJSON(ctx *ExecContext, name string) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -220,7 +218,7 @@ func SnippetWireframeJSON(ctx *ExecContext, name string) error { } qn := ast.QualifiedName{Module: parts[0], Name: parts[1]} - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } diff --git a/mdl/executor/cmd_pages_builder.go b/mdl/executor/cmd_pages_builder.go index c4ddd262..3de745ba 100644 --- a/mdl/executor/cmd_pages_builder.go +++ b/mdl/executor/cmd_pages_builder.go @@ -336,12 +336,11 @@ func (pb *pageBuilder) createFolder(name string, containerID model.ID) (model.ID // execDropPage handles DROP PAGE statement. func execDropPage(ctx *ExecContext, s *ast.DropPageStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -350,7 +349,7 @@ func execDropPage(ctx *ExecContext, s *ast.DropPageStmt) error { modID := getModuleID(ctx, p.ContainerID) modName := getModuleName(ctx, modID) if modName == s.Name.Module && p.Name == s.Name.Name { - if err := e.writer.DeletePage(p.ID); err != nil { + if err := ctx.Backend.DeletePage(p.ID); err != nil { return mdlerrors.NewBackend("delete page", err) } fmt.Fprintf(ctx.Output, "Dropped page %s\n", s.Name.String()) @@ -363,12 +362,11 @@ func execDropPage(ctx *ExecContext, s *ast.DropPageStmt) error { // execDropSnippet handles DROP SNIPPET statement. func execDropSnippet(ctx *ExecContext, s *ast.DropSnippetStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } @@ -377,7 +375,7 @@ func execDropSnippet(ctx *ExecContext, s *ast.DropSnippetStmt) error { modID := getModuleID(ctx, snip.ContainerID) modName := getModuleName(ctx, modID) if modName == s.Name.Module && snip.Name == s.Name.Name { - if err := e.writer.DeleteSnippet(snip.ID); err != nil { + if err := ctx.Backend.DeleteSnippet(snip.ID); err != nil { return mdlerrors.NewBackend("delete snippet", err) } fmt.Fprintf(ctx.Output, "Dropped snippet %s\n", s.Name.String()) diff --git a/mdl/executor/cmd_pages_create_v3.go b/mdl/executor/cmd_pages_create_v3.go index addaf11f..e993ceef 100644 --- a/mdl/executor/cmd_pages_create_v3.go +++ b/mdl/executor/cmd_pages_create_v3.go @@ -38,7 +38,7 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { moduleID := module.ID // Check if page already exists - collect ALL duplicates - existingPages, _ := e.reader.ListPages() + existingPages, _ := ctx.Backend.ListPages() var pagesToDelete []model.ID for _, p := range existingPages { modID := getModuleID(ctx, p.ContainerID) @@ -60,8 +60,8 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { widgetScope: make(map[string]model.ID), paramScope: make(map[string]model.ID), paramEntityNames: make(map[string]string), - execCache: e.cache, - fragments: e.fragments, + execCache: ctx.Cache, + fragments: ctx.Fragments, themeRegistry: e.getThemeRegistry(), } @@ -74,17 +74,17 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { if len(pagesToDelete) > 0 { // Reuse first existing page's UUID to avoid git delete+add (which crashes Studio Pro RevStatusCache) page.ID = pagesToDelete[0] - if err := e.writer.UpdatePage(page); err != nil { + if err := ctx.Backend.UpdatePage(page); err != nil { return mdlerrors.NewBackend("update page", err) } // Delete any additional duplicates for _, id := range pagesToDelete[1:] { - if err := e.writer.DeletePage(id); err != nil { + if err := ctx.Backend.DeletePage(id); err != nil { return mdlerrors.NewBackend("delete duplicate page", err) } } } else { - if err := e.writer.CreatePage(page); err != nil { + if err := ctx.Backend.CreatePage(page); err != nil { return mdlerrors.NewBackend("create page", err) } } @@ -114,7 +114,7 @@ func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error { moduleID := module.ID // Check if snippet already exists - collect ALL duplicates - existingSnippets, _ := e.reader.ListSnippets() + existingSnippets, _ := ctx.Backend.ListSnippets() var snippetsToDelete []model.ID for _, snip := range existingSnippets { modID := getModuleID(ctx, snip.ContainerID) @@ -136,8 +136,8 @@ func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error { widgetScope: make(map[string]model.ID), paramScope: make(map[string]model.ID), paramEntityNames: make(map[string]string), - execCache: e.cache, - fragments: e.fragments, + execCache: ctx.Cache, + fragments: ctx.Fragments, themeRegistry: e.getThemeRegistry(), } @@ -148,13 +148,13 @@ func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error { // Delete old snippets only after successful build for _, id := range snippetsToDelete { - if err := e.writer.DeleteSnippet(id); err != nil { + if err := ctx.Backend.DeleteSnippet(id); err != nil { return mdlerrors.NewBackend("delete existing snippet", err) } } // Create the snippet in the MPR - if err := e.writer.CreateSnippet(snippet); err != nil { + if err := ctx.Backend.CreateSnippet(snippet); err != nil { return mdlerrors.NewBackend("create snippet", err) } diff --git a/mdl/executor/cmd_pages_describe.go b/mdl/executor/cmd_pages_describe.go index ed0fd762..a62abd48 100644 --- a/mdl/executor/cmd_pages_describe.go +++ b/mdl/executor/cmd_pages_describe.go @@ -21,7 +21,6 @@ import ( // describePage handles DESCRIBE PAGE command - outputs MDL V3 syntax. func describePage(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -29,7 +28,7 @@ func describePage(ctx *ExecContext, name ast.QualifiedName) error { } // Find the page - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -76,7 +75,7 @@ func describePage(ctx *ExecContext, name ast.QualifiedName) error { // Get layout from raw data layoutName := "" - rawData, _ := e.reader.GetRawUnit(foundPage.ID) + rawData, _ := ctx.Backend.GetRawUnit(foundPage.ID) if rawData != nil { if formCall, ok := rawData["FormCall"].(map[string]any); ok { if layoutID := extractBinaryID(formCall["Layout"]); layoutID != "" { @@ -177,7 +176,6 @@ func formatParametersV3(params []string) []string { // describeSnippet handles DESCRIBE SNIPPET command - outputs MDL V3 syntax. func describeSnippet(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -185,7 +183,7 @@ func describeSnippet(ctx *ExecContext, name ast.QualifiedName) error { } // Find the snippet - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } @@ -219,7 +217,7 @@ func describeSnippet(ctx *ExecContext, name ast.QualifiedName) error { } // Get raw data to check for parameters - rawData, _ := e.reader.GetRawUnit(foundSnippet.ID) + rawData, _ := ctx.Backend.GetRawUnit(foundSnippet.ID) var params []map[string]any if rawData != nil { params = getBsonArrayMaps(rawData["Parameters"]) @@ -261,7 +259,6 @@ func describeSnippet(ctx *ExecContext, name ast.QualifiedName) error { // describeLayout handles DESCRIBE LAYOUT command - outputs MDL-style representation. func describeLayout(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -269,7 +266,7 @@ func describeLayout(ctx *ExecContext, name ast.QualifiedName) error { } // Find the layout - allLayouts, err := e.reader.ListLayouts() + allLayouts, err := ctx.Backend.ListLayouts() if err != nil { return mdlerrors.NewBackend("list layouts", err) } @@ -330,9 +327,8 @@ func describeLayout(ctx *ExecContext, name ast.QualifiedName) error { // getLayoutWidgetsFromRaw extracts widgets from raw layout BSON. func getLayoutWidgetsFromRaw(ctx *ExecContext, layoutID model.ID) []rawWidget { - e := ctx.executor // Get raw layout data - rawData, err := e.reader.GetRawUnit(layoutID) + rawData, err := ctx.Backend.GetRawUnit(layoutID) if err != nil { return nil } @@ -359,9 +355,8 @@ func outputWidgetMDLV3Comment(ctx *ExecContext, w rawWidget, indent int) { // getSnippetWidgetsFromRaw extracts widgets from raw snippet BSON. func getSnippetWidgetsFromRaw(ctx *ExecContext, snippetID model.ID) []rawWidget { - e := ctx.executor // Get raw snippet data - rawData, err := e.reader.GetRawUnit(snippetID) + rawData, err := ctx.Backend.GetRawUnit(snippetID) if err != nil { return nil } @@ -438,8 +433,7 @@ func getBsonArrayMaps(v any) []map[string]any { // resolveLayoutName resolves a layout ID to its qualified name. func resolveLayoutName(ctx *ExecContext, layoutID model.ID) string { - e := ctx.executor - layouts, err := e.reader.ListLayouts() + layouts, err := ctx.Backend.ListLayouts() if err != nil { return string(layoutID) } @@ -617,9 +611,8 @@ func getBsonArrayElements(v any) []any { // getPageWidgetsFromRaw extracts widgets from raw page BSON. func getPageWidgetsFromRaw(ctx *ExecContext, pageID model.ID) []rawWidget { - e := ctx.executor // Get raw page data - rawData, err := e.reader.GetRawUnit(pageID) + rawData, err := ctx.Backend.GetRawUnit(pageID) if err != nil { return nil } diff --git a/mdl/executor/cmd_pages_describe_output.go b/mdl/executor/cmd_pages_describe_output.go index e731dae9..7cf67d91 100644 --- a/mdl/executor/cmd_pages_describe_output.go +++ b/mdl/executor/cmd_pages_describe_output.go @@ -968,11 +968,10 @@ func extractButtonAction(ctx *ExecContext, w map[string]any) string { // getPageQualifiedName resolves a page ID to its qualified name. func getPageQualifiedName(ctx *ExecContext, pageID model.ID) string { - e := ctx.executor if pageID == "" { return "" } - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return "" } diff --git a/mdl/executor/cmd_pages_describe_parse.go b/mdl/executor/cmd_pages_describe_parse.go index dd9283b1..64c77c9e 100644 --- a/mdl/executor/cmd_pages_describe_parse.go +++ b/mdl/executor/cmd_pages_describe_parse.go @@ -666,7 +666,6 @@ func extractListViewDataSource(ctx *ExecContext, w map[string]any) *rawDataSourc // extractSnippetRef extracts the snippet reference from a SnippetCallWidget. func extractSnippetRef(ctx *ExecContext, w map[string]any) string { - e := ctx.executor // First try the FormCall.Form path (used for BY_NAME_REFERENCE) if formCall, ok := w["FormCall"].(map[string]any); ok { if form, ok := formCall["Form"].(string); ok && form != "" { @@ -675,12 +674,12 @@ func extractSnippetRef(ctx *ExecContext, w map[string]any) string { // Try binary ID and resolve to name if formID := extractBinaryID(formCall["Form"]); formID != "" { // Try to resolve the snippet name from ID - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err == nil { for _, s := range snippets { if string(s.ID) == formID { moduleName := "" - if modules, err := e.reader.ListModules(); err == nil { + if modules, err := ctx.Backend.ListModules(); err == nil { for _, m := range modules { if m.ID == s.ContainerID { moduleName = m.Name diff --git a/mdl/executor/cmd_pages_show.go b/mdl/executor/cmd_pages_show.go index cb56a1f1..e6542935 100644 --- a/mdl/executor/cmd_pages_show.go +++ b/mdl/executor/cmd_pages_show.go @@ -12,7 +12,6 @@ import ( // showPages handles SHOW PAGES command. func showPages(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -20,7 +19,7 @@ func showPages(ctx *ExecContext, moduleName string) error { } // Get all pages - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } diff --git a/mdl/executor/cmd_published_rest.go b/mdl/executor/cmd_published_rest.go index 9483ac90..9b44afb6 100644 --- a/mdl/executor/cmd_published_rest.go +++ b/mdl/executor/cmd_published_rest.go @@ -15,9 +15,8 @@ import ( // showPublishedRestServices handles SHOW PUBLISHED REST SERVICES [IN module] command. func showPublishedRestServices(ctx *ExecContext, moduleName string) error { - e := ctx.executor - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return mdlerrors.NewBackend("list published REST services", err) } @@ -79,9 +78,8 @@ func showPublishedRestServices(ctx *ExecContext, moduleName string) error { // describePublishedRestService handles DESCRIBE PUBLISHED REST SERVICE command. func describePublishedRestService(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return mdlerrors.NewBackend("list published REST services", err) } @@ -161,9 +159,8 @@ func describePublishedRestService(ctx *ExecContext, name ast.QualifiedName) erro // findPublishedRestService looks up a published REST service by module and name. func findPublishedRestService(ctx *ExecContext, moduleName, name string) (*model.PublishedRestService, error) { - e := ctx.executor - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return nil, err } @@ -183,8 +180,6 @@ func findPublishedRestService(ctx *ExecContext, moduleName, name string) (*model // execCreatePublishedRestService creates a new published REST service. func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRestServiceStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -203,7 +198,7 @@ func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRest return mdlerrors.NewBackend("find existing service", findErr) } if existing != nil { - if err := e.writer.DeletePublishedRestService(existing.ID); err != nil { + if err := ctx.Backend.DeletePublishedRestService(existing.ID); err != nil { return mdlerrors.NewBackend("replace existing service", err) } } @@ -248,11 +243,11 @@ func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRest svc.Resources = append(svc.Resources, resource) } - if err := e.writer.CreatePublishedRestService(svc); err != nil { + if err := ctx.Backend.CreatePublishedRestService(svc); err != nil { return mdlerrors.NewBackend("create published REST service", err) } - if !e.quiet { + if !ctx.Quiet { fmt.Fprintf(ctx.Output, "Created published REST service %s.%s\n", s.Name.Module, s.Name.Name) } return nil @@ -260,13 +255,11 @@ func execCreatePublishedRestService(ctx *ExecContext, s *ast.CreatePublishedRest // execDropPublishedRestService deletes a published REST service. func execDropPublishedRestService(ctx *ExecContext, s *ast.DropPublishedRestServiceStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return mdlerrors.NewBackend("list published REST services", err) } @@ -280,10 +273,10 @@ func execDropPublishedRestService(ctx *ExecContext, s *ast.DropPublishedRestServ modID := h.FindModuleID(svc.ContainerID) modName := h.GetModuleName(modID) if modName == s.Name.Module && svc.Name == s.Name.Name { - if err := e.writer.DeletePublishedRestService(svc.ID); err != nil { + if err := ctx.Backend.DeletePublishedRestService(svc.ID); err != nil { return mdlerrors.NewBackend("drop published REST service", err) } - if !e.quiet { + if !ctx.Quiet { fmt.Fprintf(ctx.Output, "Dropped published REST service %s.%s\n", s.Name.Module, s.Name.Name) } return nil @@ -311,8 +304,6 @@ func astResourceDefToModel(def *ast.PublishedRestResourceDef) *model.PublishedRe // execAlterPublishedRestService applies SET / ADD RESOURCE / DROP RESOURCE // actions to an existing published REST service. func execAlterPublishedRestService(ctx *ExecContext, s *ast.AlterPublishedRestServiceStmt) error { - e := ctx.executor - if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -371,11 +362,11 @@ func execAlterPublishedRestService(ctx *ExecContext, s *ast.AlterPublishedRestSe } } - if err := e.writer.UpdatePublishedRestService(svc); err != nil { + if err := ctx.Backend.UpdatePublishedRestService(svc); err != nil { return mdlerrors.NewBackend("alter published REST service", err) } - if !e.quiet { + if !ctx.Quiet { fmt.Fprintf(ctx.Output, "Altered published REST service %s.%s\n", s.Name.Module, s.Name.Name) } return nil diff --git a/mdl/executor/cmd_rename.go b/mdl/executor/cmd_rename.go index 1a56c070..f3a64344 100644 --- a/mdl/executor/cmd_rename.go +++ b/mdl/executor/cmd_rename.go @@ -42,14 +42,13 @@ func execRename(ctx *ExecContext, s *ast.RenameStmt) error { // execRenameEntity renames an entity and updates all BY_NAME references. func execRenameEntity(ctx *ExecContext, s *ast.RenameStmt) error { - e := ctx.executor // Find the entity module, err := findModule(ctx, s.Name.Module) if err != nil { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -69,7 +68,7 @@ func execRenameEntity(ctx *ExecContext, s *ast.RenameStmt) error { newQualifiedName := s.Name.Module + "." + s.NewName // Scan for references - hits, err := e.writer.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) + hits, err := ctx.Backend.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) if err != nil { return mdlerrors.NewBackend("scan references", err) } @@ -86,7 +85,7 @@ func execRenameEntity(ctx *ExecContext, s *ast.RenameStmt) error { break } } - if err := e.writer.UpdateDomainModel(dm); err != nil { + if err := ctx.Backend.UpdateDomainModel(dm); err != nil { return mdlerrors.NewBackend("update entity name", err) } @@ -102,7 +101,6 @@ func execRenameEntity(ctx *ExecContext, s *ast.RenameStmt) error { // execRenameModule renames a module and updates all BY_NAME references with the module prefix. func execRenameModule(ctx *ExecContext, s *ast.RenameStmt) error { - e := ctx.executor oldModuleName := s.Name.Module newModuleName := s.NewName @@ -113,13 +111,13 @@ func execRenameModule(ctx *ExecContext, s *ast.RenameStmt) error { // Scan for all references with the old module prefix // Module rename replaces "OldModule." with "NewModule." in all qualified names - hits, err := e.writer.RenameReferences(oldModuleName+".", newModuleName+".", s.DryRun) + hits, err := ctx.Backend.RenameReferences(oldModuleName+".", newModuleName+".", s.DryRun) if err != nil { return mdlerrors.NewBackend("scan references", err) } // Also scan for exact module name matches (e.g., in navigation, security role refs) - exactHits, err := e.writer.RenameReferences(oldModuleName, newModuleName, s.DryRun) + exactHits, err := ctx.Backend.RenameReferences(oldModuleName, newModuleName, s.DryRun) if err != nil { return mdlerrors.NewBackend("scan exact module references", err) } @@ -134,7 +132,7 @@ func execRenameModule(ctx *ExecContext, s *ast.RenameStmt) error { // Update the module name module.Name = newModuleName - if err := e.writer.UpdateModule(module); err != nil { + if err := ctx.Backend.UpdateModule(module); err != nil { return mdlerrors.NewBackend("update module name", err) } @@ -153,7 +151,6 @@ func execRenameModule(ctx *ExecContext, s *ast.RenameStmt) error { // The reference scanner handles updating all BY_NAME references, and then we update // the document's own Name field via a raw BSON rewrite. func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) error { - e := ctx.executor oldQualifiedName := s.Name.Module + "." + s.Name.Name newQualifiedName := s.Name.Module + "." + s.NewName @@ -166,7 +163,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err found := false switch docType { case "microflow": - mfs, _ := e.reader.ListMicroflows() + mfs, _ := ctx.Backend.ListMicroflows() for _, mf := range mfs { modID := h.FindModuleID(mf.ContainerID) if h.GetModuleName(modID) == s.Name.Module && mf.Name == s.Name.Name { @@ -175,7 +172,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err } } case "nanoflow": - nfs, _ := e.reader.ListNanoflows() + nfs, _ := ctx.Backend.ListNanoflows() for _, nf := range nfs { modID := h.FindModuleID(nf.ContainerID) if h.GetModuleName(modID) == s.Name.Module && nf.Name == s.Name.Name { @@ -184,7 +181,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err } } case "page": - pgs, _ := e.reader.ListPages() + pgs, _ := ctx.Backend.ListPages() for _, pg := range pgs { modID := h.FindModuleID(pg.ContainerID) if h.GetModuleName(modID) == s.Name.Module && pg.Name == s.Name.Name { @@ -193,7 +190,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err } } case "constant": - cs, _ := e.reader.ListConstants() + cs, _ := ctx.Backend.ListConstants() for _, c := range cs { modID := h.FindModuleID(c.ContainerID) if h.GetModuleName(modID) == s.Name.Module && c.Name == s.Name.Name { @@ -212,7 +209,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err // simple name (e.g., "OldName"), not the qualified name. So we need to // handle it separately — the scanner updates cross-references, and we // update the Name field directly. - hits, err := e.writer.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) + hits, err := ctx.Backend.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) if err != nil { return mdlerrors.NewBackend("scan references", err) } @@ -223,7 +220,7 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err } // Update the document's own Name field via the raw BSON name updater - if err := e.writer.RenameDocumentByName(s.Name.Module, s.Name.Name, s.NewName); err != nil { + if err := ctx.Backend.RenameDocumentByName(s.Name.Module, s.Name.Name, s.NewName); err != nil { return mdlerrors.NewBackend(fmt.Sprintf("rename %s", docType), err) } @@ -238,12 +235,11 @@ func execRenameDocument(ctx *ExecContext, s *ast.RenameStmt, docType string) err // execRenameEnumeration renames an enumeration and updates all references. func execRenameEnumeration(ctx *ExecContext, s *ast.RenameStmt) error { - e := ctx.executor oldQualifiedName := s.Name.Module + "." + s.Name.Name newQualifiedName := s.Name.Module + "." + s.NewName // Verify it exists - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return mdlerrors.NewBackend("list enumerations", err) } @@ -263,7 +259,7 @@ func execRenameEnumeration(ctx *ExecContext, s *ast.RenameStmt) error { return mdlerrors.NewNotFound("enumeration", oldQualifiedName) } - hits, err := e.writer.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) + hits, err := ctx.Backend.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) if err != nil { return mdlerrors.NewBackend("scan references", err) } @@ -274,12 +270,12 @@ func execRenameEnumeration(ctx *ExecContext, s *ast.RenameStmt) error { } // Update enumeration name via raw BSON - if err := e.writer.RenameDocumentByName(s.Name.Module, s.Name.Name, s.NewName); err != nil { + if err := ctx.Backend.RenameDocumentByName(s.Name.Module, s.Name.Name, s.NewName); err != nil { return mdlerrors.NewBackend("rename enumeration", err) } // Also update enumeration refs in domain models (attribute types store qualified enum names) - e.writer.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName) + ctx.Backend.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName) invalidateHierarchy(ctx) invalidateDomainModelsCache(ctx) @@ -293,7 +289,6 @@ func execRenameEnumeration(ctx *ExecContext, s *ast.RenameStmt) error { // execRenameAssociation renames an association and updates all references. func execRenameAssociation(ctx *ExecContext, s *ast.RenameStmt) error { - e := ctx.executor oldQualifiedName := s.Name.Module + "." + s.Name.Name newQualifiedName := s.Name.Module + "." + s.NewName @@ -302,7 +297,7 @@ func execRenameAssociation(ctx *ExecContext, s *ast.RenameStmt) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -318,7 +313,7 @@ func execRenameAssociation(ctx *ExecContext, s *ast.RenameStmt) error { return mdlerrors.NewNotFound("association", oldQualifiedName) } - hits, err := e.writer.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) + hits, err := ctx.Backend.RenameReferences(oldQualifiedName, newQualifiedName, s.DryRun) if err != nil { return mdlerrors.NewBackend("scan references", err) } @@ -335,7 +330,7 @@ func execRenameAssociation(ctx *ExecContext, s *ast.RenameStmt) error { break } } - if err := e.writer.UpdateDomainModel(dm); err != nil { + if err := ctx.Backend.UpdateDomainModel(dm); err != nil { return mdlerrors.NewBackend("update association name", err) } diff --git a/mdl/executor/cmd_rest_clients.go b/mdl/executor/cmd_rest_clients.go index 5d47df89..bdc4c591 100644 --- a/mdl/executor/cmd_rest_clients.go +++ b/mdl/executor/cmd_rest_clients.go @@ -23,9 +23,8 @@ func safeIdent(name string) string { // showRestClients handles SHOW REST CLIENTS [IN module] command. func showRestClients(ctx *ExecContext, moduleName string) error { - e := ctx.executor - services, err := e.reader.ListConsumedRestServices() + services, err := ctx.Backend.ListConsumedRestServices() if err != nil { return mdlerrors.NewBackend("list consumed REST services", err) } @@ -86,9 +85,8 @@ func showRestClients(ctx *ExecContext, moduleName string) error { // describeRestClient handles DESCRIBE REST CLIENT command. func describeRestClient(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - services, err := e.reader.ListConsumedRestServices() + services, err := ctx.Backend.ListConsumedRestServices() if err != nil { return mdlerrors.NewBackend("list consumed REST services", err) } @@ -281,7 +279,6 @@ func writeExportMappings(w io.Writer, mappings []*model.RestResponseMapping, ind // createRestClient handles CREATE REST CLIENT statement. func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() @@ -301,7 +298,7 @@ func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error { } // Check for existing service with same name - existingServices, _ := e.reader.ListConsumedRestServices() + existingServices, _ := ctx.Backend.ListConsumedRestServices() h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) @@ -313,7 +310,7 @@ func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error { if strings.EqualFold(existModName, moduleName) && strings.EqualFold(existing.Name, stmt.Name.Name) { if stmt.CreateOrModify { // Delete existing and recreate - if err := e.writer.DeleteConsumedRestService(existing.ID); err != nil { + if err := ctx.Backend.DeleteConsumedRestService(existing.ID); err != nil { return mdlerrors.NewBackend("delete existing REST client", err) } } else { @@ -356,7 +353,7 @@ func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error { } // Write to project - if err := e.writer.CreateConsumedRestService(svc); err != nil { + if err := ctx.Backend.CreateConsumedRestService(svc); err != nil { return mdlerrors.NewBackend("create REST client", err) } @@ -462,13 +459,12 @@ func convertMappingEntries(entries []ast.RestMappingEntry, importDirection bool) // dropRestClient handles DROP REST CLIENT statement. func dropRestClient(ctx *ExecContext, stmt *ast.DropRestClientStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - services, err := e.reader.ListConsumedRestServices() + services, err := ctx.Backend.ListConsumedRestServices() if err != nil { return mdlerrors.NewBackend("list consumed REST services", err) } @@ -482,7 +478,7 @@ func dropRestClient(ctx *ExecContext, stmt *ast.DropRestClientStmt) error { modID := h.FindModuleID(svc.ContainerID) moduleName := h.GetModuleName(modID) if strings.EqualFold(moduleName, stmt.Name.Module) && strings.EqualFold(svc.Name, stmt.Name.Name) { - if err := e.writer.DeleteConsumedRestService(svc.ID); err != nil { + if err := ctx.Backend.DeleteConsumedRestService(svc.ID); err != nil { return mdlerrors.NewBackend("delete REST client", err) } fmt.Fprintf(ctx.Output, "Dropped REST client: %s.%s\n", moduleName, svc.Name) diff --git a/mdl/executor/cmd_security.go b/mdl/executor/cmd_security.go index 96f7d4a9..e6dc0bae 100644 --- a/mdl/executor/cmd_security.go +++ b/mdl/executor/cmd_security.go @@ -15,8 +15,7 @@ import ( // showProjectSecurity handles SHOW PROJECT SECURITY. func showProjectSecurity(ctx *ExecContext) error { - e := ctx.executor - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -49,13 +48,12 @@ func showProjectSecurity(ctx *ExecContext) error { // showModuleRoles handles SHOW MODULE ROLES [IN module]. func showModuleRoles(ctx *ExecContext, moduleName string) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - allMS, err := e.reader.ListModuleSecurity() + allMS, err := ctx.Backend.ListModuleSecurity() if err != nil { return mdlerrors.NewBackend("read module security", err) } @@ -84,8 +82,7 @@ func showModuleRoles(ctx *ExecContext, moduleName string) error { // showUserRoles handles SHOW USER ROLES. func showUserRoles(ctx *ExecContext) error { - e := ctx.executor - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -112,8 +109,7 @@ func showUserRoles(ctx *ExecContext) error { // showDemoUsers handles SHOW DEMO USERS. func showDemoUsers(ctx *ExecContext) error { - e := ctx.executor - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -139,7 +135,6 @@ func showDemoUsers(ctx *ExecContext) error { // showAccessOnEntity handles SHOW ACCESS ON Module.Entity. func showAccessOnEntity(ctx *ExecContext, name *ast.QualifiedName) error { - e := ctx.executor if name == nil { return mdlerrors.NewValidation("entity name required") } @@ -149,7 +144,7 @@ func showAccessOnEntity(ctx *ExecContext, name *ast.QualifiedName) error { return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -251,7 +246,6 @@ func showAccessOnEntity(ctx *ExecContext, name *ast.QualifiedName) error { // showAccessOnMicroflow handles SHOW ACCESS ON MICROFLOW Module.MF. func showAccessOnMicroflow(ctx *ExecContext, name *ast.QualifiedName) error { - e := ctx.executor if name == nil { return mdlerrors.NewValidation("microflow name required") } @@ -261,7 +255,7 @@ func showAccessOnMicroflow(ctx *ExecContext, name *ast.QualifiedName) error { return mdlerrors.NewBackend("build hierarchy", err) } - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -286,7 +280,6 @@ func showAccessOnMicroflow(ctx *ExecContext, name *ast.QualifiedName) error { // showAccessOnPage handles SHOW ACCESS ON PAGE Module.Page. func showAccessOnPage(ctx *ExecContext, name *ast.QualifiedName) error { - e := ctx.executor if name == nil { return mdlerrors.NewValidation("page name required") } @@ -296,7 +289,7 @@ func showAccessOnPage(ctx *ExecContext, name *ast.QualifiedName) error { return mdlerrors.NewBackend("build hierarchy", err) } - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -326,8 +319,7 @@ func showAccessOnWorkflow(ctx *ExecContext, name *ast.QualifiedName) error { // showSecurityMatrix handles SHOW SECURITY MATRIX [IN module]. func showSecurityMatrix(ctx *ExecContext, moduleName string) error { - e := ctx.executor - if e.format == FormatJSON { + if ctx.Format == FormatJSON { return showSecurityMatrixJSON(ctx, moduleName) } @@ -337,7 +329,7 @@ func showSecurityMatrix(ctx *ExecContext, moduleName string) error { } // Collect all module roles - allMS, err := e.reader.ListModuleSecurity() + allMS, err := ctx.Backend.ListModuleSecurity() if err != nil { return mdlerrors.NewBackend("read module security", err) } @@ -377,7 +369,7 @@ func showSecurityMatrix(ctx *ExecContext, moduleName string) error { } // Collect entities with access rules - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return mdlerrors.NewBackend("list domain models", err) } @@ -458,7 +450,7 @@ func showSecurityMatrix(ctx *ExecContext, moduleName string) error { fmt.Fprintln(ctx.Output, "## Microflow Access") fmt.Fprintln(ctx.Output) - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -489,7 +481,7 @@ func showSecurityMatrix(ctx *ExecContext, moduleName string) error { fmt.Fprintln(ctx.Output, "## Page Access") fmt.Fprintln(ctx.Output) - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -528,7 +520,6 @@ func showSecurityMatrix(ctx *ExecContext, moduleName string) error { // showSecurityMatrixJSON emits the security matrix as a JSON table // with one row per access rule across entities, microflows, pages, and workflows. func showSecurityMatrixJSON(ctx *ExecContext, moduleName string) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) @@ -539,7 +530,7 @@ func showSecurityMatrixJSON(ctx *ExecContext, moduleName string) error { } // Entities - dms, _ := e.reader.ListDomainModels() + dms, _ := ctx.Backend.ListDomainModels() for _, dm := range dms { modID := h.FindModuleID(dm.ContainerID) modName := h.GetModuleName(modID) @@ -594,7 +585,7 @@ func showSecurityMatrixJSON(ctx *ExecContext, moduleName string) error { } // Microflows - mfs, _ := e.reader.ListMicroflows() + mfs, _ := ctx.Backend.ListMicroflows() for _, mf := range mfs { if len(mf.AllowedModuleRoles) == 0 { continue @@ -617,7 +608,7 @@ func showSecurityMatrixJSON(ctx *ExecContext, moduleName string) error { } // Pages - pages, _ := e.reader.ListPages() + pages, _ := ctx.Backend.ListPages() for _, pg := range pages { if len(pg.AllowedRoles) == 0 { continue @@ -644,13 +635,12 @@ func showSecurityMatrixJSON(ctx *ExecContext, moduleName string) error { // describeModuleRole handles DESCRIBE MODULE ROLE Module.RoleName. func describeModuleRole(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - allMS, err := e.reader.ListModuleSecurity() + allMS, err := ctx.Backend.ListModuleSecurity() if err != nil { return mdlerrors.NewBackend("read module security", err) } @@ -671,7 +661,7 @@ func describeModuleRole(ctx *ExecContext, name ast.QualifiedName) error { // Show which user roles include this module role qualifiedRole := modName + "." + mr.Name - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err == nil { var includedBy []string for _, ur := range ps.UserRoles { @@ -696,8 +686,7 @@ func describeModuleRole(ctx *ExecContext, name ast.QualifiedName) error { // describeDemoUser handles DESCRIBE DEMO USER 'name'. func describeDemoUser(ctx *ExecContext, userName string) error { - e := ctx.executor - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -722,8 +711,7 @@ func describeDemoUser(ctx *ExecContext, userName string) error { // describeUserRole handles DESCRIBE USER ROLE Name. func describeUserRole(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } diff --git a/mdl/executor/cmd_security_write.go b/mdl/executor/cmd_security_write.go index 97144d24..ffe7cc0e 100644 --- a/mdl/executor/cmd_security_write.go +++ b/mdl/executor/cmd_security_write.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/mendixlabs/mxcli/mdl/ast" + "github.com/mendixlabs/mxcli/mdl/backend" mdlerrors "github.com/mendixlabs/mxcli/mdl/errors" "github.com/mendixlabs/mxcli/model" "github.com/mendixlabs/mxcli/sdk/mpr" @@ -16,7 +17,6 @@ import ( // execCreateModuleRole handles CREATE MODULE ROLE Module.RoleName [DESCRIPTION '...']. func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -26,7 +26,7 @@ func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { return err } - ms, err := e.reader.GetModuleSecurity(module.ID) + ms, err := ctx.Backend.GetModuleSecurity(module.ID) if err != nil { return mdlerrors.NewBackend(fmt.Sprintf("read module security for %s", s.Name.Module), err) } @@ -38,7 +38,7 @@ func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { } } - if err := e.writer.AddModuleRole(ms.ID, s.Name.Name, s.Description); err != nil { + if err := ctx.Backend.AddModuleRole(ms.ID, s.Name.Name, s.Description); err != nil { return mdlerrors.NewBackend("create module role", err) } @@ -50,7 +50,6 @@ func execCreateModuleRole(ctx *ExecContext, s *ast.CreateModuleRoleStmt) error { // Cascade-removes the role from all entity access rules, microflow/nanoflow/page // allowed roles, and OData service allowed roles before deleting the role itself. func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -60,7 +59,7 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { return err } - ms, err := e.reader.GetModuleSecurity(module.ID) + ms, err := ctx.Backend.GetModuleSecurity(module.ID) if err != nil { return mdlerrors.NewBackend(fmt.Sprintf("read module security for %s", s.Name.Module), err) } @@ -80,9 +79,9 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { qualifiedRole := s.Name.Module + "." + s.Name.Name // Cascade: remove role from entity access rules - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err == nil { - if n, err := e.writer.RemoveRoleFromAllEntities(dm.ID, qualifiedRole); err != nil { + if n, err := ctx.Backend.RemoveRoleFromAllEntities(dm.ID, qualifiedRole); err != nil { return mdlerrors.NewBackend("cascade-remove entity access rules", err) } else if n > 0 { fmt.Fprintf(ctx.Output, "Removed %s from %d entity access rule(s)\n", qualifiedRole, n) @@ -93,52 +92,52 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { h, err := getHierarchy(ctx) if err == nil { // Microflows - if mfs, err := e.reader.ListMicroflows(); err == nil { + if mfs, err := ctx.Backend.ListMicroflows(); err == nil { for _, mf := range mfs { modID := h.FindModuleID(mf.ContainerID) if modID != module.ID { continue } - if removed, err := e.writer.RemoveFromAllowedRoles(mf.ID, qualifiedRole); err == nil && removed { + if removed, err := ctx.Backend.RemoveFromAllowedRoles(mf.ID, qualifiedRole); err == nil && removed { fmt.Fprintf(ctx.Output, "Removed %s from microflow %s allowed roles\n", qualifiedRole, mf.Name) } } } // Nanoflows - if nfs, err := e.reader.ListNanoflows(); err == nil { + if nfs, err := ctx.Backend.ListNanoflows(); err == nil { for _, nf := range nfs { modID := h.FindModuleID(nf.ContainerID) if modID != module.ID { continue } - if removed, err := e.writer.RemoveFromAllowedRoles(nf.ID, qualifiedRole); err == nil && removed { + if removed, err := ctx.Backend.RemoveFromAllowedRoles(nf.ID, qualifiedRole); err == nil && removed { fmt.Fprintf(ctx.Output, "Removed %s from nanoflow %s allowed roles\n", qualifiedRole, nf.Name) } } } // Pages - if pgs, err := e.reader.ListPages(); err == nil { + if pgs, err := ctx.Backend.ListPages(); err == nil { for _, pg := range pgs { modID := h.FindModuleID(pg.ContainerID) if modID != module.ID { continue } - if removed, err := e.writer.RemoveFromAllowedRoles(pg.ID, qualifiedRole); err == nil && removed { + if removed, err := ctx.Backend.RemoveFromAllowedRoles(pg.ID, qualifiedRole); err == nil && removed { fmt.Fprintf(ctx.Output, "Removed %s from page %s allowed roles\n", qualifiedRole, pg.Name) } } } // OData services - if svcs, err := e.reader.ListPublishedODataServices(); err == nil { + if svcs, err := ctx.Backend.ListPublishedODataServices(); err == nil { for _, svc := range svcs { modID := h.FindModuleID(svc.ContainerID) if modID != module.ID { continue } - if removed, err := e.writer.RemoveFromAllowedRoles(svc.ID, qualifiedRole); err == nil && removed { + if removed, err := ctx.Backend.RemoveFromAllowedRoles(svc.ID, qualifiedRole); err == nil && removed { fmt.Fprintf(ctx.Output, "Removed %s from OData service %s allowed roles\n", qualifiedRole, svc.Name) } } @@ -146,14 +145,14 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { } // Cascade: remove role from user roles in ProjectSecurity - if ps, err := e.reader.GetProjectSecurity(); err == nil { - if n, err := e.writer.RemoveModuleRoleFromAllUserRoles(ps.ID, qualifiedRole); err == nil && n > 0 { + if ps, err := ctx.Backend.GetProjectSecurity(); err == nil { + if n, err := ctx.Backend.RemoveModuleRoleFromAllUserRoles(ps.ID, qualifiedRole); err == nil && n > 0 { fmt.Fprintf(ctx.Output, "Removed %s from %d user role(s)\n", qualifiedRole, n) } } // Finally, remove the role itself - if err := e.writer.RemoveModuleRole(ms.ID, s.Name.Name); err != nil { + if err := ctx.Backend.RemoveModuleRole(ms.ID, s.Name.Name); err != nil { return mdlerrors.NewBackend("drop module role", err) } @@ -163,12 +162,11 @@ func execDropModuleRole(ctx *ExecContext, s *ast.DropModuleRoleStmt) error { // execCreateUserRole handles CREATE [OR MODIFY] USER ROLE Name (ModuleRoles) [MANAGE ALL ROLES]. func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -187,7 +185,7 @@ func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { return mdlerrors.NewAlreadyExists("user role", s.Name) } // Additive: ensure specified module roles are present - if err := e.writer.AlterUserRoleModuleRoles(ps.ID, s.Name, true, moduleRoleNames); err != nil { + if err := ctx.Backend.AlterUserRoleModuleRoles(ps.ID, s.Name, true, moduleRoleNames); err != nil { return mdlerrors.NewBackend("update user role", err) } fmt.Fprintf(ctx.Output, "Modified user role: %s\n", s.Name) @@ -195,7 +193,7 @@ func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { } } - if err := e.writer.AddUserRole(ps.ID, s.Name, moduleRoleNames, s.ManageAllRoles); err != nil { + if err := ctx.Backend.AddUserRole(ps.ID, s.Name, moduleRoleNames, s.ManageAllRoles); err != nil { return mdlerrors.NewBackend("create user role", err) } @@ -205,12 +203,11 @@ func execCreateUserRole(ctx *ExecContext, s *ast.CreateUserRoleStmt) error { // execAlterUserRole handles ALTER USER ROLE Name ADD/REMOVE MODULE ROLES (...). func execAlterUserRole(ctx *ExecContext, s *ast.AlterUserRoleStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -233,7 +230,7 @@ func execAlterUserRole(ctx *ExecContext, s *ast.AlterUserRoleStmt) error { moduleRoleNames = append(moduleRoleNames, mr.Module+"."+mr.Name) } - if err := e.writer.AlterUserRoleModuleRoles(ps.ID, s.Name, s.Add, moduleRoleNames); err != nil { + if err := ctx.Backend.AlterUserRoleModuleRoles(ps.ID, s.Name, s.Add, moduleRoleNames); err != nil { return mdlerrors.NewBackend("alter user role", err) } @@ -249,12 +246,11 @@ func execAlterUserRole(ctx *ExecContext, s *ast.AlterUserRoleStmt) error { // execDropUserRole handles DROP USER ROLE Name. func execDropUserRole(ctx *ExecContext, s *ast.DropUserRoleStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -271,7 +267,7 @@ func execDropUserRole(ctx *ExecContext, s *ast.DropUserRoleStmt) error { return mdlerrors.NewNotFound("user role", s.Name) } - if err := e.writer.RemoveUserRole(ps.ID, s.Name); err != nil { + if err := ctx.Backend.RemoveUserRole(ps.ID, s.Name); err != nil { return mdlerrors.NewBackend("drop user role", err) } @@ -291,7 +287,7 @@ func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -415,22 +411,29 @@ func execGrantEntityAccess(ctx *ExecContext, s *ast.GrantEntityAccessStmt) error }) } - if err := e.writer.AddEntityAccessRule(dm.ID, s.Entity.Name, roleNames, - allowCreate, allowDelete, - defaultMemberAccess, s.XPathConstraint, memberAccesses); err != nil { + if err := ctx.Backend.AddEntityAccessRule(backend.EntityAccessRuleParams{ + UnitID: dm.ID, + EntityName: s.Entity.Name, + RoleNames: roleNames, + AllowCreate: allowCreate, + AllowDelete: allowDelete, + DefaultMemberAccess: defaultMemberAccess, + XPathConstraint: s.XPathConstraint, + MemberAccesses: memberAccesses, + }); err != nil { return mdlerrors.NewBackend("grant entity access", err) } // Reconcile MemberAccesses on pre-existing rules for this entity's domain model - if count, err := e.writer.ReconcileMemberAccesses(dm.ID, module.Name); err != nil { + if count, err := ctx.Backend.ReconcileMemberAccesses(dm.ID, module.Name); err != nil { return mdlerrors.NewBackend("reconcile member accesses", err) - } else if count > 0 && !e.quiet { + } else if count > 0 && !ctx.Quiet { fmt.Fprintf(ctx.Output, "Reconciled %d access rule(s) in module %s\n", count, module.Name) } e.trackModifiedDomainModel(module.ID, module.Name) fmt.Fprintf(ctx.Output, "Granted access on %s.%s to %s\n", s.Entity.Module, s.Entity.Name, strings.Join(roleNames, ", ")) - if !e.quiet { + if !ctx.Quiet { fmt.Fprint(ctx.Output, formatAccessRuleResult(ctx, s.Entity.Module, s.Entity.Name, roleNames)) } return nil @@ -448,7 +451,7 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err return err } - dm, err := e.reader.GetDomainModel(module.ID) + dm, err := ctx.Backend.GetDomainModel(module.ID) if err != nil { return mdlerrors.NewBackend("get domain model", err) } @@ -491,7 +494,7 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err } } - modified, err := e.writer.RevokeEntityMemberAccess(dm.ID, s.Entity.Name, roleNames, revocation) + modified, err := ctx.Backend.RevokeEntityMemberAccess(dm.ID, s.Entity.Name, roleNames, revocation) if err != nil { return mdlerrors.NewBackend("revoke entity access", err) } @@ -500,13 +503,13 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err fmt.Fprintf(ctx.Output, "No access rules found matching %s on %s.%s\n", strings.Join(roleNames, ", "), s.Entity.Module, s.Entity.Name) } else { fmt.Fprintf(ctx.Output, "Revoked partial access on %s.%s from %s\n", s.Entity.Module, s.Entity.Name, strings.Join(roleNames, ", ")) - if !e.quiet { + if !ctx.Quiet { fmt.Fprint(ctx.Output, formatAccessRuleResult(ctx, s.Entity.Module, s.Entity.Name, roleNames)) } } } else { // Full revoke — remove entire access rule - modified, err := e.writer.RemoveEntityAccessRule(dm.ID, s.Entity.Name, roleNames) + modified, err := ctx.Backend.RemoveEntityAccessRule(dm.ID, s.Entity.Name, roleNames) if err != nil { return mdlerrors.NewBackend("revoke entity access", err) } @@ -515,7 +518,7 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err fmt.Fprintf(ctx.Output, "No access rules found matching %s on %s.%s\n", strings.Join(roleNames, ", "), s.Entity.Module, s.Entity.Name) } else { fmt.Fprintf(ctx.Output, "Revoked access on %s.%s from %s\n", s.Entity.Module, s.Entity.Name, strings.Join(roleNames, ", ")) - if !e.quiet { + if !ctx.Quiet { fmt.Fprint(ctx.Output, " Result: (no access)\n") } } @@ -526,7 +529,6 @@ func execRevokeEntityAccess(ctx *ExecContext, s *ast.RevokeEntityAccessStmt) err // execGrantMicroflowAccess handles GRANT EXECUTE ON MICROFLOW Module.MF TO roles. func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -537,7 +539,7 @@ func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) } // Find the microflow - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -572,7 +574,7 @@ func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) } } - if err := e.writer.UpdateAllowedRoles(mf.ID, merged); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(mf.ID, merged); err != nil { return mdlerrors.NewBackend("update microflow access", err) } @@ -589,7 +591,6 @@ func execGrantMicroflowAccess(ctx *ExecContext, s *ast.GrantMicroflowAccessStmt) // execRevokeMicroflowAccess handles REVOKE EXECUTE ON MICROFLOW Module.MF FROM roles. func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -600,7 +601,7 @@ func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStm } // Find the microflow - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return mdlerrors.NewBackend("list microflows", err) } @@ -629,7 +630,7 @@ func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStm } } - if err := e.writer.UpdateAllowedRoles(mf.ID, remaining); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(mf.ID, remaining); err != nil { return mdlerrors.NewBackend("update microflow access", err) } @@ -646,7 +647,6 @@ func execRevokeMicroflowAccess(ctx *ExecContext, s *ast.RevokeMicroflowAccessStm // execGrantPageAccess handles GRANT VIEW ON PAGE Module.Page TO roles. func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -657,7 +657,7 @@ func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { } // Find the page - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -692,7 +692,7 @@ func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { } } - if err := e.writer.UpdateAllowedRoles(pg.ID, merged); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(pg.ID, merged); err != nil { return mdlerrors.NewBackend("update page access", err) } @@ -709,7 +709,6 @@ func execGrantPageAccess(ctx *ExecContext, s *ast.GrantPageAccessStmt) error { // execRevokePageAccess handles REVOKE VIEW ON PAGE Module.Page FROM roles. func execRevokePageAccess(ctx *ExecContext, s *ast.RevokePageAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -720,7 +719,7 @@ func execRevokePageAccess(ctx *ExecContext, s *ast.RevokePageAccessStmt) error { } // Find the page - pages, err := e.reader.ListPages() + pages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -749,7 +748,7 @@ func execRevokePageAccess(ctx *ExecContext, s *ast.RevokePageAccessStmt) error { } } - if err := e.writer.UpdateAllowedRoles(pg.ID, remaining); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(pg.ID, remaining); err != nil { return mdlerrors.NewBackend("update page access", err) } @@ -780,13 +779,12 @@ func execRevokeWorkflowAccess(ctx *ExecContext, s *ast.RevokeWorkflowAccessStmt) // validateModuleRole checks that a module role exists in the project. func validateModuleRole(ctx *ExecContext, role ast.QualifiedName) error { - e := ctx.executor module, err := findModule(ctx, role.Module) if err != nil { return fmt.Errorf("module not found for role %s.%s: %w", role.Module, role.Name, err) } - ms, err := e.reader.GetModuleSecurity(module.ID) + ms, err := ctx.Backend.GetModuleSecurity(module.ID) if err != nil { return mdlerrors.NewBackend(fmt.Sprintf("read module security for %s", role.Module), err) } @@ -802,12 +800,11 @@ func validateModuleRole(ctx *ExecContext, role ast.QualifiedName) error { // execAlterProjectSecurity handles ALTER PROJECT SECURITY LEVEL/DEMO USERS. func execAlterProjectSecurity(ctx *ExecContext, s *ast.AlterProjectSecurityStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -826,14 +823,14 @@ func execAlterProjectSecurity(ctx *ExecContext, s *ast.AlterProjectSecurityStmt) return mdlerrors.NewUnsupported(fmt.Sprintf("unknown security level: %s", s.SecurityLevel)) } - if err := e.writer.SetProjectSecurityLevel(ps.ID, bsonLevel); err != nil { + if err := ctx.Backend.SetProjectSecurityLevel(ps.ID, bsonLevel); err != nil { return mdlerrors.NewBackend("set security level", err) } fmt.Fprintf(ctx.Output, "Set project security level to %s\n", s.SecurityLevel) } if s.DemoUsersEnabled != nil { - if err := e.writer.SetProjectDemoUsersEnabled(ps.ID, *s.DemoUsersEnabled); err != nil { + if err := ctx.Backend.SetProjectDemoUsersEnabled(ps.ID, *s.DemoUsersEnabled); err != nil { return mdlerrors.NewBackend("set demo users", err) } state := "disabled" @@ -848,12 +845,11 @@ func execAlterProjectSecurity(ctx *ExecContext, s *ast.AlterProjectSecurityStmt) // execCreateDemoUser handles CREATE [OR MODIFY] DEMO USER 'name' PASSWORD 'pw' [ENTITY Module.Entity] (Roles). func execCreateDemoUser(ctx *ExecContext, s *ast.CreateDemoUserStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -884,10 +880,10 @@ func execCreateDemoUser(ctx *ExecContext, s *ast.CreateDemoUserStmt) error { if s.Entity != "" { entity = s.Entity } - if err := e.writer.RemoveDemoUser(ps.ID, s.UserName); err != nil { + if err := ctx.Backend.RemoveDemoUser(ps.ID, s.UserName); err != nil { return mdlerrors.NewBackend("update demo user", err) } - if err := e.writer.AddDemoUser(ps.ID, s.UserName, s.Password, entity, mergedRoles); err != nil { + if err := ctx.Backend.AddDemoUser(ps.ID, s.UserName, s.Password, entity, mergedRoles); err != nil { return mdlerrors.NewBackend("update demo user", err) } fmt.Fprintf(ctx.Output, "Modified demo user: %s\n", s.UserName) @@ -905,7 +901,7 @@ func execCreateDemoUser(ctx *ExecContext, s *ast.CreateDemoUserStmt) error { entity = detected } - if err := e.writer.AddDemoUser(ps.ID, s.UserName, s.Password, entity, s.UserRoles); err != nil { + if err := ctx.Backend.AddDemoUser(ps.ID, s.UserName, s.Password, entity, s.UserRoles); err != nil { return mdlerrors.NewBackend("create demo user", err) } @@ -915,8 +911,7 @@ func execCreateDemoUser(ctx *ExecContext, s *ast.CreateDemoUserStmt) error { // detectUserEntity finds the entity that generalizes System.User. func detectUserEntity(ctx *ExecContext) (string, error) { - e := ctx.executor - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return "", mdlerrors.NewBackend("list modules", err) } @@ -925,7 +920,7 @@ func detectUserEntity(ctx *ExecContext) (string, error) { moduleNameByID[m.ID] = m.Name } - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return "", mdlerrors.NewBackend("list domain models", err) } @@ -960,12 +955,11 @@ func joinCandidates(candidates []string) string { // execDropDemoUser handles DROP DEMO USER 'name'. func execDropDemoUser(ctx *ExecContext, s *ast.DropDemoUserStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSecurity() + ps, err := ctx.Backend.GetProjectSecurity() if err != nil { return mdlerrors.NewBackend("read project security", err) } @@ -982,7 +976,7 @@ func execDropDemoUser(ctx *ExecContext, s *ast.DropDemoUserStmt) error { return mdlerrors.NewNotFound("demo user", s.UserName) } - if err := e.writer.RemoveDemoUser(ps.ID, s.UserName); err != nil { + if err := ctx.Backend.RemoveDemoUser(ps.ID, s.UserName); err != nil { return mdlerrors.NewBackend("drop demo user", err) } @@ -996,7 +990,6 @@ func execDropDemoUser(ctx *ExecContext, s *ast.DropDemoUserStmt) error { // execGrantODataServiceAccess handles GRANT ACCESS ON ODATA SERVICE Module.Svc TO roles. func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1007,7 +1000,7 @@ func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAcces } // Find the published OData service - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -1042,7 +1035,7 @@ func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAcces } } - if err := e.writer.UpdateAllowedRoles(svc.ID, merged); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(svc.ID, merged); err != nil { return mdlerrors.NewBackend("update OData service access", err) } @@ -1059,7 +1052,6 @@ func execGrantODataServiceAccess(ctx *ExecContext, s *ast.GrantODataServiceAcces // execRevokeODataServiceAccess handles REVOKE ACCESS ON ODATA SERVICE Module.Svc FROM roles. func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1070,7 +1062,7 @@ func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAcc } // Find the published OData service - services, err := e.reader.ListPublishedODataServices() + services, err := ctx.Backend.ListPublishedODataServices() if err != nil { return mdlerrors.NewBackend("list published OData services", err) } @@ -1099,7 +1091,7 @@ func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAcc } } - if err := e.writer.UpdateAllowedRoles(svc.ID, remaining); err != nil { + if err := ctx.Backend.UpdateAllowedRoles(svc.ID, remaining); err != nil { return mdlerrors.NewBackend("update OData service access", err) } @@ -1120,7 +1112,6 @@ func execRevokeODataServiceAccess(ctx *ExecContext, s *ast.RevokeODataServiceAcc // execGrantPublishedRestServiceAccess handles GRANT ACCESS ON PUBLISHED REST SERVICE Module.Svc TO roles. func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublishedRestServiceAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1136,7 +1127,7 @@ func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublished return mdlerrors.NewBackend("build hierarchy", err) } - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return mdlerrors.NewBackend("list published REST services", err) } @@ -1171,7 +1162,7 @@ func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublished } } - if err := e.writer.UpdatePublishedRestServiceRoles(svc.ID, merged); err != nil { + if err := ctx.Backend.UpdatePublishedRestServiceRoles(svc.ID, merged); err != nil { return mdlerrors.NewBackend("update published REST service access", err) } @@ -1188,7 +1179,6 @@ func execGrantPublishedRestServiceAccess(ctx *ExecContext, s *ast.GrantPublished // execRevokePublishedRestServiceAccess handles REVOKE ACCESS ON PUBLISHED REST SERVICE Module.Svc FROM roles. func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublishedRestServiceAccessStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1198,7 +1188,7 @@ func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublish return mdlerrors.NewBackend("build hierarchy", err) } - services, err := e.reader.ListPublishedRestServices() + services, err := ctx.Backend.ListPublishedRestServices() if err != nil { return mdlerrors.NewBackend("list published REST services", err) } @@ -1227,7 +1217,7 @@ func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublish } } - if err := e.writer.UpdatePublishedRestServiceRoles(svc.ID, remaining); err != nil { + if err := ctx.Backend.UpdatePublishedRestServiceRoles(svc.ID, remaining); err != nil { return mdlerrors.NewBackend("update published REST service access", err) } @@ -1244,7 +1234,6 @@ func execRevokePublishedRestServiceAccess(ctx *ExecContext, s *ast.RevokePublish // execUpdateSecurity handles UPDATE SECURITY [IN Module]. func execUpdateSecurity(ctx *ExecContext, s *ast.UpdateSecurityStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -1260,12 +1249,12 @@ func execUpdateSecurity(ctx *ExecContext, s *ast.UpdateSecurityStmt) error { continue } - dm, err := e.reader.GetDomainModel(mod.ID) + dm, err := ctx.Backend.GetDomainModel(mod.ID) if err != nil { continue // module may not have a domain model } - count, err := e.writer.ReconcileMemberAccesses(dm.ID, mod.Name) + count, err := ctx.Backend.ReconcileMemberAccesses(dm.ID, mod.Name) if err != nil { return mdlerrors.NewBackend(fmt.Sprintf("reconcile security for module %s", mod.Name), err) } diff --git a/mdl/executor/cmd_settings.go b/mdl/executor/cmd_settings.go index d84d51e4..18bf2900 100644 --- a/mdl/executor/cmd_settings.go +++ b/mdl/executor/cmd_settings.go @@ -14,12 +14,11 @@ import ( // showSettings displays an overview table of all settings parts. func showSettings(ctx *ExecContext) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -85,12 +84,11 @@ func showSettings(ctx *ExecContext) error { // describeSettings outputs the full MDL description of all settings. func describeSettings(ctx *ExecContext) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -171,12 +169,11 @@ func describeSettings(ctx *ExecContext) error { // alterSettings modifies project settings based on ALTER SETTINGS statement. func alterSettings(ctx *ExecContext, stmt *ast.AlterSettingsStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -262,7 +259,7 @@ func alterSettings(ctx *ExecContext, stmt *ast.AlterSettingsStmt) error { } // Write updated settings - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } @@ -271,7 +268,6 @@ func alterSettings(ctx *ExecContext, stmt *ast.AlterSettingsStmt) error { } func alterSettingsConfiguration(ctx *ExecContext, ps *model.ProjectSettings, stmt *ast.AlterSettingsStmt) error { - e := ctx.executor if ps.Configuration == nil { return mdlerrors.NewNotFound("settings section", "configuration") } @@ -316,7 +312,7 @@ func alterSettingsConfiguration(ctx *ExecContext, ps *model.ProjectSettings, stm } } - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } @@ -325,7 +321,6 @@ func alterSettingsConfiguration(ctx *ExecContext, ps *model.ProjectSettings, stm } func alterSettingsConstant(ctx *ExecContext, ps *model.ProjectSettings, stmt *ast.AlterSettingsStmt) error { - e := ctx.executor if ps.Configuration == nil { return mdlerrors.NewNotFound("settings section", "configuration") } @@ -357,7 +352,7 @@ func alterSettingsConstant(ctx *ExecContext, ps *model.ProjectSettings, stmt *as for i, cv := range cfg.ConstantValues { if cv.ConstantId == stmt.ConstantId { cfg.ConstantValues = append(cfg.ConstantValues[:i], cfg.ConstantValues[i+1:]...) - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } fmt.Fprintf(ctx.Output, "Dropped constant '%s' from configuration '%s'\n", @@ -386,7 +381,7 @@ func alterSettingsConstant(ctx *ExecContext, ps *model.ProjectSettings, stmt *as cfg.ConstantValues = append(cfg.ConstantValues, cv) } - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } @@ -397,12 +392,11 @@ func alterSettingsConstant(ctx *ExecContext, ps *model.ProjectSettings, stmt *as // createConfiguration handles CREATE CONFIGURATION 'name' [properties...]. func createConfiguration(ctx *ExecContext, stmt *ast.CreateConfigurationStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -457,7 +451,7 @@ func createConfiguration(ctx *ExecContext, stmt *ast.CreateConfigurationStmt) er ps.Configuration.Configurations = append(ps.Configuration.Configurations, newCfg) - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } @@ -467,12 +461,11 @@ func createConfiguration(ctx *ExecContext, stmt *ast.CreateConfigurationStmt) er // dropConfiguration handles DROP CONFIGURATION 'name'. func dropConfiguration(ctx *ExecContext, stmt *ast.DropConfigurationStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } - ps, err := e.reader.GetProjectSettings() + ps, err := ctx.Backend.GetProjectSettings() if err != nil { return mdlerrors.NewBackend("read project settings", err) } @@ -487,7 +480,7 @@ func dropConfiguration(ctx *ExecContext, stmt *ast.DropConfigurationStmt) error ps.Configuration.Configurations[:i], ps.Configuration.Configurations[i+1:]..., ) - if err := e.writer.UpdateProjectSettings(ps); err != nil { + if err := ctx.Backend.UpdateProjectSettings(ps); err != nil { return mdlerrors.NewBackend("update project settings", err) } fmt.Fprintf(ctx.Output, "Dropped configuration: %s\n", stmt.Name) diff --git a/mdl/executor/cmd_snippets.go b/mdl/executor/cmd_snippets.go index e16148c7..4a00e401 100644 --- a/mdl/executor/cmd_snippets.go +++ b/mdl/executor/cmd_snippets.go @@ -13,7 +13,6 @@ import ( // showSnippets handles SHOW SNIPPETS command. func showSnippets(ctx *ExecContext, moduleName string) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -21,7 +20,7 @@ func showSnippets(ctx *ExecContext, moduleName string) error { } // Get all snippets - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } diff --git a/mdl/executor/cmd_structure.go b/mdl/executor/cmd_structure.go index 08d692eb..fa26ffac 100644 --- a/mdl/executor/cmd_structure.go +++ b/mdl/executor/cmd_structure.go @@ -274,7 +274,6 @@ func queryCountByModule(ctx *ExecContext, tableAndWhere string) map[string]int { // countByModuleFromReader counts elements per module using the reader (for types without catalog tables). func countByModuleFromReader(ctx *ExecContext, kind string) map[string]int { - e := ctx.executor counts := make(map[string]int) h, err := getHierarchy(ctx) if err != nil { @@ -283,7 +282,7 @@ func countByModuleFromReader(ctx *ExecContext, kind string) map[string]int { switch kind { case "constants": - if constants, err := e.reader.ListConstants(); err == nil { + if constants, err := ctx.Backend.ListConstants(); err == nil { for _, c := range constants { modID := h.FindModuleID(c.ContainerID) modName := h.GetModuleName(modID) @@ -291,7 +290,7 @@ func countByModuleFromReader(ctx *ExecContext, kind string) map[string]int { } } case "scheduled_events": - if events, err := e.reader.ListScheduledEvents(); err == nil { + if events, err := ctx.Backend.ListScheduledEvents(); err == nil { for _, ev := range events { modID := h.FindModuleID(ev.ContainerID) modName := h.GetModuleName(modID) @@ -315,7 +314,6 @@ func pluralize(count int, singular, plural string) string { // ============================================================================ func structureDepth2(ctx *ExecContext, modules []structureModule) error { - e := ctx.executor // Pre-load data that needs the reader h, err := getHierarchy(ctx) if err != nil { @@ -323,7 +321,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load domain models for associations - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() dmByModule := make(map[string]*domainmodel.DomainModel) for _, dm := range domainModels { modID := h.FindModuleID(dm.ContainerID) @@ -332,7 +330,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load enumerations for values - allEnums, _ := e.reader.ListEnumerations() + allEnums, _ := ctx.Backend.ListEnumerations() enumsByModule := make(map[string][]*model.Enumeration) for _, enum := range allEnums { modID := h.FindModuleID(enum.ContainerID) @@ -341,7 +339,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load microflows for parameter types - allMicroflows, _ := e.reader.ListMicroflows() + allMicroflows, _ := ctx.Backend.ListMicroflows() mfByModule := make(map[string][]*microflows.Microflow) for _, mf := range allMicroflows { modID := h.FindModuleID(mf.ContainerID) @@ -350,7 +348,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load nanoflows - allNanoflows, _ := e.reader.ListNanoflows() + allNanoflows, _ := ctx.Backend.ListNanoflows() nfByModule := make(map[string][]*microflows.Nanoflow) for _, nf := range allNanoflows { modID := h.FindModuleID(nf.ContainerID) @@ -359,7 +357,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load constants - allConstants, _ := e.reader.ListConstants() + allConstants, _ := ctx.Backend.ListConstants() constByModule := make(map[string][]*model.Constant) for _, c := range allConstants { modID := h.FindModuleID(c.ContainerID) @@ -368,7 +366,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load scheduled events - allEvents, _ := e.reader.ListScheduledEvents() + allEvents, _ := ctx.Backend.ListScheduledEvents() eventsByModule := make(map[string][]*model.ScheduledEvent) for _, ev := range allEvents { modID := h.FindModuleID(ev.ContainerID) @@ -377,7 +375,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load java actions for parameter types - allJavaActions, _ := e.reader.ListJavaActionsFull() + allJavaActions, _ := ctx.Backend.ListJavaActionsFull() jaByModule := make(map[string][]*javaactions.JavaAction) for _, ja := range allJavaActions { modID := h.FindModuleID(ja.ContainerID) @@ -386,7 +384,7 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { } // Load workflows - allWorkflows, _ := e.reader.ListWorkflows() + allWorkflows, _ := ctx.Backend.ListWorkflows() wfByModule := make(map[string][]*workflows.Workflow) for _, wf := range allWorkflows { modID := h.FindModuleID(wf.ContainerID) @@ -477,14 +475,13 @@ func structureDepth2(ctx *ExecContext, modules []structureModule) error { // ============================================================================ func structureDepth3(ctx *ExecContext, modules []structureModule) error { - e := ctx.executor // Same data loading as depth 2 h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - domainModels, _ := e.reader.ListDomainModels() + domainModels, _ := ctx.Backend.ListDomainModels() dmByModule := make(map[string]*domainmodel.DomainModel) for _, dm := range domainModels { modID := h.FindModuleID(dm.ContainerID) @@ -492,7 +489,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { dmByModule[modName] = dm } - allEnums, _ := e.reader.ListEnumerations() + allEnums, _ := ctx.Backend.ListEnumerations() enumsByModule := make(map[string][]*model.Enumeration) for _, enum := range allEnums { modID := h.FindModuleID(enum.ContainerID) @@ -500,7 +497,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { enumsByModule[modName] = append(enumsByModule[modName], enum) } - allMicroflows, _ := e.reader.ListMicroflows() + allMicroflows, _ := ctx.Backend.ListMicroflows() mfByModule := make(map[string][]*microflows.Microflow) for _, mf := range allMicroflows { modID := h.FindModuleID(mf.ContainerID) @@ -508,7 +505,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { mfByModule[modName] = append(mfByModule[modName], mf) } - allNanoflows, _ := e.reader.ListNanoflows() + allNanoflows, _ := ctx.Backend.ListNanoflows() nfByModule := make(map[string][]*microflows.Nanoflow) for _, nf := range allNanoflows { modID := h.FindModuleID(nf.ContainerID) @@ -516,7 +513,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { nfByModule[modName] = append(nfByModule[modName], nf) } - allConstants, _ := e.reader.ListConstants() + allConstants, _ := ctx.Backend.ListConstants() constByModule := make(map[string][]*model.Constant) for _, c := range allConstants { modID := h.FindModuleID(c.ContainerID) @@ -524,7 +521,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { constByModule[modName] = append(constByModule[modName], c) } - allEvents, _ := e.reader.ListScheduledEvents() + allEvents, _ := ctx.Backend.ListScheduledEvents() eventsByModule := make(map[string][]*model.ScheduledEvent) for _, ev := range allEvents { modID := h.FindModuleID(ev.ContainerID) @@ -532,7 +529,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { eventsByModule[modName] = append(eventsByModule[modName], ev) } - allJavaActions, _ := e.reader.ListJavaActionsFull() + allJavaActions, _ := ctx.Backend.ListJavaActionsFull() jaByModule := make(map[string][]*javaactions.JavaAction) for _, ja := range allJavaActions { modID := h.FindModuleID(ja.ContainerID) @@ -541,7 +538,7 @@ func structureDepth3(ctx *ExecContext, modules []structureModule) error { } // Load workflows - allWorkflows, _ := e.reader.ListWorkflows() + allWorkflows, _ := ctx.Backend.ListWorkflows() wfByModule := make(map[string][]*workflows.Workflow) for _, wf := range allWorkflows { modID := h.FindModuleID(wf.ContainerID) diff --git a/mdl/executor/cmd_styling.go b/mdl/executor/cmd_styling.go index 5f6f8a75..6be8b460 100644 --- a/mdl/executor/cmd_styling.go +++ b/mdl/executor/cmd_styling.go @@ -20,13 +20,12 @@ import ( // ============================================================================ func execShowDesignProperties(ctx *ExecContext, s *ast.ShowDesignPropertiesStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } - projectDir := filepath.Dir(e.mprPath) + projectDir := filepath.Dir(ctx.MprPath) registry, err := loadThemeRegistry(projectDir) if err != nil { return mdlerrors.NewBackend("load theme registry", err) @@ -113,7 +112,6 @@ func printOneProperty(ctx *ExecContext, p ThemeProperty) { // ============================================================================ func execDescribeStyling(ctx *ExecContext, s *ast.DescribeStylingStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() @@ -128,7 +126,7 @@ func execDescribeStyling(ctx *ExecContext, s *ast.DescribeStylingStmt) error { if s.ContainerType == "PAGE" { // Find page - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -148,7 +146,7 @@ func execDescribeStyling(ctx *ExecContext, s *ast.DescribeStylingStmt) error { rawWidgets = getPageWidgetsFromRaw(ctx, foundPage.ID) } else if s.ContainerType == "SNIPPET" { // Find snippet - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } @@ -280,10 +278,9 @@ func execAlterStyling(ctx *ExecContext, s *ast.AlterStylingStmt) error { } func alterStylingOnPage(ctx *ExecContext, s *ast.AlterStylingStmt, h *ContainerHierarchy) error { - e := ctx.executor // Find page - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return mdlerrors.NewBackend("list pages", err) } @@ -320,7 +317,7 @@ func alterStylingOnPage(ctx *ExecContext, s *ast.AlterStylingStmt, h *ContainerH } // Save the page - if err := e.writer.UpdatePage(page); err != nil { + if err := ctx.Backend.UpdatePage(page); err != nil { return mdlerrors.NewBackend("save page", err) } @@ -329,10 +326,9 @@ func alterStylingOnPage(ctx *ExecContext, s *ast.AlterStylingStmt, h *ContainerH } func alterStylingOnSnippet(ctx *ExecContext, s *ast.AlterStylingStmt, h *ContainerHierarchy) error { - e := ctx.executor // Find snippet - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return mdlerrors.NewBackend("list snippets", err) } @@ -369,7 +365,7 @@ func alterStylingOnSnippet(ctx *ExecContext, s *ast.AlterStylingStmt, h *Contain } // Save the snippet - if err := e.writer.UpdateSnippet(snippet); err != nil { + if err := ctx.Backend.UpdateSnippet(snippet); err != nil { return mdlerrors.NewBackend("save snippet", err) } @@ -519,9 +515,8 @@ func setDesignProperty(baseWidget reflect.Value, a ast.StylingAssignment) error // findPageByName looks up a page by qualified name. func findPageByName(ctx *ExecContext, name ast.QualifiedName, h *ContainerHierarchy) (*pages.Page, error) { - e := ctx.executor - allPages, err := e.reader.ListPages() + allPages, err := ctx.Backend.ListPages() if err != nil { return nil, mdlerrors.NewBackend("list pages", err) } @@ -537,9 +532,8 @@ func findPageByName(ctx *ExecContext, name ast.QualifiedName, h *ContainerHierar // findSnippetByName looks up a snippet by qualified name. func findSnippetByName(ctx *ExecContext, name ast.QualifiedName, h *ContainerHierarchy) (*pages.Snippet, model.ID, error) { - e := ctx.executor - allSnippets, err := e.reader.ListSnippets() + allSnippets, err := ctx.Backend.ListSnippets() if err != nil { return nil, "", mdlerrors.NewBackend("list snippets", err) } diff --git a/mdl/executor/cmd_widgets.go b/mdl/executor/cmd_widgets.go index 42f4c61e..ac037c35 100644 --- a/mdl/executor/cmd_widgets.go +++ b/mdl/executor/cmd_widgets.go @@ -218,10 +218,9 @@ func updateWidgetsInContainer(ctx *ExecContext, containerID string, widgetRefs [ // updateWidgetsInPage updates widgets in a page using raw BSON. func updateWidgetsInPage(ctx *ExecContext, containerID, containerName string, widgetRefs []widgetRef, assignments []ast.WidgetPropertyAssignment, dryRun bool) (int, error) { - e := ctx.executor // Load raw BSON as ordered document (preserves field ordering) - rawBytes, err := e.reader.GetRawUnitBytes(model.ID(containerID)) + rawBytes, err := ctx.Backend.GetRawUnitBytes(model.ID(containerID)) if err != nil { return 0, mdlerrors.NewBackend(fmt.Sprintf("load page %s", containerName), err) } @@ -257,7 +256,7 @@ func updateWidgetsInPage(ctx *ExecContext, containerID, containerName string, wi if err != nil { return updated, mdlerrors.NewBackend(fmt.Sprintf("marshal page %s", containerName), err) } - if err := e.writer.UpdateRawUnit(containerID, outBytes); err != nil { + if err := ctx.Backend.UpdateRawUnit(containerID, outBytes); err != nil { return updated, mdlerrors.NewBackend(fmt.Sprintf("save page %s", containerName), err) } } @@ -267,10 +266,9 @@ func updateWidgetsInPage(ctx *ExecContext, containerID, containerName string, wi // updateWidgetsInSnippet updates widgets in a snippet using raw BSON. func updateWidgetsInSnippet(ctx *ExecContext, containerID, containerName string, widgetRefs []widgetRef, assignments []ast.WidgetPropertyAssignment, dryRun bool) (int, error) { - e := ctx.executor // Load raw BSON as ordered document (preserves field ordering) - rawBytes, err := e.reader.GetRawUnitBytes(model.ID(containerID)) + rawBytes, err := ctx.Backend.GetRawUnitBytes(model.ID(containerID)) if err != nil { return 0, mdlerrors.NewBackend(fmt.Sprintf("load snippet %s", containerName), err) } @@ -306,7 +304,7 @@ func updateWidgetsInSnippet(ctx *ExecContext, containerID, containerName string, if err != nil { return updated, mdlerrors.NewBackend(fmt.Sprintf("marshal snippet %s", containerName), err) } - if err := e.writer.UpdateRawUnit(containerID, outBytes); err != nil { + if err := ctx.Backend.UpdateRawUnit(containerID, outBytes); err != nil { return updated, mdlerrors.NewBackend(fmt.Sprintf("save snippet %s", containerName), err) } } diff --git a/mdl/executor/cmd_workflows.go b/mdl/executor/cmd_workflows.go index fc0c391b..51cf125b 100644 --- a/mdl/executor/cmd_workflows.go +++ b/mdl/executor/cmd_workflows.go @@ -15,13 +15,12 @@ import ( // showWorkflows handles SHOW WORKFLOWS command. func showWorkflows(ctx *ExecContext, moduleName string) error { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return mdlerrors.NewBackend("build hierarchy", err) } - wfs, err := e.reader.ListWorkflows() + wfs, err := ctx.Backend.ListWorkflows() if err != nil { return mdlerrors.NewBackend("list workflows", err) } @@ -143,13 +142,12 @@ func describeWorkflow(ctx *ExecContext, name ast.QualifiedName) error { // describeWorkflowToString generates MDL-like output for a workflow and returns it as a string. func describeWorkflowToString(ctx *ExecContext, name ast.QualifiedName) (string, map[string]elkSourceRange, error) { - e := ctx.executor h, err := getHierarchy(ctx) if err != nil { return "", nil, mdlerrors.NewBackend("build hierarchy", err) } - allWorkflows, err := e.reader.ListWorkflows() + allWorkflows, err := ctx.Backend.ListWorkflows() if err != nil { return "", nil, mdlerrors.NewBackend("list workflows", err) } diff --git a/mdl/executor/cmd_workflows_write.go b/mdl/executor/cmd_workflows_write.go index 63994291..d63e2789 100644 --- a/mdl/executor/cmd_workflows_write.go +++ b/mdl/executor/cmd_workflows_write.go @@ -33,7 +33,7 @@ func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { return mdlerrors.NewBackend("build hierarchy", err) } - existingWorkflows, err := e.reader.ListWorkflows() + existingWorkflows, err := ctx.Backend.ListWorkflows() if err != nil { return mdlerrors.NewBackend("list workflows", err) } @@ -114,12 +114,12 @@ func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { if existingID != "" { // Delete existing and recreate - if err := e.writer.DeleteWorkflow(existingID); err != nil { + if err := ctx.Backend.DeleteWorkflow(existingID); err != nil { return mdlerrors.NewBackend("delete existing workflow", err) } } - if err := e.writer.CreateWorkflow(wf); err != nil { + if err := ctx.Backend.CreateWorkflow(wf); err != nil { return mdlerrors.NewBackend("create workflow", err) } @@ -130,7 +130,6 @@ func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { // execDropWorkflow handles DROP WORKFLOW statements. func execDropWorkflow(ctx *ExecContext, s *ast.DropWorkflowStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -140,7 +139,7 @@ func execDropWorkflow(ctx *ExecContext, s *ast.DropWorkflowStmt) error { return mdlerrors.NewBackend("build hierarchy", err) } - wfs, err := e.reader.ListWorkflows() + wfs, err := ctx.Backend.ListWorkflows() if err != nil { return mdlerrors.NewBackend("list workflows", err) } @@ -149,7 +148,7 @@ func execDropWorkflow(ctx *ExecContext, s *ast.DropWorkflowStmt) error { modID := h.FindModuleID(wf.ContainerID) modName := h.GetModuleName(modID) if modName == s.Name.Module && wf.Name == s.Name.Name { - if err := e.writer.DeleteWorkflow(wf.ID); err != nil { + if err := ctx.Backend.DeleteWorkflow(wf.ID); err != nil { return mdlerrors.NewBackend("delete workflow", err) } invalidateHierarchy(ctx) diff --git a/mdl/executor/format.go b/mdl/executor/format.go index 98269be7..c0a8ddc8 100644 --- a/mdl/executor/format.go +++ b/mdl/executor/format.go @@ -125,8 +125,8 @@ func writeDescribeJSON(ctx *ExecContext, name, objectType string, fn func() erro origEOutput := e.output origGuard := e.guard ctx.Output = &buf - e.output = &buf - e.guard = nil // disable line guard for capture + e.output = &buf // sync executor output for closures that write to e.output + e.guard = nil // disable line guard for capture err := fn() ctx.Output = origOutput e.output = origEOutput diff --git a/mdl/executor/helpers.go b/mdl/executor/helpers.go index 9f6ba397..d8e34102 100644 --- a/mdl/executor/helpers.go +++ b/mdl/executor/helpers.go @@ -21,11 +21,10 @@ import ( // getModulesFromCache returns cached modules or loads them. func getModulesFromCache(ctx *ExecContext) ([]*model.Module, error) { - e := ctx.executor if ctx.Cache != nil && ctx.Cache.modules != nil { return ctx.Cache.modules, nil } - modules, err := e.reader.ListModules() + modules, err := ctx.Backend.ListModules() if err != nil { return nil, err } @@ -100,12 +99,11 @@ func findModuleByID(ctx *ExecContext, id model.ID) (*model.Module, error) { // resolveFolder resolves a folder path (e.g., "Resources/Images") to a folder ID. // The path is relative to the given module. If the folder doesn't exist, it creates it. func resolveFolder(ctx *ExecContext, moduleID model.ID, folderPath string) (model.ID, error) { - e := ctx.executor if folderPath == "" { return moduleID, nil } - folders, err := e.reader.ListFolders() + folders, err := ctx.Backend.ListFolders() if err != nil { return "", mdlerrors.NewBackend("list folders", err) } @@ -153,7 +151,6 @@ func resolveFolder(ctx *ExecContext, moduleID model.ID, folderPath string) (mode // createFolder creates a new folder in the project. func createFolder(ctx *ExecContext, name string, containerID model.ID) (model.ID, error) { - e := ctx.executor folder := &model.Folder{ BaseElement: model.BaseElement{ ID: model.ID(mpr.GenerateID()), @@ -163,7 +160,7 @@ func createFolder(ctx *ExecContext, name string, containerID model.ID) (model.ID Name: name, } - if err := e.writer.CreateFolder(folder); err != nil { + if err := ctx.Backend.CreateFolder(folder); err != nil { return "", err } @@ -176,7 +173,6 @@ func createFolder(ctx *ExecContext, name string, containerID model.ID) (model.ID // enumerationExists checks if an enumeration exists in the project. func enumerationExists(ctx *ExecContext, qualifiedName string) bool { - e := ctx.executor if !ctx.Connected() { return false } @@ -195,7 +191,7 @@ func enumerationExists(ctx *ExecContext, qualifiedName string) bool { } // Get all enumerations and check if one matches - enums, err := e.reader.ListEnumerations() + enums, err := ctx.Backend.ListEnumerations() if err != nil { return false } @@ -363,13 +359,12 @@ func (c *widgetRefCollector) collectFromAction(action *ast.ActionV3) { // buildMicroflowQualifiedNames returns a set of all microflow qualified names in the project. func buildMicroflowQualifiedNames(ctx *ExecContext) map[string]bool { - e := ctx.executor result := make(map[string]bool) h, err := getHierarchy(ctx) if err != nil { return result } - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return result } @@ -382,13 +377,12 @@ func buildMicroflowQualifiedNames(ctx *ExecContext) map[string]bool { // buildNanoflowQualifiedNames returns a set of all nanoflow qualified names in the project. func buildNanoflowQualifiedNames(ctx *ExecContext) map[string]bool { - e := ctx.executor result := make(map[string]bool) h, err := getHierarchy(ctx) if err != nil { return result } - nfs, err := e.reader.ListNanoflows() + nfs, err := ctx.Backend.ListNanoflows() if err != nil { return result } @@ -401,13 +395,12 @@ func buildNanoflowQualifiedNames(ctx *ExecContext) map[string]bool { // buildPageQualifiedNames returns a set of all page qualified names in the project. func buildPageQualifiedNames(ctx *ExecContext) map[string]bool { - e := ctx.executor result := make(map[string]bool) h, err := getHierarchy(ctx) if err != nil { return result } - pgs, err := e.reader.ListPages() + pgs, err := ctx.Backend.ListPages() if err != nil { return result } @@ -420,13 +413,12 @@ func buildPageQualifiedNames(ctx *ExecContext) map[string]bool { // buildSnippetQualifiedNames returns a set of all snippet qualified names in the project. func buildSnippetQualifiedNames(ctx *ExecContext) map[string]bool { - e := ctx.executor result := make(map[string]bool) h, err := getHierarchy(ctx) if err != nil { return result } - snippets, err := e.reader.ListSnippets() + snippets, err := ctx.Backend.ListSnippets() if err != nil { return result } @@ -444,12 +436,11 @@ func buildEntityQualifiedNames(ctx *ExecContext) map[string]bool { if err != nil { return result } - e := ctx.executor moduleNames := make(map[model.ID]string) for _, m := range modules { moduleNames[m.ID] = m.Name } - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return result } @@ -467,13 +458,12 @@ func buildEntityQualifiedNames(ctx *ExecContext) map[string]bool { // buildJavaActionQualifiedNames returns a set of all java action qualified names in the project. func buildJavaActionQualifiedNames(ctx *ExecContext) map[string]bool { - e := ctx.executor result := make(map[string]bool) h, err := getHierarchy(ctx) if err != nil { return result } - jas, err := e.reader.ListJavaActions() + jas, err := ctx.Backend.ListJavaActions() if err != nil { return result } diff --git a/mdl/executor/oql_type_inference.go b/mdl/executor/oql_type_inference.go index d0279e98..739af692 100644 --- a/mdl/executor/oql_type_inference.go +++ b/mdl/executor/oql_type_inference.go @@ -658,9 +658,8 @@ func inferAttributeType(ctx *ExecContext, attrPath string, col *OQLColumnInfo) a // findEntity looks up an entity by module and name. func findEntity(ctx *ExecContext, moduleName, entityName string) (*domainmodel.Entity, error) { - e := ctx.executor // Get all entities - dms, err := e.reader.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() if err != nil { return nil, err } From 702c377c2e18706d1d063eeb18610a892266878d Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 19:55:46 +0200 Subject: [PATCH 4/6] refactor: convert name lookups and theme registry to free functions using ExecContext --- mdl/executor/cmd_alter_page.go | 2 +- mdl/executor/cmd_fragments.go | 3 +-- mdl/executor/cmd_microflows_show.go | 5 ++-- mdl/executor/cmd_pages_create_v3.go | 4 +-- mdl/executor/exec_context.go | 18 +++++++++++++ mdl/executor/executor.go | 41 +++++++++-------------------- mdl/executor/validate.go | 2 -- 7 files changed, 36 insertions(+), 39 deletions(-) diff --git a/mdl/executor/cmd_alter_page.go b/mdl/executor/cmd_alter_page.go index 26ed1245..900be082 100644 --- a/mdl/executor/cmd_alter_page.go +++ b/mdl/executor/cmd_alter_page.go @@ -1548,7 +1548,7 @@ func buildWidgetsBson(ctx *ExecContext, widgets []*ast.WidgetV3, moduleName stri paramEntityNames: paramEntityNames, execCache: ctx.Cache, fragments: ctx.Fragments, - themeRegistry: e.getThemeRegistry(), + themeRegistry: ctx.GetThemeRegistry(), } var result []any diff --git a/mdl/executor/cmd_fragments.go b/mdl/executor/cmd_fragments.go index 1ca098c5..583b1d38 100644 --- a/mdl/executor/cmd_fragments.go +++ b/mdl/executor/cmd_fragments.go @@ -72,7 +72,6 @@ func describeFragment(ctx *ExecContext, name ast.QualifiedName) error { // describeFragmentFrom handles DESCRIBE FRAGMENT FROM PAGE/SNIPPET ... WIDGET ... command. // It finds a named widget in a page or snippet and outputs it as MDL. func describeFragmentFrom(ctx *ExecContext, s *ast.DescribeFragmentFromStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -131,7 +130,7 @@ func describeFragmentFrom(ctx *ExecContext, s *ast.DescribeFragmentFromStmt) err } // Output as MDL - e.outputWidgetMDLV3(*target, 0) + outputWidgetMDLV3(ctx, *target, 0) return nil } diff --git a/mdl/executor/cmd_microflows_show.go b/mdl/executor/cmd_microflows_show.go index 04109465..a2a91d3e 100644 --- a/mdl/executor/cmd_microflows_show.go +++ b/mdl/executor/cmd_microflows_show.go @@ -178,7 +178,6 @@ func calculateNanoflowComplexity(nf *microflows.Nanoflow) int { // describeMicroflow handles DESCRIBE MICROFLOW command - outputs MDL source code. func describeMicroflow(ctx *ExecContext, name ast.QualifiedName) error { - e := ctx.executor // Get hierarchy for module/folder resolution h, err := getHierarchy(ctx) if err != nil { @@ -186,8 +185,8 @@ func describeMicroflow(ctx *ExecContext, name ast.QualifiedName) error { } // Use pre-warmed cache if available (from PreWarmCache), otherwise build on demand - entityNames := e.getEntityNames(h) - microflowNames := e.getMicroflowNames(h) + entityNames := getEntityNames(ctx, h) + microflowNames := getMicroflowNames(ctx, h) // Find the microflow allMicroflows, err := ctx.Backend.ListMicroflows() diff --git a/mdl/executor/cmd_pages_create_v3.go b/mdl/executor/cmd_pages_create_v3.go index e993ceef..398ecefb 100644 --- a/mdl/executor/cmd_pages_create_v3.go +++ b/mdl/executor/cmd_pages_create_v3.go @@ -62,7 +62,7 @@ func execCreatePageV3(ctx *ExecContext, s *ast.CreatePageStmtV3) error { paramEntityNames: make(map[string]string), execCache: ctx.Cache, fragments: ctx.Fragments, - themeRegistry: e.getThemeRegistry(), + themeRegistry: ctx.GetThemeRegistry(), } page, err := pb.buildPageV3(s) @@ -138,7 +138,7 @@ func execCreateSnippetV3(ctx *ExecContext, s *ast.CreateSnippetStmtV3) error { paramEntityNames: make(map[string]string), execCache: ctx.Cache, fragments: ctx.Fragments, - themeRegistry: e.getThemeRegistry(), + themeRegistry: ctx.GetThemeRegistry(), } snippet, err := pb.buildSnippetV3(s) diff --git a/mdl/executor/exec_context.go b/mdl/executor/exec_context.go index 0d3a4157..db86a93a 100644 --- a/mdl/executor/exec_context.go +++ b/mdl/executor/exec_context.go @@ -5,6 +5,7 @@ package executor import ( "context" "io" + "path/filepath" "github.com/mendixlabs/mxcli/mdl/ast" "github.com/mendixlabs/mxcli/mdl/backend" @@ -80,3 +81,20 @@ func (ctx *ExecContext) Connected() bool { func (ctx *ExecContext) ConnectedForWrite() bool { return ctx.Connected() } + +// GetThemeRegistry returns the cached theme registry, loading it lazily +// from the project's theme sources on first access. +func (ctx *ExecContext) GetThemeRegistry() *ThemeRegistry { + if ctx.ThemeRegistry != nil { + return ctx.ThemeRegistry + } + if ctx.MprPath == "" { + return nil + } + projectDir := filepath.Dir(ctx.MprPath) + registry, err := loadThemeRegistry(projectDir) + if err == nil { + ctx.ThemeRegistry = registry + } + return ctx.ThemeRegistry +} diff --git a/mdl/executor/executor.go b/mdl/executor/executor.go index 96ab981d..07387da4 100644 --- a/mdl/executor/executor.go +++ b/mdl/executor/executor.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io" - "path/filepath" "time" "github.com/mendixlabs/mxcli/mdl/ast" @@ -70,12 +69,12 @@ type createdSnippetInfo struct { } // getEntityNames returns the entity name lookup map, using the pre-warmed cache if available. -func (e *Executor) getEntityNames(h *ContainerHierarchy) map[model.ID]string { - if e.cache != nil && len(e.cache.entityNames) > 0 { - return e.cache.entityNames +func getEntityNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]string { + if ctx.Cache != nil && len(ctx.Cache.entityNames) > 0 { + return ctx.Cache.entityNames } entityNames := make(map[model.ID]string) - dms, _ := e.reader.ListDomainModels() + dms, _ := ctx.Backend.ListDomainModels() for _, dm := range dms { modName := h.GetModuleName(dm.ContainerID) for _, ent := range dm.Entities { @@ -86,12 +85,12 @@ func (e *Executor) getEntityNames(h *ContainerHierarchy) map[model.ID]string { } // getMicroflowNames returns the microflow name lookup map, using the pre-warmed cache if available. -func (e *Executor) getMicroflowNames(h *ContainerHierarchy) map[model.ID]string { - if e.cache != nil && len(e.cache.microflowNames) > 0 { - return e.cache.microflowNames +func getMicroflowNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]string { + if ctx.Cache != nil && len(ctx.Cache.microflowNames) > 0 { + return ctx.Cache.microflowNames } microflowNames := make(map[model.ID]string) - mfs, _ := e.reader.ListMicroflows() + mfs, _ := ctx.Backend.ListMicroflows() for _, mf := range mfs { microflowNames[mf.ID] = h.GetQualifiedName(mf.ContainerID, mf.Name) } @@ -99,12 +98,12 @@ func (e *Executor) getMicroflowNames(h *ContainerHierarchy) map[model.ID]string } // getPageNames returns the page name lookup map, using the pre-warmed cache if available. -func (e *Executor) getPageNames(h *ContainerHierarchy) map[model.ID]string { - if e.cache != nil && len(e.cache.pageNames) > 0 { - return e.cache.pageNames +func getPageNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]string { + if ctx.Cache != nil && len(ctx.Cache.pageNames) > 0 { + return ctx.Cache.pageNames } pageNames := make(map[model.ID]string) - pgs, _ := e.reader.ListPages() + pgs, _ := ctx.Backend.ListPages() for _, pg := range pgs { pageNames[pg.ID] = h.GetQualifiedName(pg.ContainerID, pg.Name) } @@ -150,22 +149,6 @@ func New(output io.Writer) *Executor { } } -// getThemeRegistry returns the cached theme registry, loading it lazily from the project's theme sources. -func (e *Executor) getThemeRegistry() *ThemeRegistry { - if e.themeRegistry != nil { - return e.themeRegistry - } - if e.mprPath == "" { - return nil - } - projectDir := filepath.Dir(e.mprPath) - registry, err := loadThemeRegistry(projectDir) - if err == nil { - e.themeRegistry = registry - } - return e.themeRegistry -} - // SetQuiet enables or disables quiet mode (suppresses connection/status messages). func (e *Executor) SetQuiet(quiet bool) { e.quiet = quiet diff --git a/mdl/executor/validate.go b/mdl/executor/validate.go index 9bd82e06..0f5c482b 100644 --- a/mdl/executor/validate.go +++ b/mdl/executor/validate.go @@ -180,7 +180,6 @@ func (e *Executor) ValidateProgram(prog *ast.Program) []error { // validateWithContext validates a statement, considering objects defined in the script. func validateWithContext(ctx *ExecContext, stmt ast.Statement, sc *scriptContext) error { - e := ctx.executor switch s := stmt.(type) { // Statements that reference modules case *ast.CreateEntityStmt: @@ -370,7 +369,6 @@ func validateWithContext(ctx *ExecContext, stmt ast.Statement, sc *scriptContext return nil } - _ = e // suppress unused warning if e not referenced in all paths return nil } From 437c3520a7e9a95b94d848494ada2b47dd8b0d6e Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 20:03:25 +0200 Subject: [PATCH 5/6] feat: add mock-based handler tests for showEnumerations Demonstrate that handlers can now be tested without a real .mpr file by using MockBackend with a pre-populated ContainerHierarchy cache. Two tests verify table output and module filtering. --- mdl/executor/cmd_enumerations_mock_test.go | 131 +++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 mdl/executor/cmd_enumerations_mock_test.go diff --git a/mdl/executor/cmd_enumerations_mock_test.go b/mdl/executor/cmd_enumerations_mock_test.go new file mode 100644 index 00000000..57ef46d4 --- /dev/null +++ b/mdl/executor/cmd_enumerations_mock_test.go @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: Apache-2.0 + +package executor + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/mendixlabs/mxcli/mdl/backend/mock" + "github.com/mendixlabs/mxcli/model" +) + +// TestShowEnumerations_Mock demonstrates testing a handler with a MockBackend +// instead of a real .mpr file. The handler under test is showEnumerations, +// which calls ctx.Backend.ListEnumerations() and writes a table to ctx.Output. +func TestShowEnumerations_Mock(t *testing.T) { + modID := model.ID("mod-1") + enumID := model.ID("enum-1") + + mb := &mock.MockBackend{ + IsConnectedFunc: func() bool { return true }, + ListEnumerationsFunc: func() ([]*model.Enumeration, error) { + return []*model.Enumeration{ + { + BaseElement: model.BaseElement{ID: enumID}, + ContainerID: modID, + Name: "Color", + Values: []model.EnumerationValue{ + {Name: "Red"}, + {Name: "Green"}, + {Name: "Blue"}, + }, + }, + }, nil + }, + } + + // Pre-populate hierarchy so getHierarchy skips the e.reader path. + hierarchy := &ContainerHierarchy{ + moduleIDs: map[model.ID]bool{modID: true}, + moduleNames: map[model.ID]string{modID: "MyModule"}, + containerParent: map[model.ID]model.ID{enumID: modID}, + folderNames: map[model.ID]string{}, + } + + var buf bytes.Buffer + ctx := &ExecContext{ + Context: context.Background(), + Backend: mb, + Output: &buf, + Format: FormatTable, + Cache: &executorCache{hierarchy: hierarchy}, + } + + if err := showEnumerations(ctx, ""); err != nil { + t.Fatalf("showEnumerations returned error: %v", err) + } + + out := buf.String() + + // Verify table contains our enumeration data. + if !strings.Contains(out, "MyModule.Color") { + t.Errorf("expected qualified name 'MyModule.Color' in output, got:\n%s", out) + } + if !strings.Contains(out, "3") { + t.Errorf("expected value count '3' in output, got:\n%s", out) + } + if !strings.Contains(out, "(1 enumerations)") { + t.Errorf("expected summary '(1 enumerations)' in output, got:\n%s", out) + } +} + +// TestShowEnumerations_Mock_FilterByModule verifies that passing a module name +// filters the output to only that module's enumerations. +func TestShowEnumerations_Mock_FilterByModule(t *testing.T) { + mod1 := model.ID("mod-1") + mod2 := model.ID("mod-2") + + mb := &mock.MockBackend{ + IsConnectedFunc: func() bool { return true }, + ListEnumerationsFunc: func() ([]*model.Enumeration, error) { + return []*model.Enumeration{ + { + BaseElement: model.BaseElement{ID: model.ID("e1")}, + ContainerID: mod1, + Name: "Color", + Values: []model.EnumerationValue{{Name: "Red"}}, + }, + { + BaseElement: model.BaseElement{ID: model.ID("e2")}, + ContainerID: mod2, + Name: "Size", + Values: []model.EnumerationValue{{Name: "S"}, {Name: "M"}}, + }, + }, nil + }, + } + + hierarchy := &ContainerHierarchy{ + moduleIDs: map[model.ID]bool{mod1: true, mod2: true}, + moduleNames: map[model.ID]string{mod1: "Alpha", mod2: "Beta"}, + containerParent: map[model.ID]model.ID{}, + folderNames: map[model.ID]string{}, + } + + var buf bytes.Buffer + ctx := &ExecContext{ + Context: context.Background(), + Backend: mb, + Output: &buf, + Format: FormatTable, + Cache: &executorCache{hierarchy: hierarchy}, + } + + if err := showEnumerations(ctx, "Beta"); err != nil { + t.Fatalf("showEnumerations returned error: %v", err) + } + + out := buf.String() + if strings.Contains(out, "Alpha.Color") { + t.Errorf("should not contain Alpha.Color when filtering by Beta:\n%s", out) + } + if !strings.Contains(out, "Beta.Size") { + t.Errorf("expected Beta.Size in output:\n%s", out) + } + if !strings.Contains(out, "(1 enumerations)") { + t.Errorf("expected 1 enumeration in summary:\n%s", out) + } +} From 7fe3fb5f1ed59d8b30a5909cb7021669345bc9fa Mon Sep 17 00:00:00 2001 From: Andrew Vasilyev Date: Fri, 17 Apr 2026 21:59:11 +0200 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20address=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20error=20logging,=20auto-bind=20migration,=20output?= =?UTF-8?q?=20threading,=20disconnect=20ctx=20cleanup,=20cache=20invalidat?= =?UTF-8?q?ion,=20theme=20registry=20persistence,=20parallel=20ctx=20field?= =?UTF-8?q?s,=20settings=20persistence,=20structured=20logging,=20nil-safe?= =?UTF-8?q?ty=20guards,=20project-scoped=20cache=20reset,=20backend-based?= =?UTF-8?q?=20hierarchy,=20enum=20ref=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mdl/catalog/builder.go | 57 ++++++++++++++++++++++++- mdl/executor/cmd_alter_workflow.go | 48 ++++++++++----------- mdl/executor/cmd_alter_workflow_test.go | 7 +-- mdl/executor/cmd_catalog.go | 36 +++++++++++----- mdl/executor/cmd_javaactions.go | 2 +- mdl/executor/cmd_misc.go | 7 +++ mdl/executor/cmd_rename.go | 4 +- mdl/executor/cmd_styling.go | 3 ++ mdl/executor/cmd_workflows_write.go | 37 ++++++++-------- mdl/executor/exec_context.go | 27 ++++++++++-- mdl/executor/executor.go | 33 ++++++++++++-- mdl/executor/executor_connect.go | 31 ++++++++++++++ mdl/executor/format.go | 23 +++++++--- mdl/executor/hierarchy.go | 41 ++++++++++++++++-- 14 files changed, 278 insertions(+), 78 deletions(-) diff --git a/mdl/catalog/builder.go b/mdl/catalog/builder.go index 136e451b..56c62dc9 100644 --- a/mdl/catalog/builder.go +++ b/mdl/catalog/builder.go @@ -10,19 +10,72 @@ import ( "github.com/mendixlabs/mxcli/model" "github.com/mendixlabs/mxcli/sdk/domainmodel" + "github.com/mendixlabs/mxcli/sdk/javaactions" "github.com/mendixlabs/mxcli/sdk/microflows" "github.com/mendixlabs/mxcli/sdk/mpr" "github.com/mendixlabs/mxcli/sdk/pages" + "github.com/mendixlabs/mxcli/sdk/security" "github.com/mendixlabs/mxcli/sdk/workflows" ) +// CatalogReader defines the read-only backend surface used by the catalog builder. +// Both *mpr.Reader and backend.FullBackend satisfy this interface. +type CatalogReader interface { + // Infrastructure + GetRawUnit(id model.ID) (map[string]any, error) + ListRawUnitsByType(typePrefix string) ([]*mpr.RawUnit, error) + ListUnits() ([]*mpr.UnitInfo, error) + ListFolders() ([]*mpr.FolderInfo, error) + + // Modules + ListModules() ([]*model.Module, error) + + // Settings & security + GetProjectSettings() (*model.ProjectSettings, error) + GetProjectSecurity() (*security.ProjectSecurity, error) + GetNavigation() (*mpr.NavigationDocument, error) + + // Domain models & enumerations + ListDomainModels() ([]*domainmodel.DomainModel, error) + ListEnumerations() ([]*model.Enumeration, error) + ListConstants() ([]*model.Constant, error) + + // Microflows & nanoflows + ListMicroflows() ([]*microflows.Microflow, error) + ListNanoflows() ([]*microflows.Nanoflow, error) + + // Pages, layouts & snippets + ListPages() ([]*pages.Page, error) + ListLayouts() ([]*pages.Layout, error) + ListSnippets() ([]*pages.Snippet, error) + + // Workflows + ListWorkflows() ([]*workflows.Workflow, error) + + // Java actions + ListJavaActionsFull() ([]*javaactions.JavaAction, error) + + // Services + ListConsumedODataServices() ([]*model.ConsumedODataService, error) + ListPublishedODataServices() ([]*model.PublishedODataService, error) + ListConsumedRestServices() ([]*model.ConsumedRestService, error) + ListPublishedRestServices() ([]*model.PublishedRestService, error) + ListBusinessEventServices() ([]*model.BusinessEventService, error) + ListDatabaseConnections() ([]*model.DatabaseConnection, error) + + // Mappings & JSON structures + ListImportMappings() ([]*model.ImportMapping, error) + ListExportMappings() ([]*model.ExportMapping, error) + ListJsonStructures() ([]*mpr.JsonStructure, error) +} + // DescribeFunc generates MDL source for a given object type and qualified name. type DescribeFunc func(objectType string, qualifiedName string) (string, error) // Builder populates catalog tables from MPR data. type Builder struct { catalog *Catalog - reader *mpr.Reader + reader CatalogReader snapshot *Snapshot progress ProgressFunc hierarchy *hierarchy @@ -148,7 +201,7 @@ func (h *hierarchy) buildFolderPath(containerID model.ID) string { } // NewBuilder creates a new catalog builder. -func NewBuilder(catalog *Catalog, reader *mpr.Reader) *Builder { +func NewBuilder(catalog *Catalog, reader CatalogReader) *Builder { return &Builder{ catalog: catalog, reader: reader, diff --git a/mdl/executor/cmd_alter_workflow.go b/mdl/executor/cmd_alter_workflow.go index 872a6180..df403ca5 100644 --- a/mdl/executor/cmd_alter_workflow.go +++ b/mdl/executor/cmd_alter_workflow.go @@ -4,6 +4,7 @@ package executor import ( "fmt" + "io" "strings" "go.mongodb.org/mongo-driver/bson" @@ -21,7 +22,6 @@ const bsonArrayMarker = int32(3) // execAlterWorkflow handles ALTER WORKFLOW Module.Name { operations }. func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { - e := ctx.executor if !ctx.Connected() { return mdlerrors.NewNotConnected() } @@ -82,7 +82,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { return mdlerrors.NewBackend("SET ACTIVITY", err) } case *ast.InsertAfterOp: - if err := applyInsertAfterActivity(e, rawData, o); err != nil { + if err := applyInsertAfterActivity(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("INSERT AFTER", err) } case *ast.DropActivityOp: @@ -90,11 +90,11 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { return mdlerrors.NewBackend("DROP ACTIVITY", err) } case *ast.ReplaceActivityOp: - if err := applyReplaceActivity(e, rawData, o); err != nil { + if err := applyReplaceActivity(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("REPLACE ACTIVITY", err) } case *ast.InsertOutcomeOp: - if err := applyInsertOutcome(e, rawData, o); err != nil { + if err := applyInsertOutcome(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("INSERT OUTCOME", err) } case *ast.DropOutcomeOp: @@ -102,7 +102,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { return mdlerrors.NewBackend("DROP OUTCOME", err) } case *ast.InsertPathOp: - if err := applyInsertPath(e, rawData, o); err != nil { + if err := applyInsertPath(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("INSERT PATH", err) } case *ast.DropPathOp: @@ -110,7 +110,7 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { return mdlerrors.NewBackend("DROP PATH", err) } case *ast.InsertBranchOp: - if err := applyInsertBranch(e, rawData, o); err != nil { + if err := applyInsertBranch(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("INSERT BRANCH", err) } case *ast.DropBranchOp: @@ -118,11 +118,11 @@ func execAlterWorkflow(ctx *ExecContext, s *ast.AlterWorkflowStmt) error { return mdlerrors.NewBackend("DROP BRANCH", err) } case *ast.InsertBoundaryEventOp: - if err := applyInsertBoundaryEvent(e, rawData, o); err != nil { + if err := applyInsertBoundaryEvent(ctx, rawData, o); err != nil { return mdlerrors.NewBackend("INSERT BOUNDARY EVENT", err) } case *ast.DropBoundaryEventOp: - if err := applyDropBoundaryEvent(rawData, o); err != nil { + if err := applyDropBoundaryEvent(ctx.Output, rawData, o); err != nil { return mdlerrors.NewBackend("DROP BOUNDARY EVENT", err) } default: @@ -484,9 +484,9 @@ func deduplicateNewActivityName(act workflows.WorkflowActivity, existingNames ma // buildSubFlowBson builds a Workflows$Flow BSON document from AST activity nodes, // with auto-binding and name deduplication against existing workflow activities. -func buildSubFlowBson(e *Executor, doc bson.D, activities []ast.WorkflowActivityNode) bson.D { +func buildSubFlowBson(ctx *ExecContext, doc bson.D, activities []ast.WorkflowActivityNode) bson.D { subActs := buildWorkflowActivities(activities) - autoBindActivitiesInFlow(e, subActs) + autoBindActivitiesInFlow(ctx, subActs) existingNames := collectAllActivityNames(doc) for _, act := range subActs { deduplicateNewActivityName(act, existingNames) @@ -507,7 +507,7 @@ func buildSubFlowBson(e *Executor, doc bson.D, activities []ast.WorkflowActivity } // applyInsertAfterActivity inserts a new activity after a named activity. -func applyInsertAfterActivity(e *Executor, doc bson.D, op *ast.InsertAfterOp) error { +func applyInsertAfterActivity(ctx *ExecContext, doc bson.D, op *ast.InsertAfterOp) error { idx, activities, containingFlow, err := findActivityIndex(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -519,7 +519,7 @@ func applyInsertAfterActivity(e *Executor, doc bson.D, op *ast.InsertAfterOp) er } // Auto-bind parameters and deduplicate against existing workflow names - autoBindActivitiesInFlow(e, newActs) + autoBindActivitiesInFlow(ctx, newActs) existingNames := collectAllActivityNames(doc) for _, act := range newActs { deduplicateNewActivityName(act, existingNames) @@ -559,7 +559,7 @@ func applyDropActivity(doc bson.D, op *ast.DropActivityOp) error { } // applyReplaceActivity replaces an activity in place. -func applyReplaceActivity(e *Executor, doc bson.D, op *ast.ReplaceActivityOp) error { +func applyReplaceActivity(ctx *ExecContext, doc bson.D, op *ast.ReplaceActivityOp) error { idx, activities, containingFlow, err := findActivityIndex(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -570,7 +570,7 @@ func applyReplaceActivity(e *Executor, doc bson.D, op *ast.ReplaceActivityOp) er return mdlerrors.NewValidation("failed to build replacement activity") } - autoBindActivitiesInFlow(e, newActs) + autoBindActivitiesInFlow(ctx, newActs) existingNames := collectAllActivityNames(doc) for _, act := range newActs { deduplicateNewActivityName(act, existingNames) @@ -594,7 +594,7 @@ func applyReplaceActivity(e *Executor, doc bson.D, op *ast.ReplaceActivityOp) er } // applyInsertOutcome adds a new outcome to a user task. -func applyInsertOutcome(e *Executor, doc bson.D, op *ast.InsertOutcomeOp) error { +func applyInsertOutcome(ctx *ExecContext, doc bson.D, op *ast.InsertOutcomeOp) error { actDoc, err := findActivityByCaption(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -608,7 +608,7 @@ func applyInsertOutcome(e *Executor, doc bson.D, op *ast.InsertOutcomeOp) error // Build sub-flow if activities provided if len(op.Activities) > 0 { - outcomeDoc = append(outcomeDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(e, doc, op.Activities)}) + outcomeDoc = append(outcomeDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(ctx, doc, op.Activities)}) } outcomeDoc = append(outcomeDoc, @@ -660,7 +660,7 @@ func applyDropOutcome(doc bson.D, op *ast.DropOutcomeOp) error { } // applyInsertPath adds a new path to a parallel split. -func applyInsertPath(e *Executor, doc bson.D, op *ast.InsertPathOp) error { +func applyInsertPath(ctx *ExecContext, doc bson.D, op *ast.InsertPathOp) error { actDoc, err := findActivityByCaption(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -672,7 +672,7 @@ func applyInsertPath(e *Executor, doc bson.D, op *ast.InsertPathOp) error { } if len(op.Activities) > 0 { - pathDoc = append(pathDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(e, doc, op.Activities)}) + pathDoc = append(pathDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(ctx, doc, op.Activities)}) } pathDoc = append(pathDoc, bson.E{Key: "PersistentId", Value: mpr.IDToBsonBinary(mpr.GenerateID())}) @@ -719,7 +719,7 @@ func applyDropPath(doc bson.D, op *ast.DropPathOp) error { } // applyInsertBranch adds a new branch to a decision. -func applyInsertBranch(e *Executor, doc bson.D, op *ast.InsertBranchOp) error { +func applyInsertBranch(ctx *ExecContext, doc bson.D, op *ast.InsertBranchOp) error { actDoc, err := findActivityByCaption(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -754,7 +754,7 @@ func applyInsertBranch(e *Executor, doc bson.D, op *ast.InsertBranchOp) error { } if len(op.Activities) > 0 { - outcomeDoc = append(outcomeDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(e, doc, op.Activities)}) + outcomeDoc = append(outcomeDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(ctx, doc, op.Activities)}) } outcomes := dGetArrayElements(dGet(actDoc, "Outcomes")) @@ -820,7 +820,7 @@ func applyDropBranch(doc bson.D, op *ast.DropBranchOp) error { } // applyInsertBoundaryEvent adds a boundary event to an activity. -func applyInsertBoundaryEvent(e *Executor, doc bson.D, op *ast.InsertBoundaryEventOp) error { +func applyInsertBoundaryEvent(ctx *ExecContext, doc bson.D, op *ast.InsertBoundaryEventOp) error { actDoc, err := findActivityByCaption(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -845,7 +845,7 @@ func applyInsertBoundaryEvent(e *Executor, doc bson.D, op *ast.InsertBoundaryEve } if len(op.Activities) > 0 { - eventDoc = append(eventDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(e, doc, op.Activities)}) + eventDoc = append(eventDoc, bson.E{Key: "Flow", Value: buildSubFlowBson(ctx, doc, op.Activities)}) } eventDoc = append(eventDoc, bson.E{Key: "PersistentId", Value: mpr.IDToBsonBinary(mpr.GenerateID())}) @@ -865,7 +865,7 @@ func applyInsertBoundaryEvent(e *Executor, doc bson.D, op *ast.InsertBoundaryEve // // Limitation: this always removes events[0]. There is currently no syntax to // target a specific boundary event by name or type when multiple exist. -func applyDropBoundaryEvent(doc bson.D, op *ast.DropBoundaryEventOp) error { +func applyDropBoundaryEvent(w io.Writer, doc bson.D, op *ast.DropBoundaryEventOp) error { actDoc, err := findActivityByCaption(doc, op.ActivityRef, op.AtPosition) if err != nil { return err @@ -877,7 +877,7 @@ func applyDropBoundaryEvent(doc bson.D, op *ast.DropBoundaryEventOp) error { } if len(events) > 1 { - fmt.Printf("warning: activity %q has %d boundary events; dropping the first one\n", op.ActivityRef, len(events)) + fmt.Fprintf(w, "warning: activity %q has %d boundary events; dropping the first one\n", op.ActivityRef, len(events)) } // Drop the first boundary event diff --git a/mdl/executor/cmd_alter_workflow_test.go b/mdl/executor/cmd_alter_workflow_test.go index a65722e7..fe9de805 100644 --- a/mdl/executor/cmd_alter_workflow_test.go +++ b/mdl/executor/cmd_alter_workflow_test.go @@ -3,6 +3,7 @@ package executor import ( + "io" "strings" "testing" @@ -319,7 +320,7 @@ func TestDropBoundaryEvent_Single(t *testing.T) { doc := makeWorkflowDoc(act) op := &ast.DropBoundaryEventOp{ActivityRef: "Review"} - if err := applyDropBoundaryEvent(doc, op); err != nil { + if err := applyDropBoundaryEvent(io.Discard, doc, op); err != nil { t.Fatalf("DROP BOUNDARY EVENT failed: %v", err) } @@ -337,7 +338,7 @@ func TestDropBoundaryEvent_Multiple_DropsFirst(t *testing.T) { doc := makeWorkflowDoc(act) op := &ast.DropBoundaryEventOp{ActivityRef: "Review"} - if err := applyDropBoundaryEvent(doc, op); err != nil { + if err := applyDropBoundaryEvent(io.Discard, doc, op); err != nil { t.Fatalf("DROP BOUNDARY EVENT failed: %v", err) } @@ -358,7 +359,7 @@ func TestDropBoundaryEvent_NoEvents(t *testing.T) { doc := makeWorkflowDoc(act) op := &ast.DropBoundaryEventOp{ActivityRef: "Review"} - err := applyDropBoundaryEvent(doc, op) + err := applyDropBoundaryEvent(io.Discard, doc, op) if err == nil { t.Fatal("Expected error when dropping from activity with no boundary events") } diff --git a/mdl/executor/cmd_catalog.go b/mdl/executor/cmd_catalog.go index f6e71f09..86722751 100644 --- a/mdl/executor/cmd_catalog.go +++ b/mdl/executor/cmd_catalog.go @@ -211,6 +211,11 @@ func ensureCatalog(ctx *ExecContext, full bool) error { } } + // Guard against building without a connection. + if !ctx.Connected() { + return mdlerrors.NewNotConnected() + } + // Build fresh catalog return buildCatalog(ctx, full) } @@ -347,7 +352,7 @@ func buildCatalog(ctx *ExecContext, full bool, source ...bool) error { cat.SetProject("default", "Current Project", version) // Build catalog - builder := catalog.NewBuilder(cat, e.reader) + builder := catalog.NewBuilder(cat, ctx.Backend) builder.SetFullMode(full) if isSource { builder.SetSourceMode(true) @@ -684,18 +689,27 @@ func captureDescribeParallel(ctx *ExecContext, objectType string, qualifiedName } qn := ast.QualifiedName{Module: parts[0], Name: parts[1]} - // Create a goroutine-local executor: shared reader + cache, own output buffer. - // TODO: Replace with ExecContext.Fork() when MR 3 makes ExecContext self-contained. + // Create a goroutine-local context: shared backend + cache, own output buffer. var buf bytes.Buffer - local := &Executor{ - reader: ctx.executor.reader, - output: &buf, - cache: ctx.Cache, - } localCtx := &ExecContext{ - Output: &buf, - Cache: ctx.Cache, - executor: local, + Context: ctx.Context, + Output: &buf, + Format: ctx.Format, + Quiet: ctx.Quiet, + Logger: ctx.Logger, + Backend: ctx.Backend, + Cache: ctx.Cache, + MprPath: ctx.MprPath, + } + // If a backing Executor exists, create a local one for handlers that still + // need e.reader/e.output (e.g., describeMicroflow via writeDescribeJSON). + if ctx.executor != nil { + local := &Executor{ + reader: ctx.executor.reader, + output: &buf, + cache: ctx.Cache, + } + localCtx.executor = local } var err error diff --git a/mdl/executor/cmd_javaactions.go b/mdl/executor/cmd_javaactions.go index 6928b911..eb19cbd8 100644 --- a/mdl/executor/cmd_javaactions.go +++ b/mdl/executor/cmd_javaactions.go @@ -422,7 +422,7 @@ func execCreateJavaAction(ctx *ExecContext, s *ast.CreateJavaActionStmt) error { } // Clear cache - ctx.Cache = nil + ctx.InvalidateCache() fmt.Fprintf(ctx.Output, "Created java action: %s.%s\n", s.Name.Module, s.Name.Name) return nil diff --git a/mdl/executor/cmd_misc.go b/mdl/executor/cmd_misc.go index c8ed29c0..7588a970 100644 --- a/mdl/executor/cmd_misc.go +++ b/mdl/executor/cmd_misc.go @@ -38,6 +38,13 @@ func execRefresh(ctx *ExecContext) error { // execSet handles SET statements. func execSet(ctx *ExecContext, s *ast.SetStmt) error { + if ctx.Settings == nil { + ctx.Settings = make(map[string]any) + // Persist back to Executor so subsequent statements see the map. + if ctx.executor != nil { + ctx.executor.settings = ctx.Settings + } + } ctx.Settings[s.Key] = s.Value fmt.Fprintf(ctx.Output, "Set %s = %v\n", s.Key, s.Value) return nil diff --git a/mdl/executor/cmd_rename.go b/mdl/executor/cmd_rename.go index f3a64344..05a96443 100644 --- a/mdl/executor/cmd_rename.go +++ b/mdl/executor/cmd_rename.go @@ -275,7 +275,9 @@ func execRenameEnumeration(ctx *ExecContext, s *ast.RenameStmt) error { } // Also update enumeration refs in domain models (attribute types store qualified enum names) - ctx.Backend.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName) + if err := ctx.Backend.UpdateEnumerationRefsInAllDomainModels(oldQualifiedName, newQualifiedName); err != nil { + fmt.Fprintf(ctx.Output, "Warning: failed to update enumeration references in domain models: %v\n", err) + } invalidateHierarchy(ctx) invalidateDomainModelsCache(ctx) diff --git a/mdl/executor/cmd_styling.go b/mdl/executor/cmd_styling.go index 6be8b460..3b312a8b 100644 --- a/mdl/executor/cmd_styling.go +++ b/mdl/executor/cmd_styling.go @@ -24,6 +24,9 @@ func execShowDesignProperties(ctx *ExecContext, s *ast.ShowDesignPropertiesStmt) if !ctx.Connected() { return mdlerrors.NewNotConnected() } + if ctx.MprPath == "" { + return mdlerrors.NewValidationf("project path unavailable — connected via mock backend without MprPath") + } projectDir := filepath.Dir(ctx.MprPath) registry, err := loadThemeRegistry(projectDir) diff --git a/mdl/executor/cmd_workflows_write.go b/mdl/executor/cmd_workflows_write.go index d63e2789..db298405 100644 --- a/mdl/executor/cmd_workflows_write.go +++ b/mdl/executor/cmd_workflows_write.go @@ -17,7 +17,6 @@ import ( // execCreateWorkflow handles CREATE WORKFLOW statements. func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { - e := ctx.executor if !ctx.ConnectedForWrite() { return mdlerrors.NewNotConnectedWrite() } @@ -99,7 +98,7 @@ func execCreateWorkflow(ctx *ExecContext, s *ast.CreateWorkflowStmt) error { userActivities := buildWorkflowActivities(s.Activities) // Auto-bind microflow/workflow parameters and sanitize names - autoBindWorkflowParameters(e, userActivities) + autoBindWorkflowParameters(ctx, userActivities) // Deduplicate activity names to avoid CE0495 deduplicateActivityNames(userActivities) @@ -603,36 +602,36 @@ func sanitizeActivityName(name string) string { // autoBindWorkflowParameters resolves microflow/workflow parameters and generates // ParameterMappings, default outcomes, and sanitized names for workflow activities. -func autoBindWorkflowParameters(e *Executor, activities []workflows.WorkflowActivity) { - autoBindActivitiesInFlow(e, activities) +func autoBindWorkflowParameters(ctx *ExecContext, activities []workflows.WorkflowActivity) { + autoBindActivitiesInFlow(ctx, activities) } -func autoBindActivitiesInFlow(e *Executor, activities []workflows.WorkflowActivity) { +func autoBindActivitiesInFlow(ctx *ExecContext, activities []workflows.WorkflowActivity) { for _, act := range activities { switch a := act.(type) { case *workflows.CallMicroflowTask: - autoBindCallMicroflow(e, a) + autoBindCallMicroflow(ctx, a) // Recurse into outcomes for _, outcome := range a.Outcomes { switch o := outcome.(type) { case *workflows.BooleanConditionOutcome: if o.Flow != nil { - autoBindActivitiesInFlow(e, o.Flow.Activities) + autoBindActivitiesInFlow(ctx, o.Flow.Activities) } case *workflows.VoidConditionOutcome: if o.Flow != nil { - autoBindActivitiesInFlow(e, o.Flow.Activities) + autoBindActivitiesInFlow(ctx, o.Flow.Activities) } } } case *workflows.CallWorkflowActivity: - autoBindCallWorkflow(e, a) + autoBindCallWorkflow(ctx, a) case *workflows.UserTask: // Sanitize name a.Name = sanitizeActivityName(a.Name) for _, outcome := range a.Outcomes { if outcome.Flow != nil { - autoBindActivitiesInFlow(e, outcome.Flow.Activities) + autoBindActivitiesInFlow(ctx, outcome.Flow.Activities) } } case *workflows.ParallelSplitActivity: @@ -640,7 +639,7 @@ func autoBindActivitiesInFlow(e *Executor, activities []workflows.WorkflowActivi a.Name = sanitizeActivityName(a.Name) for _, outcome := range a.Outcomes { if outcome.Flow != nil { - autoBindActivitiesInFlow(e, outcome.Flow.Activities) + autoBindActivitiesInFlow(ctx, outcome.Flow.Activities) } } case *workflows.ExclusiveSplitActivity: @@ -649,11 +648,11 @@ func autoBindActivitiesInFlow(e *Executor, activities []workflows.WorkflowActivi switch o := outcome.(type) { case *workflows.BooleanConditionOutcome: if o.Flow != nil { - autoBindActivitiesInFlow(e, o.Flow.Activities) + autoBindActivitiesInFlow(ctx, o.Flow.Activities) } case *workflows.VoidConditionOutcome: if o.Flow != nil { - autoBindActivitiesInFlow(e, o.Flow.Activities) + autoBindActivitiesInFlow(ctx, o.Flow.Activities) } } } @@ -668,7 +667,7 @@ func autoBindActivitiesInFlow(e *Executor, activities []workflows.WorkflowActivi } // autoBindCallMicroflow resolves microflow parameters and auto-generates ParameterMappings. -func autoBindCallMicroflow(e *Executor, task *workflows.CallMicroflowTask) { +func autoBindCallMicroflow(ctx *ExecContext, task *workflows.CallMicroflowTask) { // Sanitize name task.Name = sanitizeActivityName(task.Name) @@ -678,12 +677,12 @@ func autoBindCallMicroflow(e *Executor, task *workflows.CallMicroflowTask) { } // Look up the microflow to get its parameters - mfs, err := e.reader.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() if err != nil { return } - h, err := e.getHierarchy() + h, err := getHierarchy(ctx) if err != nil { return } @@ -721,7 +720,7 @@ func autoBindCallMicroflow(e *Executor, task *workflows.CallMicroflowTask) { } // autoBindCallWorkflow resolves workflow parameters and generates ParameterMappings. -func autoBindCallWorkflow(e *Executor, act *workflows.CallWorkflowActivity) { +func autoBindCallWorkflow(ctx *ExecContext, act *workflows.CallWorkflowActivity) { // Sanitize name act.Name = sanitizeActivityName(act.Name) @@ -731,12 +730,12 @@ func autoBindCallWorkflow(e *Executor, act *workflows.CallWorkflowActivity) { } // Look up the target workflow to check its parameter - wfs, err := e.reader.ListWorkflows() + wfs, err := ctx.Backend.ListWorkflows() if err != nil { return } - h, err := e.getHierarchy() + h, err := getHierarchy(ctx) if err != nil { return } diff --git a/mdl/executor/exec_context.go b/mdl/executor/exec_context.go index db86a93a..14844e7d 100644 --- a/mdl/executor/exec_context.go +++ b/mdl/executor/exec_context.go @@ -65,8 +65,8 @@ type ExecContext struct { // executor is a temporary back-reference used during incremental migration. // Handlers that have not yet been migrated to use Backend can access the - // original Executor through this field. It will be removed once all handlers - // are fully migrated to ctx.Backend. + // original Executor through this field. Remove once all handlers are fully + // migrated to ctx.Backend (tracked: ~27 e := ctx.executor sites remain). executor *Executor } @@ -82,6 +82,16 @@ func (ctx *ExecContext) ConnectedForWrite() bool { return ctx.Connected() } +// InvalidateCache clears the hierarchy/entity/microflow cache on both the +// ExecContext and the underlying Executor so that subsequent statements see +// fresh data. +func (ctx *ExecContext) InvalidateCache() { + ctx.Cache = nil + if ctx.executor != nil { + ctx.executor.cache = nil + } +} + // GetThemeRegistry returns the cached theme registry, loading it lazily // from the project's theme sources on first access. func (ctx *ExecContext) GetThemeRegistry() *ThemeRegistry { @@ -93,8 +103,17 @@ func (ctx *ExecContext) GetThemeRegistry() *ThemeRegistry { } projectDir := filepath.Dir(ctx.MprPath) registry, err := loadThemeRegistry(projectDir) - if err == nil { - ctx.ThemeRegistry = registry + if err != nil { + if ctx.Logger != nil { + ctx.Logger.Warn("failed to load theme registry", "error", err) + } + return nil + } + ctx.ThemeRegistry = registry + // Persist back to Executor so subsequent statements pick up the cached value + // via newExecContext without reloading. + if ctx.executor != nil { + ctx.executor.themeRegistry = registry } return ctx.ThemeRegistry } diff --git a/mdl/executor/executor.go b/mdl/executor/executor.go index 07387da4..b858fd2c 100644 --- a/mdl/executor/executor.go +++ b/mdl/executor/executor.go @@ -74,13 +74,22 @@ func getEntityNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]string return ctx.Cache.entityNames } entityNames := make(map[model.ID]string) - dms, _ := ctx.Backend.ListDomainModels() + dms, err := ctx.Backend.ListDomainModels() + if err != nil { + if ctx.Logger != nil { + ctx.Logger.Warn("getEntityNames: ListDomainModels failed", "error", err) + } + return entityNames + } for _, dm := range dms { modName := h.GetModuleName(dm.ContainerID) for _, ent := range dm.Entities { entityNames[ent.ID] = modName + "." + ent.Name } } + if ctx.Cache != nil { + ctx.Cache.entityNames = entityNames + } return entityNames } @@ -90,10 +99,19 @@ func getMicroflowNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]str return ctx.Cache.microflowNames } microflowNames := make(map[model.ID]string) - mfs, _ := ctx.Backend.ListMicroflows() + mfs, err := ctx.Backend.ListMicroflows() + if err != nil { + if ctx.Logger != nil { + ctx.Logger.Warn("getMicroflowNames: ListMicroflows failed", "error", err) + } + return microflowNames + } for _, mf := range mfs { microflowNames[mf.ID] = h.GetQualifiedName(mf.ContainerID, mf.Name) } + if ctx.Cache != nil { + ctx.Cache.microflowNames = microflowNames + } return microflowNames } @@ -103,10 +121,19 @@ func getPageNames(ctx *ExecContext, h *ContainerHierarchy) map[model.ID]string { return ctx.Cache.pageNames } pageNames := make(map[model.ID]string) - pgs, _ := ctx.Backend.ListPages() + pgs, err := ctx.Backend.ListPages() + if err != nil { + if ctx.Logger != nil { + ctx.Logger.Warn("getPageNames: ListPages failed", "error", err) + } + return pageNames + } for _, pg := range pgs { pageNames[pg.ID] = h.GetQualifiedName(pg.ContainerID, pg.Name) } + if ctx.Cache != nil { + ctx.Cache.pageNames = pageNames + } return pageNames } diff --git a/mdl/executor/executor_connect.go b/mdl/executor/executor_connect.go index a219b570..74ad9894 100644 --- a/mdl/executor/executor_connect.go +++ b/mdl/executor/executor_connect.go @@ -30,6 +30,19 @@ func execConnect(ctx *ExecContext, s *ast.ConnectStmt) error { // Wrap the writer in an MprBackend for ctx.Backend propagation. e.backend = mprbackend.Wrap(writer, s.Path) + // Propagate connection state back to ctx so subsequent code in this + // dispatch cycle sees the updated values. + ctx.Backend = e.backend + ctx.Cache = e.cache + ctx.MprPath = e.mprPath + + // Reset project-scoped caches — previous project's catalog and theme + // registry are invalid for the new connection. + e.catalog = nil + e.themeRegistry = nil + ctx.Catalog = nil + ctx.ThemeRegistry = nil + // Display connection info with version pv := e.reader.ProjectVersion() if !ctx.Quiet { @@ -64,6 +77,17 @@ func reconnect(ctx *ExecContext) error { e.reader = writer.Reader() e.cache = &executorCache{} // Reset cache e.backend = mprbackend.Wrap(writer, e.mprPath) + + // Propagate reconnection state back to ctx. + ctx.Backend = e.backend + ctx.Cache = e.cache + + // Reset project-scoped caches — file may have changed externally. + e.catalog = nil + e.themeRegistry = nil + ctx.Catalog = nil + ctx.ThemeRegistry = nil + return nil } @@ -86,6 +110,13 @@ func execDisconnect(ctx *ExecContext) error { e.mprPath = "" e.cache = nil e.backend = nil + + // Propagate disconnection state back to ctx so subsequent code in this + // dispatch cycle sees the cleared values. + ctx.Backend = nil + ctx.MprPath = "" + ctx.Cache = nil + return nil } diff --git a/mdl/executor/format.go b/mdl/executor/format.go index c0a8ddc8..2a1c7d19 100644 --- a/mdl/executor/format.go +++ b/mdl/executor/format.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "io" "strings" ) @@ -120,17 +121,27 @@ func writeDescribeJSON(ctx *ExecContext, name, objectType string, fn func() erro } // Capture the text output from fn. + // TODO: Once all handlers write to ctx.Output exclusively, the e.output/e.guard + // swap can be removed. Currently needed because some closures still write to e.output. var buf bytes.Buffer origOutput := ctx.Output - origEOutput := e.output - origGuard := e.guard ctx.Output = &buf - e.output = &buf // sync executor output for closures that write to e.output - e.guard = nil // disable line guard for capture + + // Swap executor output/guard only when a backing Executor exists. + var origEOutput io.Writer + var origGuard *outputGuard + if e != nil { + origEOutput = e.output + origGuard = e.guard + e.output = &buf // sync executor output for closures that write to e.output + e.guard = nil // disable line guard for capture + } err := fn() ctx.Output = origOutput - e.output = origEOutput - e.guard = origGuard + if e != nil { + e.output = origEOutput + e.guard = origGuard + } if err != nil { return err } diff --git a/mdl/executor/hierarchy.go b/mdl/executor/hierarchy.go index 628febfb..700a4c66 100644 --- a/mdl/executor/hierarchy.go +++ b/mdl/executor/hierarchy.go @@ -6,6 +6,7 @@ import ( "context" "strings" + "github.com/mendixlabs/mxcli/mdl/backend" "github.com/mendixlabs/mxcli/model" "github.com/mendixlabs/mxcli/sdk/mpr" ) @@ -54,6 +55,38 @@ func NewContainerHierarchy(reader *mpr.Reader) (*ContainerHierarchy, error) { return h, nil } +// NewContainerHierarchyFromBackend creates a new hierarchy from a Backend interface. +func NewContainerHierarchyFromBackend(b backend.FullBackend) (*ContainerHierarchy, error) { + h := &ContainerHierarchy{ + moduleIDs: make(map[model.ID]bool), + moduleNames: make(map[model.ID]string), + containerParent: make(map[model.ID]model.ID), + folderNames: make(map[model.ID]string), + } + + modules, err := b.ListModules() + if err != nil { + return nil, err + } + for _, m := range modules { + h.moduleIDs[m.ID] = true + h.moduleNames[m.ID] = m.Name + } + + units, _ := b.ListUnits() + for _, u := range units { + h.containerParent[u.ID] = u.ContainerID + } + + folders, _ := b.ListFolders() + for _, f := range folders { + h.folderNames[f.ID] = f.Name + h.containerParent[f.ID] = f.ContainerID + } + + return h, nil +} + // FindModuleID finds the module ID for any container by traversing the hierarchy. func (h *ContainerHierarchy) FindModuleID(containerID model.ID) model.ID { current := containerID @@ -112,19 +145,19 @@ func (h *ContainerHierarchy) GetQualifiedName(containerID model.ID, name string) // getHierarchy returns a cached ContainerHierarchy or creates a new one. func getHierarchy(ctx *ExecContext) (*ContainerHierarchy, error) { - e := ctx.executor - // Ensure cache exists if !ctx.Connected() { return nil, nil } if ctx.Cache == nil { ctx.Cache = &executorCache{} - e.cache = ctx.Cache + if ctx.executor != nil { + ctx.executor.cache = ctx.Cache + } } if ctx.Cache.hierarchy != nil { return ctx.Cache.hierarchy, nil } - h, err := NewContainerHierarchy(e.reader) + h, err := NewContainerHierarchyFromBackend(ctx.Backend) if err != nil { return nil, err }