From d9729b112b3046bf9ec24b6fb8bfb219239516fb Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Wed, 15 Apr 2026 14:12:20 +0530 Subject: [PATCH 01/16] Added force delete capability for Connect-Cluster & Connector --- .../client/NamespacedResourceClient.java | 2 +- .../com/michelin/kafkactl/command/Delete.java | 2 +- .../kafkactl/service/ResourceService.java | 5 +- .../michelin/kafkactl/command/DeleteTest.java | 7 +-- .../kafkactl/service/ResourceServiceTest.java | 54 +++++++++++-------- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java index 3d0216e..f2859ec 100644 --- a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java +++ b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java @@ -60,7 +60,7 @@ HttpResponse> delete( @QueryValue String name, @Nullable @QueryValue String version, @QueryValue boolean dryrun, - @QueryValue boolean force); + @Nullable @QueryValue Boolean force); /** * Apply a given resource. diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index a954813..79d7928 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -68,7 +68,7 @@ public class Delete extends DryRunHook { @Option( names = {"--force"}, - description = "Force delete resource from Ns4Kafka. Only for connector and connect cluster.") + description = "Force deletion for supported resources such as connect clusters and connectors.") public boolean force; /** diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index f76615c..1e7d777 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -258,6 +258,9 @@ public boolean delete( boolean force, CommandSpec commandSpec) { try { + Boolean forceDelete = force && List.of(CONNECTOR, CONNECT_CLUSTER).contains(apiResource.getKind()) + ? Boolean.TRUE + : null; HttpResponse> response = apiResource.isNamespaced() ? namespacedClient.delete( namespace, @@ -266,7 +269,7 @@ public boolean delete( name, version, dryRun, - force) + forceDelete) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 05fa45f..ec50140 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -320,7 +320,7 @@ void shouldDeleteByName() { } @Test - void shouldForceDeleteConnectorByName() { + void shouldDeleteConnectorByNameWithForce() { when(configService.isCurrentContextValid()).thenReturn(true); when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); @@ -346,7 +346,7 @@ void shouldForceDeleteConnectorByName() { } @Test - void shouldForceDeleteConnectClusterByName() { + void shouldDeleteConnectClusterByNameWithForce() { when(configService.isCurrentContextValid()).thenReturn(true); when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); @@ -367,7 +367,8 @@ void shouldForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); - verify(resourceService).delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), any()); + verify(resourceService) + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), any()); } @Test diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 5428d0b..aea9d24 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -800,7 +800,7 @@ void shouldDeleteNamespacedResource() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -811,8 +811,7 @@ void shouldDeleteNamespacedResource() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -834,7 +833,7 @@ void shouldDeleteMultipleNamespacedResources() { .metadata(Resource.Metadata.builder().name("name2").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.ok(List.of(deletedResource1, deletedResource2)) .header("X-Ns4kafka-Result", "created")); @@ -866,7 +865,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -893,10 +892,10 @@ void shouldForceDeleteConnector() { doCallRealMethod().when(formatService).prettifyKind(any()); Resource deletedResource = Resource.builder() - .metadata(Resource.Metadata.builder().name("connector").build()) + .metadata(Metadata.builder().name("connector").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -912,7 +911,14 @@ void shouldForceDeleteConnector() { assertTrue(actual); verify(namespacedClient) - .delete(eq("namespace"), eq("connectors"), any(), eq("connector"), isNull(), eq(false), eq(true)); + .delete( + eq("namespace"), + eq("connectors"), + any(), + eq("connector"), + isNull(), + eq(false), + eq(Boolean.TRUE)); } @Test @@ -924,10 +930,10 @@ void shouldForceDeleteConnectCluster() { doCallRealMethod().when(formatService).prettifyKind(any()); Resource deletedResource = Resource.builder() - .metadata(Resource.Metadata.builder().name("cluster").build()) + .metadata(Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -943,7 +949,14 @@ void shouldForceDeleteConnectCluster() { assertTrue(actual); verify(namespacedClient) - .delete(eq("namespace"), eq("connect-clusters"), any(), eq("cluster"), isNull(), eq(false), eq(true)); + .delete( + eq("namespace"), + eq("connect-clusters"), + any(), + eq("cluster"), + isNull(), + eq(false), + eq(Boolean.TRUE)); } @Test @@ -969,8 +982,7 @@ void shouldDeleteNonNamespacedResource() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1025,11 +1037,11 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { CommandLine cmd = new CommandLine(new Kafkactl()); HttpClientResponseException exception = new HttpClientResponseException("error", HttpResponse.serverError()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenThrow(exception); - boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = + resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1047,11 +1059,11 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { CommandLine cmd = new CommandLine(new Kafkactl()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.notFound()); - boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = + resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1077,8 +1089,8 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); - boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = + resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From a677b87efc0c53843b3004764bb46481c68b9815 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Wed, 15 Apr 2026 14:18:09 +0530 Subject: [PATCH 02/16] SpotlessApply --- .../kafkactl/service/ResourceService.java | 5 ++--- .../michelin/kafkactl/command/DeleteTest.java | 3 +-- .../kafkactl/service/ResourceServiceTest.java | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index 1e7d777..572b986 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -258,9 +258,8 @@ public boolean delete( boolean force, CommandSpec commandSpec) { try { - Boolean forceDelete = force && List.of(CONNECTOR, CONNECT_CLUSTER).contains(apiResource.getKind()) - ? Boolean.TRUE - : null; + Boolean forceDelete = + force && List.of(CONNECTOR, CONNECT_CLUSTER).contains(apiResource.getKind()) ? Boolean.TRUE : null; HttpResponse> response = apiResource.isNamespaced() ? namespacedClient.delete( namespace, diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index ec50140..ceaa01c 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -367,8 +367,7 @@ void shouldDeleteConnectClusterByNameWithForce() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); - verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), any()); + verify(resourceService).delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), any()); } @Test diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index aea9d24..a3fd7be 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -811,7 +811,8 @@ void shouldDeleteNamespacedResource() { .synchronizable(true) .build(); - boolean actual = resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + boolean actual = + resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -982,7 +983,8 @@ void shouldDeleteNonNamespacedResource() { .synchronizable(true) .build(); - boolean actual = resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + boolean actual = + resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1040,8 +1042,8 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenThrow(exception); - boolean actual = - resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1062,8 +1064,8 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) .thenReturn(HttpResponse.notFound()); - boolean actual = - resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1089,8 +1091,8 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); - boolean actual = - resourceService.delete(apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From e56fa1ac757060d165c5448b7b7231238aafa6fe Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Wed, 15 Apr 2026 14:42:46 +0530 Subject: [PATCH 03/16] Retrigger From ce2a4ab256762f69ce8c8e32295d048ac139369a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Greffier?= Date: Wed, 15 Apr 2026 21:31:38 +0200 Subject: [PATCH 04/16] Fix metadata import --- .../com/michelin/kafkactl/service/ResourceServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index a3fd7be..4976cd6 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -893,7 +893,7 @@ void shouldForceDeleteConnector() { doCallRealMethod().when(formatService).prettifyKind(any()); Resource deletedResource = Resource.builder() - .metadata(Metadata.builder().name("connector").build()) + .metadata(Resource.Metadata.builder().name("connector").build()) .build(); when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) @@ -931,7 +931,7 @@ void shouldForceDeleteConnectCluster() { doCallRealMethod().when(formatService).prettifyKind(any()); Resource deletedResource = Resource.builder() - .metadata(Metadata.builder().name("cluster").build()) + .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) From d62d0d362c98de925e134e2e5024c87cf954c75a Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Thu, 16 Apr 2026 23:48:35 +0530 Subject: [PATCH 05/16] Address PR Comments test name modified & boolean force --- .../client/NamespacedResourceClient.java | 2 +- .../com/michelin/kafkactl/command/Delete.java | 2 +- .../kafkactl/service/ResourceService.java | 4 +-- .../michelin/kafkactl/command/DeleteTest.java | 4 +-- .../kafkactl/service/ResourceServiceTest.java | 32 ++++++------------- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java index f2859ec..3d0216e 100644 --- a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java +++ b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java @@ -60,7 +60,7 @@ HttpResponse> delete( @QueryValue String name, @Nullable @QueryValue String version, @QueryValue boolean dryrun, - @Nullable @QueryValue Boolean force); + @QueryValue boolean force); /** * Apply a given resource. diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index 79d7928..a954813 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -68,7 +68,7 @@ public class Delete extends DryRunHook { @Option( names = {"--force"}, - description = "Force deletion for supported resources such as connect clusters and connectors.") + description = "Force delete resource from Ns4Kafka. Only for connector and connect cluster.") public boolean force; /** diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index 572b986..f76615c 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -258,8 +258,6 @@ public boolean delete( boolean force, CommandSpec commandSpec) { try { - Boolean forceDelete = - force && List.of(CONNECTOR, CONNECT_CLUSTER).contains(apiResource.getKind()) ? Boolean.TRUE : null; HttpResponse> response = apiResource.isNamespaced() ? namespacedClient.delete( namespace, @@ -268,7 +266,7 @@ public boolean delete( name, version, dryRun, - forceDelete) + force) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index ceaa01c..05fa45f 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -320,7 +320,7 @@ void shouldDeleteByName() { } @Test - void shouldDeleteConnectorByNameWithForce() { + void shouldForceDeleteConnectorByName() { when(configService.isCurrentContextValid()).thenReturn(true); when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); @@ -346,7 +346,7 @@ void shouldDeleteConnectorByNameWithForce() { } @Test - void shouldDeleteConnectClusterByNameWithForce() { + void shouldForceDeleteConnectClusterByName() { when(configService.isCurrentContextValid()).thenReturn(true); when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 4976cd6..5428d0b 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -800,7 +800,7 @@ void shouldDeleteNamespacedResource() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -834,7 +834,7 @@ void shouldDeleteMultipleNamespacedResources() { .metadata(Resource.Metadata.builder().name("name2").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource1, deletedResource2)) .header("X-Ns4kafka-Result", "created")); @@ -866,7 +866,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -896,7 +896,7 @@ void shouldForceDeleteConnector() { .metadata(Resource.Metadata.builder().name("connector").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -912,14 +912,7 @@ void shouldForceDeleteConnector() { assertTrue(actual); verify(namespacedClient) - .delete( - eq("namespace"), - eq("connectors"), - any(), - eq("connector"), - isNull(), - eq(false), - eq(Boolean.TRUE)); + .delete(eq("namespace"), eq("connectors"), any(), eq("connector"), isNull(), eq(false), eq(true)); } @Test @@ -934,7 +927,7 @@ void shouldForceDeleteConnectCluster() { .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -950,14 +943,7 @@ void shouldForceDeleteConnectCluster() { assertTrue(actual); verify(namespacedClient) - .delete( - eq("namespace"), - eq("connect-clusters"), - any(), - eq("cluster"), - isNull(), - eq(false), - eq(Boolean.TRUE)); + .delete(eq("namespace"), eq("connect-clusters"), any(), eq("cluster"), isNull(), eq(false), eq(true)); } @Test @@ -1039,7 +1025,7 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { CommandLine cmd = new CommandLine(new Kafkactl()); HttpClientResponseException exception = new HttpClientResponseException("error", HttpResponse.serverError()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenThrow(exception); boolean actual = resourceService.delete( @@ -1061,7 +1047,7 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { CommandLine cmd = new CommandLine(new Kafkactl()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), any())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( From a926d22b3d11442d36e6d586205478500a602df9 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Sat, 18 Apr 2026 18:57:11 +0530 Subject: [PATCH 06/16] Adding capability to cascade force delete connect cluster and connector --- .../client/NamespacedResourceClient.java | 5 +- .../com/michelin/kafkactl/command/Delete.java | 6 + .../kafkactl/service/ResourceService.java | 4 +- .../michelin/kafkactl/command/DeleteTest.java | 83 +++++++-- .../kafkactl/service/ResourceServiceTest.java | 157 +++++++++++++++--- 5 files changed, 217 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java index 3d0216e..1af6aac 100644 --- a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java +++ b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java @@ -47,7 +47,7 @@ public interface NamespacedResourceClient { * @param dryrun Is dry-run mode or not? * @return The delete response */ - @Delete("{namespace}/{kind}{?name,version,dryrun,force}") + @Delete("{namespace}/{kind}{?name,version,dryrun,force,cascade}") @Retryable( delay = "${kafkactl.retry.delay}", attempts = "${kafkactl.retry.attempt}", @@ -60,7 +60,8 @@ HttpResponse> delete( @QueryValue String name, @Nullable @QueryValue String version, @QueryValue boolean dryrun, - @QueryValue boolean force); + @QueryValue boolean force, + @QueryValue boolean cascade); /** * Apply a given resource. diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index a954813..b41b6c3 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -71,6 +71,11 @@ public class Delete extends DryRunHook { description = "Force delete resource from Ns4Kafka. Only for connector and connect cluster.") public boolean force; + @Option( + names = {"--cascade"}, + description = "Cascade delete related connectors from Ns4Kafka. Only for connect cluster.") + public boolean cascade; + /** * Run the "delete" command. * @@ -115,6 +120,7 @@ public Integer onAuthSuccess() { : null), dryRun, force, + cascade, commandSpec); }) .mapToInt(value -> value ? 0 : 1) diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index f76615c..bdcaa90 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -256,6 +256,7 @@ public boolean delete( @Nullable String version, boolean dryRun, boolean force, + boolean cascade, CommandSpec commandSpec) { try { HttpResponse> response = apiResource.isNamespaced() @@ -266,7 +267,8 @@ public boolean delete( name, version, dryRun, - force) + force, + cascade) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 05fa45f..dbc8f42 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -243,7 +243,7 @@ void shouldDeleteByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -282,7 +282,7 @@ void shouldDeleteOneVersionByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -308,7 +308,7 @@ void shouldDeleteByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -334,7 +334,7 @@ void shouldForceDeleteConnectorByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -342,7 +342,7 @@ void shouldForceDeleteConnectorByName() { int code = cmd.execute("connector", "prefix.connector", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("prefix.connector"), eq(null), eq(false), eq(true), any()); + .delete(any(), eq("namespace"), eq("prefix.connector"), eq(null), eq(false), eq(true), eq(false), any()); } @Test @@ -360,14 +360,67 @@ void shouldForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); - verify(resourceService).delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), any()); + verify(resourceService) + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(false), any()); + } + + @Test + void shouldCascadeDeleteConnectClusterByName() { + when(configService.isCurrentContextValid()).thenReturn(true); + when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); + + ApiResource apiResource = ApiResource.builder() + .kind("ConnectCluster") + .path("connect-clusters") + .names(List.of("connect-clusters", "connect-cluster", "cc")) + .namespaced(true) + .synchronizable(true) + .build(); + + when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); + when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + .thenReturn(true); + + CommandLine cmd = new CommandLine(delete); + + int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade"); + assertEquals(0, code); + verify(resourceService) + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(false), eq(true), any()); + } + + @Test + void shouldCascadeForceDeleteConnectClusterByName() { + when(configService.isCurrentContextValid()).thenReturn(true); + when(loginService.doAuthenticate(any(), anyBoolean())).thenReturn(true); + + ApiResource apiResource = ApiResource.builder() + .kind("ConnectCluster") + .path("connect-clusters") + .names(List.of("connect-clusters", "connect-cluster", "cc")) + .namespaced(true) + .synchronizable(true) + .build(); + + when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); + when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + .thenReturn(true); + + CommandLine cmd = new CommandLine(delete); + + int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade", "--force"); + assertEquals(0, code); + verify(resourceService) + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(true), any()); } @Test @@ -385,7 +438,7 @@ void shouldDeleteOneVersionByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -421,7 +474,7 @@ void shouldNotDeleteByFileWhenInDryRunMode() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -461,7 +514,7 @@ void shouldNotDeleteByFileWhenFail() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(false); CommandLine cmd = new CommandLine(delete); @@ -515,7 +568,7 @@ void shouldDeleteByNameWithWildcard() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -541,7 +594,7 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -559,7 +612,7 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -577,7 +630,7 @@ void shouldNotDeleteByNameWithWildcardInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -604,7 +657,7 @@ void shouldNotDeleteByNameWithWildcardWithExecuteInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 5428d0b..57e1d67 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -800,7 +800,7 @@ void shouldDeleteNamespacedResource() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -812,7 +812,7 @@ void shouldDeleteNamespacedResource() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -834,7 +834,7 @@ void shouldDeleteMultipleNamespacedResources() { .metadata(Resource.Metadata.builder().name("name2").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource1, deletedResource2)) .header("X-Ns4kafka-Result", "created")); @@ -847,7 +847,7 @@ void shouldDeleteMultipleNamespacedResources() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "name*", null, false, false, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -866,7 +866,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -877,8 +877,8 @@ void shouldDeleteNamespacedResourceWithVersion() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name", "latest", false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "name", "latest", false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" version latest deleted.")); @@ -896,7 +896,7 @@ void shouldForceDeleteConnector() { .metadata(Resource.Metadata.builder().name("connector").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -908,11 +908,11 @@ void shouldForceDeleteConnector() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "connector", null, false, true, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) - .delete(eq("namespace"), eq("connectors"), any(), eq("connector"), isNull(), eq(false), eq(true)); + .delete(eq("namespace"), eq("connectors"), any(), eq("connector"), isNull(), eq(false), eq(true), eq(false)); } @Test @@ -927,7 +927,7 @@ void shouldForceDeleteConnectCluster() { .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() @@ -939,11 +939,128 @@ void shouldForceDeleteConnectCluster() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "cluster", null, false, true, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); + + assertTrue(actual); + verify(namespacedClient) + .delete( + eq("namespace"), + eq("connect-clusters"), + any(), + eq("cluster"), + isNull(), + eq(false), + eq(true), + eq(false)); + } + + @Test + void shouldCascadeDeleteConnectCluster() { + CommandLine cmd = new CommandLine(new Kafkactl()); + StringWriter sw = new StringWriter(); + cmd.setOut(new PrintWriter(sw)); + + doCallRealMethod().when(formatService).prettifyKind(any()); + + Resource deletedResource = Resource.builder() + .metadata(Resource.Metadata.builder().name("cluster").build()) + .build(); + + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + .thenReturn(HttpResponse.ok(List.of(deletedResource))); + + ApiResource apiResource = ApiResource.builder() + .kind(CONNECT_CLUSTER) + .path("connect-clusters") + .names(List.of("connect-clusters", "connect-cluster", "cc")) + .namespaced(true) + .synchronizable(true) + .build(); + + boolean actual = + resourceService.delete(apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); + + assertTrue(actual); + verify(namespacedClient) + .delete( + eq("namespace"), + eq("connect-clusters"), + any(), + eq("cluster"), + isNull(), + eq(false), + eq(false), + eq(true)); + } + + @Test + void shouldForceCascadeDeleteConnectCluster() { + CommandLine cmd = new CommandLine(new Kafkactl()); + StringWriter sw = new StringWriter(); + cmd.setOut(new PrintWriter(sw)); + + doCallRealMethod().when(formatService).prettifyKind(any()); + + Resource deletedResource = Resource.builder() + .metadata(Resource.Metadata.builder().name("cluster").build()) + .build(); + + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + .thenReturn(HttpResponse.ok(List.of(deletedResource))); + + ApiResource apiResource = ApiResource.builder() + .kind(CONNECT_CLUSTER) + .path("connect-clusters") + .names(List.of("connect-clusters", "connect-cluster", "cc")) + .namespaced(true) + .synchronizable(true) + .build(); + + boolean actual = + resourceService.delete(apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); + + assertTrue(actual); + verify(namespacedClient) + .delete( + eq("namespace"), + eq("connect-clusters"), + any(), + eq("cluster"), + isNull(), + eq(false), + eq(true), + eq(true)); + } + + @Test + void shouldPassForceCascadeForUnsupportedResource() { + CommandLine cmd = new CommandLine(new Kafkactl()); + StringWriter sw = new StringWriter(); + cmd.setOut(new PrintWriter(sw)); + + doCallRealMethod().when(formatService).prettifyKind(any()); + + Resource deletedResource = Resource.builder() + .metadata(Resource.Metadata.builder().name("topic").build()) + .build(); + + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + .thenReturn(HttpResponse.ok(List.of(deletedResource))); + + ApiResource apiResource = ApiResource.builder() + .kind("Topic") + .path("topics") + .names(List.of("topics", "topic", "to")) + .namespaced(true) + .synchronizable(true) + .build(); + + boolean actual = + resourceService.delete(apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) - .delete(eq("namespace"), eq("connect-clusters"), any(), eq("cluster"), isNull(), eq(false), eq(true)); + .delete(eq("namespace"), eq("topics"), any(), eq("topic"), isNull(), eq(false), eq(true), eq(true)); } @Test @@ -970,7 +1087,7 @@ void shouldDeleteNonNamespacedResource() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1005,7 +1122,7 @@ void shouldDeleteMultipleNonNamespacedResources() { .build(); boolean actual = - resourceService.delete(apiResource, "namespace", "name*", null, false, false, cmd.getCommandSpec()); + resourceService.delete(apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -1025,11 +1142,11 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { CommandLine cmd = new CommandLine(new Kafkactl()); HttpClientResponseException exception = new HttpClientResponseException("error", HttpResponse.serverError()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenThrow(exception); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1047,11 +1164,11 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { CommandLine cmd = new CommandLine(new Kafkactl()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) .thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1078,7 +1195,7 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From 544398c1ea037a22bee3ecc22ce0bb0774ba778f Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Sun, 19 Apr 2026 16:39:26 +0530 Subject: [PATCH 07/16] Modified ReadMe documentation for cascade delete --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f54cb7..3444e94 100644 --- a/README.md +++ b/README.md @@ -588,7 +588,7 @@ Please note that the resources are deleted instantly and cannot be recovered onc with the resource is permanently lost. ```console -Usage: kafkactl delete [-hv] [--dry-run] [--force] [-n=] ([ [-V[=]]] | [[-f=] [-R]]) +Usage: kafkactl delete [-hv] [--dry-run] [--force] [--cascade] [-n=] ([ [-V[=]]] | [[-f=] [-R]]) Description: Delete a resource. Parameters: @@ -598,6 +598,7 @@ Parameters: Options: -c, --context= Override context defined in config. + --cascade Cascade delete related connectors from Ns4Kafka. Only for connect cluster. --dry-run Does not persist resources. Validate only. --execute This option is mandatory to delete resources with wildcard. -f, --file= YAML file or directory containing resources to delete. @@ -619,6 +620,8 @@ kafkactl delete -f resource.yml kafkactl delete topic myTopic kafkactl delete connector myConnector --force kafkactl delete connect-cluster myConnectCluster --force +kafkactl delete connect-cluster myConnectCluster --cascade +kafkactl delete connect-cluster myConnectCluster --cascade --force kafkactl delete topic *-test kafkactl delete schema * kafkactl delete schema mySchema -V latest From 9ffb697e92a3bc72b76348949b76180bb700fb96 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Sun, 19 Apr 2026 16:42:34 +0530 Subject: [PATCH 08/16] Spotless Apply --- .../michelin/kafkactl/command/DeleteTest.java | 16 +++++-- .../kafkactl/service/ResourceServiceTest.java | 46 +++++++++++-------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index dbc8f42..2924712 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -342,7 +342,15 @@ void shouldForceDeleteConnectorByName() { int code = cmd.execute("connector", "prefix.connector", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("prefix.connector"), eq(null), eq(false), eq(true), eq(false), any()); + .delete( + any(), + eq("namespace"), + eq("prefix.connector"), + eq(null), + eq(false), + eq(true), + eq(false), + any()); } @Test @@ -594,7 +602,8 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()) + .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -612,7 +621,8 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()) + .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 57e1d67..3250bb2 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -811,8 +811,8 @@ void shouldDeleteNamespacedResource() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -846,8 +846,8 @@ void shouldDeleteMultipleNamespacedResources() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -907,12 +907,20 @@ void shouldForceDeleteConnector() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) - .delete(eq("namespace"), eq("connectors"), any(), eq("connector"), isNull(), eq(false), eq(true), eq(false)); + .delete( + eq("namespace"), + eq("connectors"), + any(), + eq("connector"), + isNull(), + eq(false), + eq(true), + eq(false)); } @Test @@ -938,8 +946,8 @@ void shouldForceDeleteConnectCluster() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -977,8 +985,8 @@ void shouldCascadeDeleteConnectCluster() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1016,8 +1024,8 @@ void shouldForceCascadeDeleteConnectCluster() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1055,8 +1063,8 @@ void shouldPassForceCascadeForUnsupportedResource() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1086,8 +1094,8 @@ void shouldDeleteNonNamespacedResource() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1121,8 +1129,8 @@ void shouldDeleteMultipleNonNamespacedResources() { .synchronizable(true) .build(); - boolean actual = - resourceService.delete(apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + boolean actual = resourceService.delete( + apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); From 5fa776ee904b6f486f959554dae9047d3265373b Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Thu, 23 Apr 2026 11:03:04 +0530 Subject: [PATCH 09/16] Fixing Sonar Issue - Added a model replace bolean delete flag --- .../com/michelin/kafkactl/command/Delete.java | 13 ++- .../michelin/kafkactl/model/DeleteMode.java | 56 ++++++++++ .../kafkactl/service/ResourceService.java | 9 +- .../michelin/kafkactl/command/DeleteTest.java | 61 ++++++---- .../kafkactl/service/ResourceServiceTest.java | 105 +++++++++++++++--- 5 files changed, 201 insertions(+), 43 deletions(-) create mode 100644 src/main/java/com/michelin/kafkactl/model/DeleteMode.java diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index b41b6c3..db2f698 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -20,6 +20,7 @@ import com.michelin.kafkactl.hook.DryRunHook; import com.michelin.kafkactl.model.ApiResource; +import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.service.FileService; import com.michelin.kafkactl.service.FormatService; @@ -106,7 +107,7 @@ public Integer onAuthSuccess() { // Process each document individually, return 0 when all succeed int errors = resources.stream() - .map(resource -> { + .mapToInt(resource -> { ApiResource apiResource = apiResourcesService .getResourceDefinitionByKind(resource.getKind()) .orElseThrow(); @@ -119,14 +120,14 @@ public Integer onAuthSuccess() { ? spec.get(VERSION).toString() : null), dryRun, - force, - cascade, - commandSpec); + DeleteMode.of(force, cascade), + commandSpec) + ? 0 + : 1; }) - .mapToInt(value -> value ? 0 : 1) .sum(); - return errors > 0 ? 1 : 0; + return errors == 0 ? 0 : 1; } catch (HttpClientResponseException e) { formatService.displayError(e, commandSpec); return 1; diff --git a/src/main/java/com/michelin/kafkactl/model/DeleteMode.java b/src/main/java/com/michelin/kafkactl/model/DeleteMode.java new file mode 100644 index 0000000..e35e434 --- /dev/null +++ b/src/main/java/com/michelin/kafkactl/model/DeleteMode.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.michelin.kafkactl.model; + +/** Delete mode. */ +public enum DeleteMode { + STANDARD(false, false), + FORCE(true, false), + CASCADE(false, true), + FORCE_CASCADE(true, true); + + private final boolean force; + private final boolean cascade; + + DeleteMode(boolean force, boolean cascade) { + this.force = force; + this.cascade = cascade; + } + + public boolean force() { + return force; + } + + public boolean cascade() { + return cascade; + } + + public static DeleteMode of(boolean force, boolean cascade) { + if (force && cascade) { + return FORCE_CASCADE; + } + if (force) { + return FORCE; + } + if (cascade) { + return CASCADE; + } + return STANDARD; + } +} \ No newline at end of file diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index bdcaa90..1cb5ca1 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -30,6 +30,7 @@ import com.michelin.kafkactl.client.ClusterResourceClient; import com.michelin.kafkactl.client.NamespacedResourceClient; import com.michelin.kafkactl.model.ApiResource; +import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Output; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; @@ -246,6 +247,7 @@ public HttpResponse apply( * @param name The resource name or wildcard * @param version The version of the resource, for schemas only * @param dryRun Is dry run mode or not? + * @param deleteMode The delete mode * @param commandSpec The command that triggered the action * @return true if deletion succeeded, false otherwise */ @@ -255,8 +257,7 @@ public boolean delete( String name, @Nullable String version, boolean dryRun, - boolean force, - boolean cascade, + DeleteMode deleteMode, CommandSpec commandSpec) { try { HttpResponse> response = apiResource.isNamespaced() @@ -267,8 +268,8 @@ public boolean delete( name, version, dryRun, - force, - cascade) + deleteMode.force(), + deleteMode.cascade()) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 2924712..205aca3 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when; import com.michelin.kafkactl.model.ApiResource; +import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.property.KafkactlProperties; import com.michelin.kafkactl.service.ApiResourcesService; @@ -243,7 +244,7 @@ void shouldDeleteByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -282,7 +283,7 @@ void shouldDeleteOneVersionByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -308,7 +309,7 @@ void shouldDeleteByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -334,7 +335,7 @@ void shouldForceDeleteConnectorByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -348,8 +349,7 @@ void shouldForceDeleteConnectorByName() { eq("prefix.connector"), eq(null), eq(false), - eq(true), - eq(false), + eq(DeleteMode.FORCE), any()); } @@ -368,7 +368,7 @@ void shouldForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -376,7 +376,14 @@ void shouldForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(false), any()); + .delete( + any(), + eq("namespace"), + eq("my-cluster"), + eq(null), + eq(false), + eq(DeleteMode.FORCE), + any()); } @Test @@ -394,7 +401,7 @@ void shouldCascadeDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -402,7 +409,14 @@ void shouldCascadeDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(false), eq(true), any()); + .delete( + any(), + eq("namespace"), + eq("my-cluster"), + eq(null), + eq(false), + eq(DeleteMode.CASCADE), + any()); } @Test @@ -420,7 +434,7 @@ void shouldCascadeForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -428,7 +442,14 @@ void shouldCascadeForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(true), any()); + .delete( + any(), + eq("namespace"), + eq("my-cluster"), + eq(null), + eq(false), + eq(DeleteMode.FORCE_CASCADE), + any()); } @Test @@ -446,7 +467,7 @@ void shouldDeleteOneVersionByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -482,7 +503,7 @@ void shouldNotDeleteByFileWhenInDryRunMode() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -522,7 +543,7 @@ void shouldNotDeleteByFileWhenFail() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(false); CommandLine cmd = new CommandLine(delete); @@ -576,7 +597,7 @@ void shouldDeleteByNameWithWildcard() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -603,7 +624,7 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + .delete(any(), any(), any(), any(), anyBoolean(), any(), any()); } @Test @@ -622,7 +643,7 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + .delete(any(), any(), any(), any(), anyBoolean(), any(), any()); } @Test @@ -640,7 +661,7 @@ void shouldNotDeleteByNameWithWildcardInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -667,7 +688,7 @@ void shouldNotDeleteByNameWithWildcardWithExecuteInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 3250bb2..c0926c1 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -47,6 +47,7 @@ import com.michelin.kafkactl.client.ClusterResourceClient; import com.michelin.kafkactl.client.NamespacedResourceClient; import com.michelin.kafkactl.model.ApiResource; +import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; import io.micronaut.http.HttpResponse; @@ -812,7 +813,13 @@ void shouldDeleteNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "name", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -847,7 +854,13 @@ void shouldDeleteMultipleNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "name*", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -878,7 +891,13 @@ void shouldDeleteNamespacedResourceWithVersion() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", "latest", false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "name", + "latest", + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" version latest deleted.")); @@ -908,7 +927,13 @@ void shouldForceDeleteConnector() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "connector", + null, + false, + DeleteMode.FORCE, + cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -947,7 +972,13 @@ void shouldForceDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "cluster", + null, + false, + DeleteMode.FORCE, + cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -986,7 +1017,13 @@ void shouldCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); + apiResource, + "namespace", + "cluster", + null, + false, + DeleteMode.CASCADE, + cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1025,7 +1062,13 @@ void shouldForceCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); + apiResource, + "namespace", + "cluster", + null, + false, + DeleteMode.FORCE_CASCADE, + cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1064,7 +1107,13 @@ void shouldPassForceCascadeForUnsupportedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); + apiResource, + "namespace", + "topic", + null, + false, + DeleteMode.FORCE_CASCADE, + cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1095,7 +1144,13 @@ void shouldDeleteNonNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "name", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1130,7 +1185,13 @@ void shouldDeleteMultipleNonNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "name*", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -1154,7 +1215,13 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { .thenThrow(exception); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "prefix.topic", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1176,7 +1243,13 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { .thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "prefix.topic", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1203,7 +1276,13 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + "namespace", + "prefix.topic", + null, + false, + DeleteMode.STANDARD, + cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From 618a9bc1f1046b1dbddf801893a7e708178c4115 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Thu, 23 Apr 2026 11:05:12 +0530 Subject: [PATCH 10/16] Spotless Apply --- .../com/michelin/kafkactl/command/Delete.java | 18 +-- .../michelin/kafkactl/model/DeleteMode.java | 2 +- .../michelin/kafkactl/command/DeleteTest.java | 24 +--- .../kafkactl/service/ResourceServiceTest.java | 104 +++--------------- 4 files changed, 27 insertions(+), 121 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index db2f698..75ed4ed 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -113,15 +113,15 @@ public Integer onAuthSuccess() { .orElseThrow(); Map spec = resource.getSpec(); return resourceService.delete( - apiResource, - namespace, - resource.getMetadata().getName(), - (spec != null && spec.containsKey(VERSION) - ? spec.get(VERSION).toString() - : null), - dryRun, - DeleteMode.of(force, cascade), - commandSpec) + apiResource, + namespace, + resource.getMetadata().getName(), + (spec != null && spec.containsKey(VERSION) + ? spec.get(VERSION).toString() + : null), + dryRun, + DeleteMode.of(force, cascade), + commandSpec) ? 0 : 1; }) diff --git a/src/main/java/com/michelin/kafkactl/model/DeleteMode.java b/src/main/java/com/michelin/kafkactl/model/DeleteMode.java index e35e434..b74f6a1 100644 --- a/src/main/java/com/michelin/kafkactl/model/DeleteMode.java +++ b/src/main/java/com/michelin/kafkactl/model/DeleteMode.java @@ -53,4 +53,4 @@ public static DeleteMode of(boolean force, boolean cascade) { } return STANDARD; } -} \ No newline at end of file +} diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 205aca3..686e984 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -376,14 +376,7 @@ void shouldForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete( - any(), - eq("namespace"), - eq("my-cluster"), - eq(null), - eq(false), - eq(DeleteMode.FORCE), - any()); + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(DeleteMode.FORCE), any()); } @Test @@ -409,14 +402,7 @@ void shouldCascadeDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade"); assertEquals(0, code); verify(resourceService) - .delete( - any(), - eq("namespace"), - eq("my-cluster"), - eq(null), - eq(false), - eq(DeleteMode.CASCADE), - any()); + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(DeleteMode.CASCADE), any()); } @Test @@ -623,8 +609,7 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), any(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), any(), any()); } @Test @@ -642,8 +627,7 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), any(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), any(), any()); } @Test diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index c0926c1..ebf1270 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -813,13 +813,7 @@ void shouldDeleteNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "name", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "name", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -854,13 +848,7 @@ void shouldDeleteMultipleNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "name*", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "name*", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -891,13 +879,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "name", - "latest", - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "name", "latest", false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" version latest deleted.")); @@ -927,13 +909,7 @@ void shouldForceDeleteConnector() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "connector", - null, - false, - DeleteMode.FORCE, - cmd.getCommandSpec()); + apiResource, "namespace", "connector", null, false, DeleteMode.FORCE, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -972,13 +948,7 @@ void shouldForceDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "cluster", - null, - false, - DeleteMode.FORCE, - cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, DeleteMode.FORCE, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1017,13 +987,7 @@ void shouldCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "cluster", - null, - false, - DeleteMode.CASCADE, - cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, DeleteMode.CASCADE, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1062,13 +1026,7 @@ void shouldForceCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "cluster", - null, - false, - DeleteMode.FORCE_CASCADE, - cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, DeleteMode.FORCE_CASCADE, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1107,13 +1065,7 @@ void shouldPassForceCascadeForUnsupportedResource() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "topic", - null, - false, - DeleteMode.FORCE_CASCADE, - cmd.getCommandSpec()); + apiResource, "namespace", "topic", null, false, DeleteMode.FORCE_CASCADE, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1144,13 +1096,7 @@ void shouldDeleteNonNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "name", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "name", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1185,13 +1131,7 @@ void shouldDeleteMultipleNonNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, - "namespace", - "name*", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "name*", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -1215,13 +1155,7 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { .thenThrow(exception); boolean actual = resourceService.delete( - apiResource, - "namespace", - "prefix.topic", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1243,13 +1177,7 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { .thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, - "namespace", - "prefix.topic", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1276,13 +1204,7 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, - "namespace", - "prefix.topic", - null, - false, - DeleteMode.STANDARD, - cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From 705d73c45a6a4b347386f2183315f595fe3ff4e7 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Mon, 8 Jun 2026 13:13:48 +0530 Subject: [PATCH 11/16] As per comments simplify delete force and cascade forwarding --- .../com/michelin/kafkactl/command/Delete.java | 4 +- .../michelin/kafkactl/model/DeleteMode.java | 56 ------------------- .../kafkactl/service/ResourceService.java | 11 ++-- .../michelin/kafkactl/command/DeleteTest.java | 41 +++++++------- .../kafkactl/service/ResourceServiceTest.java | 27 +++++---- 5 files changed, 42 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/com/michelin/kafkactl/model/DeleteMode.java diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index 75ed4ed..875a144 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -20,7 +20,6 @@ import com.michelin.kafkactl.hook.DryRunHook; import com.michelin.kafkactl.model.ApiResource; -import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.service.FileService; import com.michelin.kafkactl.service.FormatService; @@ -120,7 +119,8 @@ public Integer onAuthSuccess() { ? spec.get(VERSION).toString() : null), dryRun, - DeleteMode.of(force, cascade), + force, + cascade, commandSpec) ? 0 : 1; diff --git a/src/main/java/com/michelin/kafkactl/model/DeleteMode.java b/src/main/java/com/michelin/kafkactl/model/DeleteMode.java deleted file mode 100644 index b74f6a1..0000000 --- a/src/main/java/com/michelin/kafkactl/model/DeleteMode.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.michelin.kafkactl.model; - -/** Delete mode. */ -public enum DeleteMode { - STANDARD(false, false), - FORCE(true, false), - CASCADE(false, true), - FORCE_CASCADE(true, true); - - private final boolean force; - private final boolean cascade; - - DeleteMode(boolean force, boolean cascade) { - this.force = force; - this.cascade = cascade; - } - - public boolean force() { - return force; - } - - public boolean cascade() { - return cascade; - } - - public static DeleteMode of(boolean force, boolean cascade) { - if (force && cascade) { - return FORCE_CASCADE; - } - if (force) { - return FORCE; - } - if (cascade) { - return CASCADE; - } - return STANDARD; - } -} diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index 1cb5ca1..870cf7b 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -30,7 +30,6 @@ import com.michelin.kafkactl.client.ClusterResourceClient; import com.michelin.kafkactl.client.NamespacedResourceClient; import com.michelin.kafkactl.model.ApiResource; -import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Output; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; @@ -247,7 +246,8 @@ public HttpResponse apply( * @param name The resource name or wildcard * @param version The version of the resource, for schemas only * @param dryRun Is dry run mode or not? - * @param deleteMode The delete mode + * @param force Force resource deletion + * @param cascade Cascade resource deletion * @param commandSpec The command that triggered the action * @return true if deletion succeeded, false otherwise */ @@ -257,7 +257,8 @@ public boolean delete( String name, @Nullable String version, boolean dryRun, - DeleteMode deleteMode, + boolean force, + boolean cascade, CommandSpec commandSpec) { try { HttpResponse> response = apiResource.isNamespaced() @@ -268,8 +269,8 @@ public boolean delete( name, version, dryRun, - deleteMode.force(), - deleteMode.cascade()) + force, + cascade) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 686e984..2995456 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.when; import com.michelin.kafkactl.model.ApiResource; -import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.property.KafkactlProperties; import com.michelin.kafkactl.service.ApiResourcesService; @@ -244,7 +243,7 @@ void shouldDeleteByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -283,7 +282,7 @@ void shouldDeleteOneVersionByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -309,7 +308,7 @@ void shouldDeleteByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -335,7 +334,7 @@ void shouldForceDeleteConnectorByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -349,7 +348,8 @@ void shouldForceDeleteConnectorByName() { eq("prefix.connector"), eq(null), eq(false), - eq(DeleteMode.FORCE), + eq(true), + eq(false), any()); } @@ -368,7 +368,7 @@ void shouldForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -376,7 +376,7 @@ void shouldForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(DeleteMode.FORCE), any()); + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(false), any()); } @Test @@ -394,7 +394,7 @@ void shouldCascadeDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -402,7 +402,7 @@ void shouldCascadeDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(DeleteMode.CASCADE), any()); + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(false), eq(true), any()); } @Test @@ -420,7 +420,7 @@ void shouldCascadeForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -434,7 +434,8 @@ void shouldCascadeForceDeleteConnectClusterByName() { eq("my-cluster"), eq(null), eq(false), - eq(DeleteMode.FORCE_CASCADE), + eq(true), + eq(true), any()); } @@ -453,7 +454,7 @@ void shouldDeleteOneVersionByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -489,7 +490,7 @@ void shouldNotDeleteByFileWhenInDryRunMode() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -529,7 +530,7 @@ void shouldNotDeleteByFileWhenFail() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(false); CommandLine cmd = new CommandLine(delete); @@ -583,7 +584,7 @@ void shouldDeleteByNameWithWildcard() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -609,7 +610,7 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), any(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -627,7 +628,7 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), any(), any()); + verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -645,7 +646,7 @@ void shouldNotDeleteByNameWithWildcardInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -672,7 +673,7 @@ void shouldNotDeleteByNameWithWildcardWithExecuteInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), any(), any())) + when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) .thenReturn(true); CommandLine cmd = new CommandLine(delete); diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index ebf1270..3250bb2 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -47,7 +47,6 @@ import com.michelin.kafkactl.client.ClusterResourceClient; import com.michelin.kafkactl.client.NamespacedResourceClient; import com.michelin.kafkactl.model.ApiResource; -import com.michelin.kafkactl.model.DeleteMode; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; import io.micronaut.http.HttpResponse; @@ -813,7 +812,7 @@ void shouldDeleteNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -848,7 +847,7 @@ void shouldDeleteMultipleNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -879,7 +878,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", "latest", false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "name", "latest", false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" version latest deleted.")); @@ -909,7 +908,7 @@ void shouldForceDeleteConnector() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "connector", null, false, DeleteMode.FORCE, cmd.getCommandSpec()); + apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -948,7 +947,7 @@ void shouldForceDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, DeleteMode.FORCE, cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -987,7 +986,7 @@ void shouldCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, DeleteMode.CASCADE, cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1026,7 +1025,7 @@ void shouldForceCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, DeleteMode.FORCE_CASCADE, cmd.getCommandSpec()); + apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1065,7 +1064,7 @@ void shouldPassForceCascadeForUnsupportedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "topic", null, false, DeleteMode.FORCE_CASCADE, cmd.getCommandSpec()); + apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); assertTrue(actual); verify(namespacedClient) @@ -1096,7 +1095,7 @@ void shouldDeleteNonNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1131,7 +1130,7 @@ void shouldDeleteMultipleNonNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -1155,7 +1154,7 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { .thenThrow(exception); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1177,7 +1176,7 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { .thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1204,7 +1203,7 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, DeleteMode.STANDARD, cmd.getCommandSpec()); + apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); assertFalse(actual); verify(formatService) From fbb40c8a27102fb354a206486c6e101ecf9fbd12 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Mon, 8 Jun 2026 13:16:00 +0530 Subject: [PATCH 12/16] Linting Resolved --- .../kafkactl/service/ResourceService.java | 12 ++++++------ .../michelin/kafkactl/command/DeleteTest.java | 16 +++++----------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index 870cf7b..ffaf229 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -246,8 +246,8 @@ public HttpResponse apply( * @param name The resource name or wildcard * @param version The version of the resource, for schemas only * @param dryRun Is dry run mode or not? - * @param force Force resource deletion - * @param cascade Cascade resource deletion + * @param force Force resource deletion + * @param cascade Cascade resource deletion * @param commandSpec The command that triggered the action * @return true if deletion succeeded, false otherwise */ @@ -257,8 +257,8 @@ public boolean delete( String name, @Nullable String version, boolean dryRun, - boolean force, - boolean cascade, + boolean force, + boolean cascade, CommandSpec commandSpec) { try { HttpResponse> response = apiResource.isNamespaced() @@ -269,8 +269,8 @@ public boolean delete( name, version, dryRun, - force, - cascade) + force, + cascade) : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); // Micronaut does not throw exception on 404, so produce a 404 manually diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 2995456..2924712 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -428,15 +428,7 @@ void shouldCascadeForceDeleteConnectClusterByName() { int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade", "--force"); assertEquals(0, code); verify(resourceService) - .delete( - any(), - eq("namespace"), - eq("my-cluster"), - eq(null), - eq(false), - eq(true), - eq(true), - any()); + .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(true), any()); } @Test @@ -610,7 +602,8 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()) + .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test @@ -628,7 +621,8 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()).delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()) + .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); } @Test From 6f3a7fe520a28397490c311a9fd8bc58d869046d Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Mon, 8 Jun 2026 14:21:31 +0530 Subject: [PATCH 13/16] Retrigger CI From 0fcebf935b7df71007339325666f38de8ac4f90d Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Tue, 9 Jun 2026 23:55:12 +0530 Subject: [PATCH 14/16] Refactor delete flow to use DeleteResourceRequest --- .../client/NamespacedResourceClient.java | 19 ++------- .../com/michelin/kafkactl/command/Delete.java | 31 ++++++++------ .../model/request/DeleteResourceRequest.java | 41 +++++++++++++++++++ .../kafkactl/service/ResourceService.java | 36 ++++------------ 4 files changed, 73 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/michelin/kafkactl/model/request/DeleteResourceRequest.java diff --git a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java index 1af6aac..bf5347d 100644 --- a/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java +++ b/src/main/java/com/michelin/kafkactl/client/NamespacedResourceClient.java @@ -19,6 +19,7 @@ package com.michelin.kafkactl.client; import com.michelin.kafkactl.model.Resource; +import com.michelin.kafkactl.model.request.DeleteResourceRequest; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Body; @@ -27,6 +28,7 @@ import io.micronaut.http.annotation.Header; import io.micronaut.http.annotation.Post; import io.micronaut.http.annotation.QueryValue; +import io.micronaut.http.annotation.RequestBean; import io.micronaut.http.client.annotation.Client; import io.micronaut.http.client.exceptions.ReadTimeoutException; import io.micronaut.retry.annotation.Retryable; @@ -39,12 +41,7 @@ public interface NamespacedResourceClient { /** * Delete a given resource. * - * @param namespace The namespace - * @param kind The kind of resource - * @param name The name of the resource - * @param token The auth token - * @param version The version of the resource, for schemas only. - * @param dryrun Is dry-run mode or not? + * @param request The delete resource request * @return The delete response */ @Delete("{namespace}/{kind}{?name,version,dryrun,force,cascade}") @@ -53,15 +50,7 @@ public interface NamespacedResourceClient { attempts = "${kafkactl.retry.attempt}", multiplier = "${kafkactl.retry.multiplier}", includes = ReadTimeoutException.class) - HttpResponse> delete( - String namespace, - String kind, - @Header("Authorization") String token, - @QueryValue String name, - @Nullable @QueryValue String version, - @QueryValue boolean dryrun, - @QueryValue boolean force, - @QueryValue boolean cascade); + HttpResponse> delete(@RequestBean DeleteResourceRequest request); /** * Apply a given resource. diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index 875a144..91c4fa7 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -18,19 +18,22 @@ */ package com.michelin.kafkactl.command; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Optional; + import com.michelin.kafkactl.hook.DryRunHook; import com.michelin.kafkactl.model.ApiResource; import com.michelin.kafkactl.model.Resource; +import com.michelin.kafkactl.model.request.DeleteResourceRequest; import com.michelin.kafkactl.service.FileService; import com.michelin.kafkactl.service.FormatService; import com.michelin.kafkactl.service.ResourceService; + import io.micronaut.core.annotation.ReflectiveAccess; import io.micronaut.http.client.exceptions.HttpClientResponseException; import jakarta.inject.Inject; -import java.io.File; -import java.util.List; -import java.util.Map; -import java.util.Optional; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -111,16 +114,20 @@ public Integer onAuthSuccess() { .getResourceDefinitionByKind(resource.getKind()) .orElseThrow(); Map spec = resource.getSpec(); + String version = spec != null && spec.containsKey(VERSION) + ? spec.get(VERSION).toString() + : null; return resourceService.delete( apiResource, - namespace, - resource.getMetadata().getName(), - (spec != null && spec.containsKey(VERSION) - ? spec.get(VERSION).toString() - : null), - dryRun, - force, - cascade, + new DeleteResourceRequest( + namespace, + apiResource.getPath(), + null, + resource.getMetadata().getName(), + version, + dryRun, + force, + cascade), commandSpec) ? 0 : 1; diff --git a/src/main/java/com/michelin/kafkactl/model/request/DeleteResourceRequest.java b/src/main/java/com/michelin/kafkactl/model/request/DeleteResourceRequest.java new file mode 100644 index 0000000..fb012d3 --- /dev/null +++ b/src/main/java/com/michelin/kafkactl/model/request/DeleteResourceRequest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.michelin.kafkactl.model.request; + +import io.micronaut.core.annotation.Introspected; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.annotation.Header; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.QueryValue; + +/** Delete resource request. */ +@Introspected +public record DeleteResourceRequest( + @PathVariable String namespace, + @PathVariable String kind, + @Nullable @Header("Authorization") String token, + @QueryValue String name, + @Nullable @QueryValue String version, + @QueryValue boolean dryrun, + @QueryValue boolean force, + @QueryValue boolean cascade) { + public DeleteResourceRequest withToken(String token) { + return new DeleteResourceRequest(namespace, kind, token, name, version, dryrun, force, cascade); + } +} diff --git a/src/main/java/com/michelin/kafkactl/service/ResourceService.java b/src/main/java/com/michelin/kafkactl/service/ResourceService.java index ffaf229..7c12176 100644 --- a/src/main/java/com/michelin/kafkactl/service/ResourceService.java +++ b/src/main/java/com/michelin/kafkactl/service/ResourceService.java @@ -33,6 +33,7 @@ import com.michelin.kafkactl.model.Output; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; +import com.michelin.kafkactl.model.request.DeleteResourceRequest; import io.confluent.kafka.schemaregistry.avro.AvroSchema; import io.confluent.kafka.schemaregistry.client.rest.entities.SchemaReference; import io.micronaut.core.annotation.Nullable; @@ -242,36 +243,17 @@ public HttpResponse apply( * Delete a given resource. * * @param apiResource The resource type - * @param namespace The namespace - * @param name The resource name or wildcard - * @param version The version of the resource, for schemas only - * @param dryRun Is dry run mode or not? - * @param force Force resource deletion - * @param cascade Cascade resource deletion + * @param request The delete resource request * @param commandSpec The command that triggered the action * @return true if deletion succeeded, false otherwise */ - public boolean delete( - ApiResource apiResource, - String namespace, - String name, - @Nullable String version, - boolean dryRun, - boolean force, - boolean cascade, - CommandSpec commandSpec) { + public boolean delete(ApiResource apiResource, DeleteResourceRequest request, CommandSpec commandSpec) { try { + DeleteResourceRequest authorizedRequest = request.withToken(loginService.getAuthorization()); HttpResponse> response = apiResource.isNamespaced() - ? namespacedClient.delete( - namespace, - apiResource.getPath(), - loginService.getAuthorization(), - name, - version, - dryRun, - force, - cascade) - : nonNamespacedClient.delete(loginService.getAuthorization(), apiResource.getPath(), name, dryRun); + ? namespacedClient.delete(authorizedRequest) + : nonNamespacedClient.delete( + loginService.getAuthorization(), apiResource.getPath(), request.name(), request.dryrun()); // Micronaut does not throw exception on 404, so produce a 404 manually if (response.getStatus().equals(HttpStatus.NOT_FOUND)) { @@ -286,11 +268,11 @@ public boolean delete( .commandLine() .getOut() .println(formatService.prettifyKind(apiResource.getKind()) + " \"" + resourceName + "\"" - + (version != null ? " version " + version : "") + " deleted.")); + + (request.version() != null ? " version " + request.version() : "") + " deleted.")); return true; } catch (HttpClientResponseException exception) { - formatService.displayError(exception, apiResource.getKind(), name, commandSpec); + formatService.displayError(exception, apiResource.getKind(), request.name(), commandSpec); return false; } } From 3679970ff7cf49760b669bcf9a672fb22daa2c93 Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Wed, 10 Jun 2026 14:53:45 +0530 Subject: [PATCH 15/16] Update delete tests for DeleteResourceRequest flow & coverage --- .../michelin/kafkactl/command/DeleteTest.java | 79 ++++++------ .../kafkactl/service/ResourceServiceTest.java | 114 +++++++----------- 2 files changed, 81 insertions(+), 112 deletions(-) diff --git a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java index 2924712..2de072a 100644 --- a/src/test/java/com/michelin/kafkactl/command/DeleteTest.java +++ b/src/test/java/com/michelin/kafkactl/command/DeleteTest.java @@ -31,6 +31,7 @@ import com.michelin.kafkactl.model.ApiResource; import com.michelin.kafkactl.model.Resource; +import com.michelin.kafkactl.model.request.DeleteResourceRequest; import com.michelin.kafkactl.property.KafkactlProperties; import com.michelin.kafkactl.service.ApiResourcesService; import com.michelin.kafkactl.service.ConfigService; @@ -243,8 +244,7 @@ void shouldDeleteByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -282,8 +282,7 @@ void shouldDeleteOneVersionByFile() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -308,8 +307,7 @@ void shouldDeleteByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -334,8 +332,7 @@ void shouldForceDeleteConnectorByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); @@ -344,12 +341,7 @@ void shouldForceDeleteConnectorByName() { verify(resourceService) .delete( any(), - eq("namespace"), - eq("prefix.connector"), - eq(null), - eq(false), - eq(true), - eq(false), + eq(deleteRequest("namespace", "connectors", "prefix.connector", null, false, true, false)), any()); } @@ -368,15 +360,17 @@ void shouldForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(false), any()); + .delete( + any(), + eq(deleteRequest("namespace", "connect-clusters", "my-cluster", null, false, true, false)), + any()); } @Test @@ -394,15 +388,17 @@ void shouldCascadeDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(false), eq(true), any()); + .delete( + any(), + eq(deleteRequest("namespace", "connect-clusters", "my-cluster", null, false, false, true)), + any()); } @Test @@ -420,15 +416,17 @@ void shouldCascadeForceDeleteConnectClusterByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); int code = cmd.execute("connect-cluster", "my-cluster", "-n", "namespace", "--cascade", "--force"); assertEquals(0, code); verify(resourceService) - .delete(any(), eq("namespace"), eq("my-cluster"), eq(null), eq(false), eq(true), eq(true), any()); + .delete( + any(), + eq(deleteRequest("namespace", "connect-clusters", "my-cluster", null, false, true, true)), + any()); } @Test @@ -446,8 +444,7 @@ void shouldDeleteOneVersionByName() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -482,8 +479,7 @@ void shouldNotDeleteByFileWhenInDryRunMode() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -522,8 +518,7 @@ void shouldNotDeleteByFileWhenFail() { .build(); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(false); + when(resourceService.delete(any(), any(), any())).thenReturn(false); CommandLine cmd = new CommandLine(delete); @@ -576,8 +571,7 @@ void shouldDeleteByNameWithWildcard() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -602,8 +596,7 @@ void shouldNotDeleteByNameWithWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()).delete(any(), any(), any()); } @Test @@ -621,8 +614,7 @@ void shouldNotDeleteByNameWithQuestionMarkWildcardWithoutExecute() { assertTrue(sw.toString() .contains("Rerun the command with option --dry-run to see the resources that will be deleted.")); assertTrue(sw.toString().contains("Rerun the command with option --execute to execute this operation.")); - verify(resourceService, never()) - .delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()); + verify(resourceService, never()).delete(any(), any(), any()); } @Test @@ -640,8 +632,7 @@ void shouldNotDeleteByNameWithWildcardInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -667,8 +658,7 @@ void shouldNotDeleteByNameWithWildcardWithExecuteInDryRunMode() { when(apiResourcesService.getResourceDefinitionByName(any())).thenReturn(Optional.of(apiResource)); when(apiResourcesService.getResourceDefinitionByKind(any())).thenReturn(Optional.of(apiResource)); - when(resourceService.delete(any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any())) - .thenReturn(true); + when(resourceService.delete(any(), any(), any())).thenReturn(true); CommandLine cmd = new CommandLine(delete); StringWriter sw = new StringWriter(); @@ -679,4 +669,15 @@ void shouldNotDeleteByNameWithWildcardWithExecuteInDryRunMode() { assertTrue(sw.toString().contains("Dry run execution.")); assertFalse(sw.toString().contains("You are about to potentially delete multiple resources with wildcard")); } + + private static DeleteResourceRequest deleteRequest( + String namespace, + String kind, + String name, + String version, + boolean dryRun, + boolean force, + boolean cascade) { + return new DeleteResourceRequest(namespace, kind, null, name, version, dryRun, force, cascade); + } } diff --git a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java index 3250bb2..380acd1 100644 --- a/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java +++ b/src/test/java/com/michelin/kafkactl/service/ResourceServiceTest.java @@ -35,7 +35,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.eq; @@ -49,6 +48,7 @@ import com.michelin.kafkactl.model.ApiResource; import com.michelin.kafkactl.model.Resource; import com.michelin.kafkactl.model.SubjectCompatibility; +import com.michelin.kafkactl.model.request.DeleteResourceRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.client.exceptions.HttpClientResponseException; @@ -800,7 +800,7 @@ void shouldDeleteNamespacedResource() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -812,7 +812,7 @@ void shouldDeleteNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "name", null, false, false, false), cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -834,7 +834,7 @@ void shouldDeleteMultipleNamespacedResources() { .metadata(Resource.Metadata.builder().name("name2").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any())) .thenReturn(HttpResponse.ok(List.of(deletedResource1, deletedResource2)) .header("X-Ns4kafka-Result", "created")); @@ -847,7 +847,7 @@ void shouldDeleteMultipleNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "name*", null, false, false, false), cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -866,7 +866,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .metadata(Resource.Metadata.builder().name("name").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) + when(namespacedClient.delete(any())) .thenReturn(HttpResponse.ok(List.of(deletedResource)).header("X-Ns4kafka-Result", "created")); ApiResource apiResource = ApiResource.builder() @@ -878,7 +878,7 @@ void shouldDeleteNamespacedResourceWithVersion() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", "latest", false, false, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "name", "latest", false, false, false), cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" version latest deleted.")); @@ -896,8 +896,7 @@ void shouldForceDeleteConnector() { .metadata(Resource.Metadata.builder().name("connector").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.ok(List.of(deletedResource))); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() .kind(CONNECTOR) @@ -908,19 +907,10 @@ void shouldForceDeleteConnector() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "connector", null, false, true, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "connector", null, false, true, false), cmd.getCommandSpec()); assertTrue(actual); - verify(namespacedClient) - .delete( - eq("namespace"), - eq("connectors"), - any(), - eq("connector"), - isNull(), - eq(false), - eq(true), - eq(false)); + verify(namespacedClient).delete(eq(deleteRequest(apiResource, "connector", null, false, true, false))); } @Test @@ -935,8 +925,7 @@ void shouldForceDeleteConnectCluster() { .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.ok(List.of(deletedResource))); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() .kind(CONNECT_CLUSTER) @@ -947,19 +936,10 @@ void shouldForceDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, true, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "cluster", null, false, true, false), cmd.getCommandSpec()); assertTrue(actual); - verify(namespacedClient) - .delete( - eq("namespace"), - eq("connect-clusters"), - any(), - eq("cluster"), - isNull(), - eq(false), - eq(true), - eq(false)); + verify(namespacedClient).delete(eq(deleteRequest(apiResource, "cluster", null, false, true, false))); } @Test @@ -974,8 +954,7 @@ void shouldCascadeDeleteConnectCluster() { .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.ok(List.of(deletedResource))); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() .kind(CONNECT_CLUSTER) @@ -986,19 +965,10 @@ void shouldCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, false, true, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "cluster", null, false, false, true), cmd.getCommandSpec()); assertTrue(actual); - verify(namespacedClient) - .delete( - eq("namespace"), - eq("connect-clusters"), - any(), - eq("cluster"), - isNull(), - eq(false), - eq(false), - eq(true)); + verify(namespacedClient).delete(eq(deleteRequest(apiResource, "cluster", null, false, false, true))); } @Test @@ -1013,8 +983,7 @@ void shouldForceCascadeDeleteConnectCluster() { .metadata(Resource.Metadata.builder().name("cluster").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.ok(List.of(deletedResource))); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() .kind(CONNECT_CLUSTER) @@ -1025,19 +994,10 @@ void shouldForceCascadeDeleteConnectCluster() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "cluster", null, false, true, true, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "cluster", null, false, true, true), cmd.getCommandSpec()); assertTrue(actual); - verify(namespacedClient) - .delete( - eq("namespace"), - eq("connect-clusters"), - any(), - eq("cluster"), - isNull(), - eq(false), - eq(true), - eq(true)); + verify(namespacedClient).delete(eq(deleteRequest(apiResource, "cluster", null, false, true, true))); } @Test @@ -1052,8 +1012,7 @@ void shouldPassForceCascadeForUnsupportedResource() { .metadata(Resource.Metadata.builder().name("topic").build()) .build(); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.ok(List.of(deletedResource))); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.ok(List.of(deletedResource))); ApiResource apiResource = ApiResource.builder() .kind("Topic") @@ -1064,11 +1023,10 @@ void shouldPassForceCascadeForUnsupportedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "topic", null, false, true, true, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "topic", null, false, true, true), cmd.getCommandSpec()); assertTrue(actual); - verify(namespacedClient) - .delete(eq("namespace"), eq("topics"), any(), eq("topic"), isNull(), eq(false), eq(true), eq(true)); + verify(namespacedClient).delete(eq(deleteRequest(apiResource, "topic", null, false, true, true))); } @Test @@ -1095,7 +1053,7 @@ void shouldDeleteNonNamespacedResource() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name", null, false, false, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "name", null, false, false, false), cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name\" deleted.")); @@ -1130,7 +1088,7 @@ void shouldDeleteMultipleNonNamespacedResources() { .build(); boolean actual = resourceService.delete( - apiResource, "namespace", "name*", null, false, false, false, cmd.getCommandSpec()); + apiResource, deleteRequest(apiResource, "name*", null, false, false, false), cmd.getCommandSpec()); assertTrue(actual); assertTrue(sw.toString().contains("Topic \"name1\" deleted.")); @@ -1150,11 +1108,12 @@ void shouldDeleteNamespacedResourceAndHandleHttpResponseException() { CommandLine cmd = new CommandLine(new Kafkactl()); HttpClientResponseException exception = new HttpClientResponseException("error", HttpResponse.serverError()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenThrow(exception); + when(namespacedClient.delete(any())).thenThrow(exception); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + deleteRequest(apiResource, "prefix.topic", null, false, false, false), + cmd.getCommandSpec()); assertFalse(actual); verify(formatService).displayError(exception, "Topic", "prefix.topic", cmd.getCommandSpec()); @@ -1172,11 +1131,12 @@ void shouldNotDeleteWhenNamespacedResourceNotFound() { CommandLine cmd = new CommandLine(new Kafkactl()); - when(namespacedClient.delete(any(), any(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean())) - .thenReturn(HttpResponse.notFound()); + when(namespacedClient.delete(any())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + deleteRequest(apiResource, "prefix.topic", null, false, false, false), + cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1203,7 +1163,9 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { when(nonNamespacedClient.delete(any(), any(), any(), anyBoolean())).thenReturn(HttpResponse.notFound()); boolean actual = resourceService.delete( - apiResource, "namespace", "prefix.topic", null, false, false, false, cmd.getCommandSpec()); + apiResource, + deleteRequest(apiResource, "prefix.topic", null, false, false, false), + cmd.getCommandSpec()); assertFalse(actual); verify(formatService) @@ -1215,6 +1177,12 @@ void shouldNotDeleteWhenNonNamespacedResourceNotFound() { eq(cmd.getCommandSpec())); } + private static DeleteResourceRequest deleteRequest( + ApiResource apiResource, String name, String version, boolean dryRun, boolean force, boolean cascade) { + return new DeleteResourceRequest( + "namespace", apiResource.getPath(), null, name, version, dryRun, force, cascade); + } + @Test void shouldImportAllResources() { ApiResource apiResource = ApiResource.builder() From c5e69c4d55d100aa2535d8e7977887829355600a Mon Sep 17 00:00:00 2001 From: AfrinKhan02 Date: Wed, 10 Jun 2026 14:59:13 +0530 Subject: [PATCH 16/16] Spotless Apply --- .../java/com/michelin/kafkactl/command/Delete.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/michelin/kafkactl/command/Delete.java b/src/main/java/com/michelin/kafkactl/command/Delete.java index 91c4fa7..218a733 100644 --- a/src/main/java/com/michelin/kafkactl/command/Delete.java +++ b/src/main/java/com/michelin/kafkactl/command/Delete.java @@ -18,11 +18,6 @@ */ package com.michelin.kafkactl.command; -import java.io.File; -import java.util.List; -import java.util.Map; -import java.util.Optional; - import com.michelin.kafkactl.hook.DryRunHook; import com.michelin.kafkactl.model.ApiResource; import com.michelin.kafkactl.model.Resource; @@ -30,10 +25,13 @@ import com.michelin.kafkactl.service.FileService; import com.michelin.kafkactl.service.FormatService; import com.michelin.kafkactl.service.ResourceService; - import io.micronaut.core.annotation.ReflectiveAccess; import io.micronaut.http.client.exceptions.HttpClientResponseException; import jakarta.inject.Inject; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Optional; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Option;