Remove not used WeightValidationHelper (#2916)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2026-02-10 12:50:31 +02:00
committed by GitHub
parent f7a05ab73a
commit 87c4cd8cd1
35 changed files with 134 additions and 236 deletions

View File

@@ -23,7 +23,7 @@
<description>Standalone MCP server that connects to hawkBit via REST API</description>
<properties>
<spring.app.class>org.eclipse.hawkbit.mcp.server.HawkbitMcpServerApplication</spring.app.class>
<spring.app.class>org.eclipse.hawkbit.mcp.server.McpServerStart</spring.app.class>
</properties>
<dependencies>

View File

@@ -24,9 +24,9 @@ import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServic
*/
@SpringBootApplication(exclude = UserDetailsServiceAutoConfiguration.class)
@EnableConfigurationProperties(HawkbitMcpProperties.class)
public class HawkbitMcpServerApplication {
public class McpServerStart {
public static void main(String[] args) {
SpringApplication.run(HawkbitMcpServerApplication.class, args);
public static void main(final String[] args) {
SpringApplication.run(McpServerStart.class, args);
}
}
}

View File

@@ -27,6 +27,7 @@ public interface AuthenticationValidator {
* Result of authentication validation.
*/
enum ValidationResult {
/**
* Credentials are valid (authenticated user).
*/
@@ -47,4 +48,4 @@ public interface AuthenticationValidator {
*/
HAWKBIT_ERROR
}
}
}

View File

@@ -9,6 +9,11 @@
*/
package org.eclipse.hawkbit.mcp.server.client;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import feign.FeignException;
@@ -21,11 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
/**
* Validates authentication credentials against hawkBit REST API using the SDK.
* This validator is conditionally created when {@code hawkbit.mcp.validation.enabled=true}.
@@ -39,9 +39,7 @@ public class HawkbitAuthenticationValidator implements AuthenticationValidator {
private final Tenant dummyTenant;
private final Cache<String, Boolean> validationCache;
public HawkbitAuthenticationValidator(final HawkbitClient hawkbitClient,
final Tenant dummyTenant,
final HawkbitMcpProperties properties) {
public HawkbitAuthenticationValidator(final HawkbitClient hawkbitClient, final Tenant dummyTenant, final HawkbitMcpProperties properties) {
this.hawkbitClient = hawkbitClient;
this.dummyTenant = dummyTenant;
@@ -127,4 +125,4 @@ public class HawkbitAuthenticationValidator implements AuthenticationValidator {
throw new McpAuthenticationException("SHA-256 not available." + e.getMessage());
}
}
}
}

View File

@@ -14,4 +14,4 @@ public class McpAuthenticationException extends RuntimeException {
public McpAuthenticationException(String message) {
super(message);
}
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.mcp.server.config;
import java.util.function.BiFunction;
import feign.Contract;
import feign.RequestInterceptor;
import feign.codec.Decoder;
@@ -23,8 +25,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.function.BiFunction;
/**
* Common configuration for the hawkBit SDK client.
* <p>
@@ -46,7 +46,7 @@ public class HawkbitClientConfiguration {
@Bean
@Primary
public HawkbitServer hawkbitServer() {
HawkbitServer server = new HawkbitServer();
final HawkbitServer server = new HawkbitServer();
server.setMgmtUrl(properties.getMgmtUrl());
log.info("Configured hawkBit server URL: {}", properties.getMgmtUrl());
return server;
@@ -68,5 +68,4 @@ public class HawkbitClientConfiguration {
.requestInterceptorFn(hawkbitRequestInterceptor)
.build();
}
}
}

View File

@@ -26,6 +26,12 @@ import java.time.Duration;
@ConfigurationProperties(prefix = "hawkbit.mcp")
public class HawkbitMcpProperties {
public static final String OP_LIST = "list";
public static final String OP_CREATE = "create";
public static final String OP_UPDATE = "update";
public static final String OP_DELETE = "delete";
public static final String OP_DELETE_BATCH = "delete-batch";
/**
* Base URL of the hawkBit Management API (e.g., <a href="http://localhost:8080">...</a>).
*/
@@ -56,19 +62,19 @@ public class HawkbitMcpProperties {
/**
* Whether to enable the built-in hawkBit tools.
* Set to false to provide custom tool implementations.
* Set to <code>false</code> to provide custom tool implementations.
*/
private boolean toolsEnabled = true;
/**
* Whether to enable the built-in hawkBit documentation resources.
* Set to false to provide custom resource implementations.
* Set <code>false</code> provide custom resource implementations.
*/
private boolean resourcesEnabled = true;
/**
* Whether to enable the built-in hawkBit prompts.
* Set to false to provide custom prompt implementations.
* Set <code>false</code> provide custom prompt implementations.
*/
private boolean promptsEnabled = true;
@@ -130,10 +136,10 @@ public class HawkbitMcpProperties {
*/
public boolean isGlobalOperationEnabled(final String operation) {
return switch (operation.toLowerCase()) {
case "list" -> listEnabled;
case "create" -> createEnabled;
case "update" -> updateEnabled;
case "delete" -> deleteEnabled;
case OP_LIST -> listEnabled;
case OP_CREATE -> createEnabled;
case OP_UPDATE -> updateEnabled;
case OP_DELETE -> deleteEnabled;
default -> true;
};
}
@@ -155,10 +161,10 @@ public class HawkbitMcpProperties {
*/
public Boolean getOperationEnabled(final String operation) {
return switch (operation.toLowerCase()) {
case "list" -> listEnabled;
case "create" -> createEnabled;
case "update" -> updateEnabled;
case "delete" -> deleteEnabled;
case OP_LIST -> listEnabled;
case OP_CREATE -> createEnabled;
case OP_UPDATE -> updateEnabled;
case OP_DELETE -> deleteEnabled;
default -> null;
};
}
@@ -215,11 +221,11 @@ public class HawkbitMcpProperties {
*/
public Boolean getOperationEnabled(final String operation) {
return switch (operation.toLowerCase().replace("_", "-")) {
case "list" -> listEnabled;
case "delete" -> deleteEnabled;
case "delete-batch" -> deleteBatchEnabled;
case OP_LIST -> listEnabled;
case OP_DELETE -> deleteEnabled;
case OP_DELETE_BATCH -> deleteBatchEnabled;
default -> null;
};
}
}
}
}

View File

@@ -68,5 +68,4 @@ public class McpHttpClientConfiguration {
log.info("Configured tenant for HTTP mode (per-request authentication)");
return new Tenant();
}
}
}

View File

@@ -9,10 +9,14 @@
*/
package org.eclipse.hawkbit.mcp.server.config;
import java.io.IOException;
import java.util.Optional;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -33,9 +37,6 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Optional;
/**
* Security configuration for the MCP server.
* <p>
@@ -62,15 +63,14 @@ public class McpSecurityConfiguration {
@SuppressWarnings("java:S4502") // CSRF protection is not needed for stateless REST APIs using Authorization header
public SecurityFilterChain mcpSecurityFilterChain(final HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
authenticationValidator.ifPresentOrElse(
validator -> {
log.info("Authentication validation enabled - adding validation filter");
http.addFilterBefore(new HawkBitAuthenticationFilter(validator),
UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new HawkBitAuthenticationFilter(validator), UsernamePasswordAuthenticationFilter.class);
},
() -> log.info("Authentication validation disabled - requests will be forwarded without validation")
);
@@ -91,8 +91,9 @@ public class McpSecurityConfiguration {
private final AuthenticationValidator validator;
@Override
protected void doFilterInternal(final HttpServletRequest request, final @NonNull HttpServletResponse response,
final @NonNull FilterChain filterChain) throws ServletException, IOException {
protected void doFilterInternal(
final HttpServletRequest request, final @NonNull HttpServletResponse response, final @NonNull FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
final ValidationResult result = validator.validate(authHeader);
@@ -120,10 +121,7 @@ public class McpSecurityConfiguration {
throws IOException {
response.setStatus(status.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(String.format(
"{\"error\":\"%s\",\"message\":\"%s\"}",
status.getReasonPhrase(),
message));
response.getWriter().write(String.format("{\"error\":\"%s\",\"message\":\"%s\"}", status.getReasonPhrase(), message));
}
}
}
}

View File

@@ -46,9 +46,8 @@ public class McpStdioClientConfiguration {
log.info("Configuring STDIO mode request interceptor (static credentials)");
return (tenant, controller) -> template -> {
if (properties.hasStaticCredentials()) {
String credentials = properties.getUsername() + ":" + properties.getPassword();
String authHeader = "Basic " + Base64.getEncoder().encodeToString(
credentials.getBytes(StandardCharsets.UTF_8));
final String credentials = properties.getUsername() + ":" + properties.getPassword();
final String authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
template.header(HttpHeaders.AUTHORIZATION, authHeader);
log.trace("Using static credentials from properties (STDIO mode)");
} else {
@@ -72,5 +71,4 @@ public class McpStdioClientConfiguration {
}
return tenant;
}
}
}

View File

@@ -45,9 +45,7 @@ public class McpToolConfiguration {
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "hawkbit.mcp.tools-enabled", havingValue = "true", matchIfMissing = true)
public HawkbitMcpToolProvider hawkBitMcpToolProvider(
final HawkbitClient hawkbitClient,
final Tenant dummyTenant,
final HawkbitMcpProperties properties) {
final HawkbitClient hawkbitClient, final Tenant dummyTenant, final HawkbitMcpProperties properties) {
return new HawkbitMcpToolProvider(hawkbitClient, dummyTenant, properties);
}
@@ -80,4 +78,4 @@ public class McpToolConfiguration {
public HawkbitPromptProvider hawkBitPromptProvider() {
return new HawkbitPromptProvider();
}
}
}

View File

@@ -9,11 +9,11 @@
*/
package org.eclipse.hawkbit.mcp.server.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.util.List;
/**
* Sealed interface for action management operations.
*/
@@ -36,7 +36,7 @@ public sealed interface ActionRequest
* Request to delete multiple actions.
*
* @param actionIds list of action IDs to delete (mutually exclusive with rsql)
* @param rsql RSQL filter query for selecting actions to delete (mutually exclusive with actionIds)
* @param rsql RSQL filter query for selecting actions to delete (mutually exclusive with actionIds)
*/
record DeleteBatch(List<Long> actionIds, String rsql) implements ActionRequest {}
}

View File

@@ -17,13 +17,10 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription;
public record ListRequest(
@JsonPropertyDescription("RSQL filter query (e.g., 'name==test*')")
String rsql,
@JsonPropertyDescription("Number of items to skip (default: 0)")
Integer offset,
@JsonPropertyDescription("Maximum number of items to return (default: 50)")
Integer limit
) {
Integer limit) {
public static final int DEFAULT_OFFSET = 0;
public static final int DEFAULT_LIMIT = 50;
@@ -39,4 +36,4 @@ public record ListRequest(
public String getRsqlOrNull() {
return rsql != null && !rsql.isBlank() ? rsql : null;
}
}
}

View File

@@ -18,12 +18,7 @@ package org.eclipse.hawkbit.mcp.server.dto;
* @param message optional message (typically for success confirmations or error details)
* @param data the operation result data (e.g., created/updated entity)
*/
public record OperationResponse<T>(
String operation,
boolean success,
String message,
T data
) {
public record OperationResponse<T>(String operation, boolean success, String message, T data) {
/**
* Creates a successful response with data.
@@ -52,4 +47,4 @@ public record OperationResponse<T>(
public static <T> OperationResponse<T> failure(final String operation, final String message) {
return new OperationResponse<>(operation, false, message, null);
}
}
}

View File

@@ -16,14 +16,9 @@ import java.util.List;
*
* @param <T> the type of items in the response
*/
public record PagedResponse<T>(
List<T> content,
long total,
int offset,
int limit
) {
public record PagedResponse<T>(List<T> content, long total, int offset, int limit) {
public static <T> PagedResponse<T> of(final List<T> content, final long total, final int offset, final int limit) {
return new PagedResponse<>(content, total, offset, limit);
}
}
}

View File

@@ -116,4 +116,4 @@ public sealed interface RolloutRequest
* @param rolloutId the rollout ID
*/
record TriggerNextGroup(Long rolloutId) implements RolloutRequest {}
}
}

View File

@@ -47,4 +47,4 @@ public sealed interface SoftwareModuleRequest
* @param softwareModuleId the software module ID to delete
*/
record Delete(Long softwareModuleId) implements SoftwareModuleRequest {}
}
}

View File

@@ -46,4 +46,4 @@ public sealed interface TargetFilterRequest
* @param filterId the target filter ID to delete
*/
record Delete(Long filterId) implements TargetFilterRequest {}
}
}

View File

@@ -46,4 +46,4 @@ public sealed interface TargetRequest
* @param controllerId the target controller ID to delete
*/
record Delete(String controllerId) implements TargetRequest {}
}
}

View File

@@ -64,4 +64,4 @@ public class HawkbitPromptProvider {
return "Prompt content not available.";
}
}
}
}

View File

@@ -198,4 +198,4 @@ public class HawkbitDocumentationResource {
return "Documentation not available. Please refer to the hawkBit documentation at https://eclipse.dev/hawkbit/";
}
}
}
}

View File

@@ -53,10 +53,10 @@ import java.util.List;
@RequiredArgsConstructor
public class HawkbitMcpToolProvider {
private static final String OP_CREATE = "CREATE";
private static final String OP_UPDATE = "UPDATE";
private static final String OP_DELETE = "DELETE";
private static final String OP_DELETE_BATCH = "DELETE_BATCH";
private static final String OP_CREATE = HawkbitMcpProperties.OP_CREATE.toUpperCase();
private static final String OP_UPDATE = HawkbitMcpProperties.OP_UPDATE.toUpperCase();
private static final String OP_DELETE = HawkbitMcpProperties.OP_DELETE.toUpperCase();
private static final String OP_DELETE_BATCH = HawkbitMcpProperties.OP_DELETE_BATCH.toUpperCase().replace('-', '_');
private static final String OP_START = "START";
private static final String OP_PAUSE = "PAUSE";
private static final String OP_STOP = "STOP";
@@ -66,6 +66,14 @@ public class HawkbitMcpToolProvider {
private static final String OP_RETRY = "RETRY";
private static final String OP_TRIGGER_NEXT_GROUP = "TRIGGER_NEXT_GROUP";
private static final String SOFTWARE_MODULES = "softwareModules";
private static final String DISTRIBUTION_SETS = "distributionSets";
private static final String TARGETS = "targets";
private static final String TARGET_FILTERS = "targetFilters";
private static final String BODY_IS_REQUIRED_FOR_UPDATE_OPERATION = "body is required for UPDATE operation";
private static final String BODY_IS_REQUIRED_FOR_CREATE_OPERATION = "body is required for CREATE operation";
private final HawkbitClient hawkbitClient;
private final Tenant dummyTenant;
private final HawkbitMcpProperties properties;
@@ -199,13 +207,14 @@ public class HawkbitMcpToolProvider {
"{\"type\":\"Create\",\"body\":{\"controllerId\":\"id\",\"name\":\"name\"}}, " +
"{\"type\":\"Update\",\"controllerId\":\"id\",\"body\":{...}}, " +
"{\"type\":\"Delete\",\"controllerId\":\"id\"}")
@SuppressWarnings("java:S3776") // not too complex
public OperationResponse<Object> manageTarget(final TargetRequest request) {
log.debug("Managing target: request={}", request.getClass().getSimpleName());
final MgmtTargetRestApi api = hawkbitClient.mgmtService(MgmtTargetRestApi.class, dummyTenant);
if (request instanceof TargetRequest.Create r) {
validateOperation("create", "targets");
validateOperation(OP_CREATE, TARGETS);
if (r.body() == null) {
return OperationResponse.failure(OP_CREATE, "Request body is required for CREATE operation");
}
@@ -214,7 +223,7 @@ public class HawkbitMcpToolProvider {
return OperationResponse.success(OP_CREATE, "Target created successfully",
created != null && !created.isEmpty() ? created.get(0) : null);
} else if (request instanceof TargetRequest.Update r) {
validateOperation("update", "targets");
validateOperation(OP_UPDATE, TARGETS);
if (r.controllerId() == null || r.controllerId().isBlank()) {
return OperationResponse.failure(OP_UPDATE, "controllerId is required for UPDATE operation");
}
@@ -224,7 +233,7 @@ public class HawkbitMcpToolProvider {
final ResponseEntity<MgmtTarget> response = api.updateTarget(r.controllerId(), r.body());
return OperationResponse.success(OP_UPDATE, "Target updated successfully", response.getBody());
} else if (request instanceof TargetRequest.Delete r) {
validateOperation("delete", "targets");
validateOperation(OP_DELETE, TARGETS);
if (r.controllerId() == null || r.controllerId().isBlank()) {
return OperationResponse.failure(OP_DELETE, "controllerId is required for DELETE operation");
}
@@ -243,15 +252,16 @@ public class HawkbitMcpToolProvider {
"Examples: {\"type\":\"Create\",\"body\":{...}}, " +
"{\"type\":\"Start\",\"rolloutId\":123}, " +
"{\"type\":\"Approve\",\"rolloutId\":123,\"remark\":\"approved\"}")
@SuppressWarnings("java:S3776") // not too complex, iterative logic
public OperationResponse<Object> manageRollout(final RolloutRequest request) {
log.debug("Managing rollout: request={}", request.getClass().getSimpleName());
final MgmtRolloutRestApi api = hawkbitClient.mgmtService(MgmtRolloutRestApi.class, dummyTenant);
if (request instanceof RolloutRequest.Create r) {
validateRolloutOperation("create");
validateRolloutOperation(OP_CREATE);
if (r.body() == null) {
return OperationResponse.failure(OP_CREATE, "body is required for CREATE operation");
return OperationResponse.failure(OP_CREATE, BODY_IS_REQUIRED_FOR_CREATE_OPERATION);
}
final ResponseEntity<MgmtRolloutResponseBody> response = api.create(r.body());
return OperationResponse.success(OP_CREATE, "Rollout created successfully", response.getBody());
@@ -261,12 +271,12 @@ public class HawkbitMcpToolProvider {
return OperationResponse.failure(OP_UPDATE, "rolloutId is required for UPDATE operation");
}
if (r.body() == null) {
return OperationResponse.failure(OP_UPDATE, "body is required for UPDATE operation");
return OperationResponse.failure(OP_UPDATE, BODY_IS_REQUIRED_FOR_UPDATE_OPERATION);
}
final ResponseEntity<MgmtRolloutResponseBody> response = api.update(r.rolloutId(), r.body());
return OperationResponse.success(OP_UPDATE, "Rollout updated successfully", response.getBody());
} else if (request instanceof RolloutRequest.Delete r) {
validateRolloutOperation("delete");
validateRolloutOperation(OP_DELETE);
if (r.rolloutId() == null) {
return OperationResponse.failure(OP_DELETE, "rolloutId is required for DELETE operation");
}
@@ -344,26 +354,26 @@ public class HawkbitMcpToolProvider {
final MgmtDistributionSetRestApi api = hawkbitClient.mgmtService(MgmtDistributionSetRestApi.class, dummyTenant);
if (request instanceof DistributionSetRequest.Create r) {
validateOperation("create", "distributionSets");
validateOperation(OP_CREATE, DISTRIBUTION_SETS);
if (r.body() == null) {
return OperationResponse.failure(OP_CREATE, "body is required for CREATE operation");
return OperationResponse.failure(OP_CREATE, BODY_IS_REQUIRED_FOR_CREATE_OPERATION);
}
final ResponseEntity<List<MgmtDistributionSet>> response = api.createDistributionSets(List.of(r.body()));
final List<MgmtDistributionSet> created = response.getBody();
return OperationResponse.success(OP_CREATE, "Distribution set created successfully",
created != null && !created.isEmpty() ? created.get(0) : null);
} else if (request instanceof DistributionSetRequest.Update r) {
validateOperation("update", "distributionSets");
validateOperation(OP_UPDATE, DISTRIBUTION_SETS);
if (r.distributionSetId() == null) {
return OperationResponse.failure(OP_UPDATE, "distributionSetId is required for UPDATE operation");
}
if (r.body() == null) {
return OperationResponse.failure(OP_UPDATE, "body is required for UPDATE operation");
return OperationResponse.failure(OP_UPDATE, BODY_IS_REQUIRED_FOR_UPDATE_OPERATION);
}
final ResponseEntity<MgmtDistributionSet> response = api.updateDistributionSet(r.distributionSetId(), r.body());
return OperationResponse.success(OP_UPDATE, "Distribution set updated successfully", response.getBody());
} else if (request instanceof DistributionSetRequest.Delete r) {
validateOperation("delete", "distributionSets");
validateOperation(OP_DELETE, DISTRIBUTION_SETS);
if (r.distributionSetId() == null) {
return OperationResponse.failure(OP_DELETE, "distributionSetId is required for DELETE operation");
}
@@ -384,7 +394,7 @@ public class HawkbitMcpToolProvider {
final MgmtActionRestApi api = hawkbitClient.mgmtService(MgmtActionRestApi.class, dummyTenant);
if (request instanceof ActionRequest.Delete r) {
validateActionOperation("delete");
validateActionOperation(OP_DELETE);
if (r.actionId() == null) {
return OperationResponse.failure(OP_DELETE, "actionId is required for DELETE operation");
}
@@ -414,26 +424,26 @@ public class HawkbitMcpToolProvider {
final MgmtSoftwareModuleRestApi api = hawkbitClient.mgmtService(MgmtSoftwareModuleRestApi.class, dummyTenant);
if (request instanceof SoftwareModuleRequest.Create r) {
validateOperation("create", "softwareModules");
validateOperation(OP_CREATE, SOFTWARE_MODULES);
if (r.body() == null) {
return OperationResponse.failure(OP_CREATE, "body is required for CREATE operation");
return OperationResponse.failure(OP_CREATE, BODY_IS_REQUIRED_FOR_CREATE_OPERATION);
}
final ResponseEntity<List<MgmtSoftwareModule>> response = api.createSoftwareModules(List.of(r.body()));
final List<MgmtSoftwareModule> created = response.getBody();
return OperationResponse.success(OP_CREATE, "Software module created successfully",
created != null && !created.isEmpty() ? created.get(0) : null);
} else if (request instanceof SoftwareModuleRequest.Update r) {
validateOperation("update", "softwareModules");
validateOperation(OP_UPDATE, SOFTWARE_MODULES);
if (r.softwareModuleId() == null) {
return OperationResponse.failure(OP_UPDATE, "softwareModuleId is required for UPDATE operation");
}
if (r.body() == null) {
return OperationResponse.failure(OP_UPDATE, "body is required for UPDATE operation");
return OperationResponse.failure(OP_UPDATE, BODY_IS_REQUIRED_FOR_UPDATE_OPERATION);
}
final ResponseEntity<MgmtSoftwareModule> response = api.updateSoftwareModule(r.softwareModuleId(), r.body());
return OperationResponse.success(OP_UPDATE, "Software module updated successfully", response.getBody());
} else if (request instanceof SoftwareModuleRequest.Delete r) {
validateOperation("delete", "softwareModules");
validateOperation(OP_DELETE, SOFTWARE_MODULES);
if (r.softwareModuleId() == null) {
return OperationResponse.failure(OP_DELETE, "softwareModuleId is required for DELETE operation");
}
@@ -455,24 +465,24 @@ public class HawkbitMcpToolProvider {
final MgmtTargetFilterQueryRestApi api = hawkbitClient.mgmtService(MgmtTargetFilterQueryRestApi.class, dummyTenant);
if (request instanceof TargetFilterRequest.Create r) {
validateOperation("create", "targetFilters");
validateOperation(OP_CREATE, TARGET_FILTERS);
if (r.body() == null) {
return OperationResponse.failure(OP_CREATE, "body is required for CREATE operation");
return OperationResponse.failure(OP_CREATE, BODY_IS_REQUIRED_FOR_CREATE_OPERATION);
}
final ResponseEntity<MgmtTargetFilterQuery> response = api.createFilter(r.body());
return OperationResponse.success(OP_CREATE, "Target filter created successfully", response.getBody());
} else if (request instanceof TargetFilterRequest.Update r) {
validateOperation("update", "targetFilters");
validateOperation(OP_UPDATE, TARGET_FILTERS);
if (r.filterId() == null) {
return OperationResponse.failure(OP_UPDATE, "filterId is required for UPDATE operation");
}
if (r.body() == null) {
return OperationResponse.failure(OP_UPDATE, "body is required for UPDATE operation");
return OperationResponse.failure(OP_UPDATE, BODY_IS_REQUIRED_FOR_UPDATE_OPERATION);
}
final ResponseEntity<MgmtTargetFilterQuery> response = api.updateFilter(r.filterId(), r.body());
return OperationResponse.success(OP_UPDATE, "Target filter updated successfully", response.getBody());
} else if (request instanceof TargetFilterRequest.Delete r) {
validateOperation("delete", "targetFilters");
validateOperation(OP_DELETE, TARGET_FILTERS);
if (r.filterId() == null) {
return OperationResponse.failure(OP_DELETE, "filterId is required for DELETE operation");
}
@@ -517,7 +527,7 @@ public class HawkbitMcpToolProvider {
final Boolean entitySetting = config.getOperationEnabled(operation);
if (entitySetting == null) {
if (operation.equals("delete") && !properties.getOperations().isGlobalOperationEnabled("delete")) {
if (operation.equals("delete") && !properties.getOperations().isGlobalOperationEnabled(OP_DELETE)) {
throw new IllegalArgumentException(
"Operation " + operation.toUpperCase() + " is not enabled for actions. " +
"Check hawkbit.mcp.operations configuration.");
@@ -547,7 +557,7 @@ public class HawkbitMcpToolProvider {
private HawkbitMcpProperties.EntityConfig getEntityConfig(final String entity) {
final HawkbitMcpProperties.Operations ops = properties.getOperations();
return switch (entity.toLowerCase()) {
case "targets" -> ops.getTargets();
case TARGETS -> ops.getTargets();
case "rollouts" -> ops.getRollouts();
case "distributionsets" -> ops.getDistributionSets();
case "softwaremodules" -> ops.getSoftwareModules();