Skip to content

Commit ebae4b2

Browse files
akoclaude
andcommitted
fix: CREATE OR MODIFY REST CLIENT reports 'Modified' for existing client (issue #421)
When CREATE OR MODIFY found and replaced an existing REST client, the success message always said "Created". Track whether an existing service was deleted and emit "Modified rest client" vs "Created rest client" accordingly. Applies to both the manual definition path and the OpenAPI import path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4c801fd commit ebae4b2

3 files changed

Lines changed: 81 additions & 5 deletions

File tree

mdl/executor/cmd_rest_clients.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,15 @@ func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error {
320320
}
321321

322322
var preservedID model.ID
323+
wasModified := false
323324
for _, existing := range existingServices {
324325
existModID := h.FindModuleID(existing.ContainerID)
325326
existModName := h.GetModuleName(existModID)
326327
if strings.EqualFold(existModName, moduleName) && strings.EqualFold(existing.Name, stmt.Name.Name) {
327328
if stmt.CreateOrModify {
328329
// Preserve the existing ID so SEND REST REQUEST references stay valid after replace.
329330
preservedID = existing.ID
331+
wasModified = true
330332
if err := ctx.Backend.DeleteConsumedRestService(existing.ID); err != nil {
331333
return mdlerrors.NewBackend("delete existing rest client", err)
332334
}
@@ -409,7 +411,11 @@ func createRestClient(ctx *ExecContext, stmt *ast.CreateRestClientStmt) error {
409411
return mdlerrors.NewBackend("create rest client", err)
410412
}
411413

412-
fmt.Fprintf(ctx.Output, "Created rest client: %s.%s (%d operations)\n", moduleName, stmt.Name.Name, len(svc.Operations))
414+
verb := "Created"
415+
if wasModified {
416+
verb = "Modified"
417+
}
418+
fmt.Fprintf(ctx.Output, "%s rest client: %s.%s (%d operations)\n", verb, moduleName, stmt.Name.Name, len(svc.Operations))
413419
return nil
414420
}
415421

@@ -675,13 +681,15 @@ func createRestClientFromSpec(ctx *ExecContext, stmt *ast.CreateRestClientStmt)
675681
if err != nil {
676682
return mdlerrors.NewBackend("build hierarchy", err)
677683
}
684+
openAPIWasModified := false
678685
for _, existing := range existingServices {
679686
existModID := h.FindModuleID(existing.ContainerID)
680687
existModName := h.GetModuleName(existModID)
681688
if strings.EqualFold(existModName, moduleName) && strings.EqualFold(existing.Name, stmt.Name.Name) {
682689
if stmt.CreateOrModify {
683690
// Reuse the existing ID so microflow references stay valid.
684691
svc.ID = existing.ID
692+
openAPIWasModified = true
685693
if err := ctx.Backend.DeleteConsumedRestService(existing.ID); err != nil {
686694
return mdlerrors.NewBackend("delete existing rest client", err)
687695
}
@@ -696,8 +704,12 @@ func createRestClientFromSpec(ctx *ExecContext, stmt *ast.CreateRestClientStmt)
696704
return mdlerrors.NewBackend("create rest client", err)
697705
}
698706

699-
fmt.Fprintf(ctx.Output, "Created rest client: %s.%s (%d operations from OpenAPI spec)\n",
700-
moduleName, stmt.Name.Name, len(svc.Operations))
707+
openAPIVerb := "Created"
708+
if openAPIWasModified {
709+
openAPIVerb = "Modified"
710+
}
711+
fmt.Fprintf(ctx.Output, "%s rest client: %s.%s (%d operations from OpenAPI spec)\n",
712+
openAPIVerb, moduleName, stmt.Name.Name, len(svc.Operations))
701713
return nil
702714
}
703715

mdl/executor/cmd_rest_clients_mock_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/mendixlabs/mxcli/mdl/ast"
99
"github.com/mendixlabs/mxcli/mdl/backend/mock"
10+
"github.com/mendixlabs/mxcli/mdl/types"
1011
"github.com/mendixlabs/mxcli/model"
1112
)
1213

@@ -95,3 +96,66 @@ func TestShowRestClients_FilterByModule(t *testing.T) {
9596
assertNoError(t, listRestClients(ctx, "Integrations"))
9697
assertContainsStr(t, buf.String(), "Integrations.PaymentAPI")
9798
}
99+
100+
func restClientProjectVersion() *types.ProjectVersion {
101+
return &types.ProjectVersion{ProductVersion: "10.6.0", MajorVersion: 10, MinorVersion: 6}
102+
}
103+
104+
func TestCreateRestClient_OrModify_SaysModified(t *testing.T) {
105+
mod := mkModule("MyModule")
106+
existingID := nextID("crs")
107+
existing := &model.ConsumedRestService{
108+
BaseElement: model.BaseElement{ID: existingID},
109+
ContainerID: mod.ID,
110+
Name: "WeatherAPI",
111+
}
112+
h := mkHierarchy(mod)
113+
withContainer(h, existing.ContainerID, mod.ID)
114+
115+
mb := &mock.MockBackend{
116+
IsConnectedFunc: func() bool { return true },
117+
ProjectVersionFunc: func() *types.ProjectVersion { return restClientProjectVersion() },
118+
ListModulesFunc: func() ([]*model.Module, error) {
119+
return []*model.Module{mod}, nil
120+
},
121+
ListConsumedRestServicesFunc: func() ([]*model.ConsumedRestService, error) {
122+
return []*model.ConsumedRestService{existing}, nil
123+
},
124+
DeleteConsumedRestServiceFunc: func(id model.ID) error { return nil },
125+
CreateConsumedRestServiceFunc: func(svc *model.ConsumedRestService) error { return nil },
126+
}
127+
128+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
129+
stmt := &ast.CreateRestClientStmt{
130+
Name: ast.QualifiedName{Module: "MyModule", Name: "WeatherAPI"},
131+
BaseUrl: "https://api.weather.com",
132+
CreateOrModify: true,
133+
}
134+
assertNoError(t, createRestClient(ctx, stmt))
135+
assertContainsStr(t, buf.String(), "Modified rest client")
136+
}
137+
138+
func TestCreateRestClient_New_SaysCreated(t *testing.T) {
139+
mod := mkModule("MyModule")
140+
h := mkHierarchy(mod)
141+
142+
mb := &mock.MockBackend{
143+
IsConnectedFunc: func() bool { return true },
144+
ProjectVersionFunc: func() *types.ProjectVersion { return restClientProjectVersion() },
145+
ListModulesFunc: func() ([]*model.Module, error) {
146+
return []*model.Module{mod}, nil
147+
},
148+
ListConsumedRestServicesFunc: func() ([]*model.ConsumedRestService, error) {
149+
return nil, nil
150+
},
151+
CreateConsumedRestServiceFunc: func(svc *model.ConsumedRestService) error { return nil },
152+
}
153+
154+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
155+
stmt := &ast.CreateRestClientStmt{
156+
Name: ast.QualifiedName{Module: "MyModule", Name: "WeatherAPI"},
157+
BaseUrl: "https://api.weather.com",
158+
}
159+
assertNoError(t, createRestClient(ctx, stmt))
160+
assertContainsStr(t, buf.String(), "Created rest client")
161+
}

mdl/executor/cmd_rest_openapi_mock_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,12 @@ func TestCreateRestClientFromSpec_OrModifyPreservesID(t *testing.T) {
182182
return nil
183183
}
184184

185-
ctx, _ := newMockCtx(t, withBackend(mb), withHierarchy(h))
185+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
186186
stmt := &ast.CreateRestClientStmt{
187187
Name: ast.QualifiedName{Module: "PetModule", Name: "PetStoreAPI"},
188188
OpenApiPath: srv.URL,
189189
CreateOrModify: true,
190190
}
191-
192191
assertNoError(t, createRestClient(ctx, stmt))
193192

194193
if deletedID != existingID {
@@ -200,6 +199,7 @@ func TestCreateRestClientFromSpec_OrModifyPreservesID(t *testing.T) {
200199
if created.ID != existingID {
201200
t.Errorf("expected recreated service to reuse existing ID %v, got %v", existingID, created.ID)
202201
}
202+
assertContainsStr(t, buf.String(), "Modified rest client")
203203
}
204204

205205
func TestCreateRestClientFromSpec_DuplicateWithoutOrModify(t *testing.T) {

0 commit comments

Comments
 (0)