From e217a96c3e9aa1a75b012c9ab073ad505bf454bd Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 12:04:35 +0200 Subject: [PATCH 1/6] fix: use idiomatic patterns for error messages and stream collection - Replace string concatenation in ServiceException messages with SLF4J-style {} placeholders in AICoreSetupHandler (3 locations) - Replace Collectors.toList() with .toList() in AbstractCrudHandler - Replace new ArrayList<>() with List.of() for null case - Remove unused imports (ArrayList, Collectors) Closes #32 --- .../cds/feature/aicore/core/AICoreSetupHandler.java | 10 +++++++--- .../aicore/core/handler/AbstractCrudHandler.java | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/AICoreSetupHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/AICoreSetupHandler.java index e605e15..fddd340 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/AICoreSetupHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/AICoreSetupHandler.java @@ -41,7 +41,8 @@ public void afterSubscribe(SubscribeEventContext context) { } catch (Exception e) { throw new ServiceException( ErrorStatuses.SERVER_ERROR, - "Failed to create AI Core resources for tenant: " + tenantId, + "Failed to create AI Core resources for tenant: {}", + tenantId, e); } } @@ -79,7 +80,9 @@ private void deleteResourceGroupForTenant(String tenantId) { } throw new ServiceException( ErrorStatuses.SERVER_ERROR, - "Failed to delete AI Core resource group " + resourceGroupId + " for tenant " + tenantId, + "Failed to delete AI Core resource group {} for tenant {}", + resourceGroupId, + tenantId, e); } } @@ -103,7 +106,8 @@ private String resolveResourceGroupId(String tenantId) { } catch (OpenApiRequestException e) { throw new ServiceException( ErrorStatuses.SERVER_ERROR, - "Failed to look up AI Core resource group for tenant " + tenantId, + "Failed to look up AI Core resource group for tenant {}", + tenantId, e); } List resources = result.getResources(); diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/AbstractCrudHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/AbstractCrudHandler.java index 3793e76..47f3858 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/AbstractCrudHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/AbstractCrudHandler.java @@ -5,12 +5,10 @@ import com.sap.cds.feature.aicore.core.AICoreServiceImpl; import com.sap.cds.services.handler.EventHandler; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; abstract class AbstractCrudHandler implements EventHandler { @@ -34,7 +32,7 @@ protected static Map merge(Map keys, Map List mapResources(List resources, Function mapper) { - if (resources == null) return new ArrayList<>(); - return resources.stream().map(mapper).collect(Collectors.toList()); + if (resources == null) return List.of(); + return resources.stream().map(mapper).toList(); } } From c9953056ad58941153b8fe0297ec25d4cd084d44 Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 12:13:45 +0200 Subject: [PATCH 2/6] refactor: use cds-maven-plugin generated interfaces for type-safe entity access Replace manual AICoreElements constants class with generated typed interfaces from cds-maven-plugin (following cds-feature-attachments pattern): - Add cds-maven-plugin generate goal to cds-feature-ai-core/pom.xml - Generated interfaces: Deployments, Configurations, ResourceGroups, BckndResourceGroupLabel (with constants + typed getters/setters) - Update all handlers to use generated constants (e.g., Deployments.ID) and typed factory methods (e.g., Deployments.create(), ResourceGroups.create(id)) - Remove manual AICoreElements.java (replaced by code generation) - DeploymentHandler: use Deployments.create() with typed setters instead of 17-parameter buildDeploymentData method Closes #33 --- cds-feature-ai-core/pom.xml | 37 ++++ .../aicore/core/handler/ActionHandler.java | 4 +- .../core/handler/ConfigurationHandler.java | 55 +++--- .../core/handler/DeploymentHandler.java | 159 +++++++----------- .../core/handler/ResourceGroupHandler.java | 51 +++--- 5 files changed, 158 insertions(+), 148 deletions(-) diff --git a/cds-feature-ai-core/pom.xml b/cds-feature-ai-core/pom.xml index 7658c88..45109b8 100644 --- a/cds-feature-ai-core/pom.xml +++ b/cds-feature-ai-core/pom.xml @@ -51,6 +51,43 @@ ${project.artifactId} + + com.sap.cds + cds-maven-plugin + + + cds.install-node + + install-node + + + + cds.build + + cds + + + ./src/main/resources/cds/com.sap.cds/ai + + build --for java --src ./ --dest ../../../../../../gen/srv + + + + + cds.generate + + generate + + + com.sap.cds.feature.aicore.generated.cds4j + ${project.basedir}/src/gen/srv/src/main/resources/edmx/csn.json + + AICore.** + + + + + org.jacoco jacoco-maven-plugin diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ActionHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ActionHandler.java index b811c96..611cd23 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ActionHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ActionHandler.java @@ -3,6 +3,8 @@ */ package com.sap.cds.feature.aicore.core.handler; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.Deployments; + import com.sap.ai.sdk.core.client.DeploymentApi; import com.sap.ai.sdk.core.model.AiDeploymentModificationRequest; import com.sap.ai.sdk.core.model.AiDeploymentTargetStatus; @@ -40,7 +42,7 @@ public void onResourceGroupForTenant(EventContext context) { @On(event = "stop", entity = AICoreService.DEPLOYMENTS) public void onStop(EventContext context) { Map keys = asMap(context.get("keys")); - String deploymentId = (String) keys.get("id"); + String deploymentId = (String) keys.get(Deployments.ID); String resourceGroupId = resolveResourceGroup(keys); DeploymentApi api = service.getDeploymentApi(); diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java index d3fb8d9..69861aa 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java @@ -11,6 +11,9 @@ import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.Configurations; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ParameterArgumentBinding; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; import com.sap.cds.ql.cqn.CqnInsert; @@ -54,16 +57,16 @@ public void onRead(CdsReadEventContext context) { keys, values); - String id = (String) keys.get("id"); + String id = (String) keys.get(Configurations.ID); if (id != null) { AiConfiguration config = configurationApi.get(resourceGroupId, id); - context.setResult(List.of(toMap(config, resourceGroupId))); + context.setResult(List.of(toConfigurations(config, resourceGroupId))); } else { - String scenarioId = (String) values.get("scenarioId"); + String scenarioId = (String) values.get(Configurations.SCENARIO_ID); AiConfigurationList result = configurationApi.query(resourceGroupId, scenarioId, null, null, null, null, null, null); List> results = - mapResources(result.getResources(), c -> toMap(c, resourceGroupId)); + mapResources(result.getResources(), c -> toConfigurations(c, resourceGroupId)); logger.debug("ConfigurationApi.query returned {} resources", results.size()); context.setResult(results); } @@ -77,9 +80,9 @@ public void onCreate(CdsCreateEventContext context) { for (Map entry : entries) { String resourceGroupId = resolveResourceGroup(entry); - String name = (String) entry.get("name"); - String executableId = (String) entry.get("executableId"); - String scenarioId = (String) entry.get("scenarioId"); + String name = (String) entry.get(Configurations.NAME); + String executableId = (String) entry.get(Configurations.EXECUTABLE_ID); + String scenarioId = (String) entry.get(Configurations.SCENARIO_ID); AiConfigurationBaseData request = AiConfigurationBaseData.create() @@ -89,22 +92,22 @@ public void onCreate(CdsCreateEventContext context) { @SuppressWarnings("unchecked") List> paramBindings = - (List>) entry.get("parameterBindings"); + (List>) entry.get(Configurations.PARAMETER_BINDINGS); if (paramBindings != null) { List sdkBindings = paramBindings.stream() .map( p -> AiParameterArgumentBinding.create() - .key((String) p.get("key")) - .value((String) p.get("value"))) + .key((String) p.get(ParameterArgumentBinding.KEY)) + .value((String) p.get(ParameterArgumentBinding.VALUE))) .toList(); request.parameterBindings(sdkBindings); } var response = configurationApi.create(resourceGroupId, request); CdsData result = CdsData.create(entry); - result.put("id", response.getId()); + result.put(Configurations.ID, response.getId()); results.add(result); logger.debug( "Created configuration {} in resource group {}", response.getId(), resourceGroupId); @@ -112,25 +115,25 @@ public void onCreate(CdsCreateEventContext context) { context.setResult(results); } - private CdsData toMap(AiConfiguration config, String resourceGroupId) { - CdsData data = CdsData.create(); - data.put("id", config.getId()); - data.put("name", config.getName()); - data.put("executableId", config.getExecutableId()); - data.put("scenarioId", config.getScenarioId()); - data.put("createdAt", config.getCreatedAt()); + private Configurations toConfigurations(AiConfiguration config, String resourceGroupId) { + Configurations data = Configurations.create(); + data.setId(config.getId()); + data.setName(config.getName()); + data.setExecutableId(config.getExecutableId()); + data.setScenarioId(config.getScenarioId()); + data.put(Configurations.CREATED_AT, config.getCreatedAt()); if (config.getParameterBindings() != null) { List bindings = config.getParameterBindings().stream() .map( b -> { - CdsData bm = CdsData.create(); - bm.put("key", b.getKey()); - bm.put("value", b.getValue()); - return bm; + var bm = ParameterArgumentBinding.create(); + bm.setKey(b.getKey()); + bm.setValue(b.getValue()); + return (CdsData) bm; }) .toList(); - data.put("parameterBindings", bindings); + data.put(Configurations.PARAMETER_BINDINGS, bindings); } if (config.getInputArtifactBindings() != null) { List bindings = @@ -138,14 +141,14 @@ private CdsData toMap(AiConfiguration config, String resourceGroupId) { .map( b -> { CdsData bm = CdsData.create(); - bm.put("key", b.getKey()); + bm.put(ParameterArgumentBinding.KEY, b.getKey()); bm.put("artifactId", b.getArtifactId()); return bm; }) .toList(); - data.put("inputArtifactBindings", bindings); + data.put(Configurations.INPUT_ARTIFACT_BINDINGS, bindings); } - data.putPath("resourceGroup.resourceGroupId", resourceGroupId); + data.setResourceGroup(ResourceGroups.create(resourceGroupId)); return data; } } diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java index f1e6c76..2d3052a 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java @@ -13,6 +13,8 @@ import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.Deployments; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; import com.sap.cds.ql.cqn.CqnDelete; @@ -57,14 +59,15 @@ public void onRead(CdsReadEventContext context) { String resourceGroupId = resolveResourceGroup(merge(keys, values)); - String id = (String) keys.get("id"); + String id = (String) keys.get(Deployments.ID); if (id != null) { AiDeploymentResponseWithDetails deployment = deploymentApi.get(resourceGroupId, id); - context.setResult(List.of(toMap(deployment, resourceGroupId))); + context.setResult(List.of(toDeployments(deployment, resourceGroupId))); } else { AiDeploymentList result = deploymentApi.query(resourceGroupId, null, null, null, null, null, null, null); - context.setResult(mapResources(result.getResources(), d -> toMap(d, resourceGroupId))); + context.setResult( + mapResources(result.getResources(), d -> toDeployments(d, resourceGroupId))); } } @@ -76,19 +79,19 @@ public void onCreate(CdsCreateEventContext context) { for (Map entry : entries) { String resourceGroupId = resolveResourceGroup(entry); - String configurationId = (String) entry.get("configurationId"); + String configurationId = (String) entry.get(Deployments.CONFIGURATION_ID); AiDeploymentCreationRequest request = AiDeploymentCreationRequest.create().configurationId(configurationId); - if (entry.containsKey("ttl")) { - request.ttl((String) entry.get("ttl")); + if (entry.containsKey(Deployments.TTL)) { + request.ttl((String) entry.get(Deployments.TTL)); } var response = deploymentApi.create(resourceGroupId, request); CdsData result = CdsData.create(entry); - result.put("id", response.getId()); - result.put("status", response.getStatus().getValue()); + result.put(Deployments.ID, response.getId()); + result.put(Deployments.STATUS, response.getStatus().getValue()); results.add(result); logger.debug("Created deployment {} in resource group {}", response.getId(), resourceGroupId); } @@ -103,7 +106,8 @@ public void onUpdate(CdsUpdateEventContext context) { throw new ServiceException(ErrorStatuses.BAD_REQUEST, "No update payload provided"); } Map data = entries.get(0); - if (!data.containsKey("targetStatus") && !data.containsKey("configurationId")) { + if (!data.containsKey(Deployments.TARGET_STATUS) + && !data.containsKey(Deployments.CONFIGURATION_ID)) { throw new ServiceException( ErrorStatuses.BAD_REQUEST, "Update payload must contain 'targetStatus' or 'configurationId'"); @@ -113,17 +117,17 @@ public void onUpdate(CdsUpdateEventContext context) { CqnAnalyzer analyzer = CqnAnalyzer.create(model); Map keys = analyzer.analyze(update).targetKeys(); - String deploymentId = (String) keys.get("id"); + String deploymentId = (String) keys.get(Deployments.ID); String resourceGroupId = resolveResourceGroup(merge(keys, data)); AiDeploymentModificationRequest modRequest = AiDeploymentModificationRequest.create(); - if (data.containsKey("targetStatus")) { - String targetStatus = (String) data.get("targetStatus"); + if (data.containsKey(Deployments.TARGET_STATUS)) { + String targetStatus = (String) data.get(Deployments.TARGET_STATUS); modRequest.targetStatus(AiDeploymentTargetStatus.fromValue(targetStatus)); } - if (data.containsKey("configurationId")) { - modRequest.configurationId((String) data.get("configurationId")); + if (data.containsKey(Deployments.CONFIGURATION_ID)) { + modRequest.configurationId((String) data.get(Deployments.CONFIGURATION_ID)); } deploymentApi.modify(resourceGroupId, deploymentId, modRequest); @@ -138,7 +142,7 @@ public void onDelete(CdsDeleteEventContext context) { CqnAnalyzer analyzer = CqnAnalyzer.create(model); Map keys = analyzer.analyze(delete).targetKeys(); - String deploymentId = (String) keys.get("id"); + String deploymentId = (String) keys.get(Deployments.ID); String resourceGroupId = resolveResourceGroup(keys); deploymentApi.delete(resourceGroupId, deploymentId); @@ -147,90 +151,51 @@ public void onDelete(CdsDeleteEventContext context) { } // CPD-OFF - SDK types AiDeploymentResponseWithDetails and AiDeployment share no common interface - private CdsData toMap(AiDeploymentResponseWithDetails d, String resourceGroupId) { - return buildDeploymentData( - d.getId(), - d.getDeploymentUrl(), - d.getConfigurationId(), - d.getConfigurationName(), - d.getExecutableId(), - d.getScenarioId(), - d.getStatus().getValue(), - d.getStatusMessage(), - d.getTargetStatus().getValue(), - d.getLastOperation() != null ? d.getLastOperation().getValue() : null, - d.getLatestRunningConfigurationId(), - d.getTtl(), - d.getCreatedAt(), - d.getModifiedAt(), - d.getSubmissionTime(), - d.getStartTime(), - d.getCompletionTime(), - resourceGroupId); - } - - private CdsData toMap(AiDeployment d, String resourceGroupId) { - return buildDeploymentData( - d.getId(), - d.getDeploymentUrl(), - d.getConfigurationId(), - d.getConfigurationName(), - d.getExecutableId(), - d.getScenarioId(), - d.getStatus().getValue(), - d.getStatusMessage(), - d.getTargetStatus().getValue(), - d.getLastOperation() != null ? d.getLastOperation().getValue() : null, - d.getLatestRunningConfigurationId(), - d.getTtl(), - d.getCreatedAt(), - d.getModifiedAt(), - d.getSubmissionTime(), - d.getStartTime(), - d.getCompletionTime(), - resourceGroupId); + private static Deployments toDeployments( + AiDeploymentResponseWithDetails d, String resourceGroupId) { + Deployments data = Deployments.create(); + data.setId(d.getId()); + data.setDeploymentUrl(d.getDeploymentUrl()); + data.setConfigurationId(d.getConfigurationId()); + data.setConfigurationName(d.getConfigurationName()); + data.setExecutableId(d.getExecutableId()); + data.setScenarioId(d.getScenarioId()); + data.setStatus(d.getStatus().getValue()); + data.setStatusMessage(d.getStatusMessage()); + data.setTargetStatus(d.getTargetStatus().getValue()); + data.setLastOperation(d.getLastOperation() != null ? d.getLastOperation().getValue() : null); + data.setLatestRunningConfigurationId(d.getLatestRunningConfigurationId()); + data.setTtl(d.getTtl()); + data.put(Deployments.CREATED_AT, d.getCreatedAt()); + data.put(Deployments.MODIFIED_AT, d.getModifiedAt()); + data.put(Deployments.SUBMISSION_TIME, d.getSubmissionTime()); + data.put(Deployments.START_TIME, d.getStartTime()); + data.put(Deployments.COMPLETION_TIME, d.getCompletionTime()); + data.setResourceGroup(ResourceGroups.create(resourceGroupId)); + return data; } - // CPD-ON - - private static CdsData buildDeploymentData( - String id, - String deploymentUrl, - String configurationId, - String configurationName, - String executableId, - String scenarioId, - String status, - String statusMessage, - String targetStatus, - String lastOperation, - String latestRunningConfigurationId, - String ttl, - Object createdAt, - Object modifiedAt, - Object submissionTime, - Object startTime, - Object completionTime, - String resourceGroupId) { - CdsData data = CdsData.create(); - data.put("id", id); - data.put("deploymentUrl", deploymentUrl); - data.put("configurationId", configurationId); - data.put("configurationName", configurationName); - data.put("executableId", executableId); - data.put("scenarioId", scenarioId); - data.put("status", status); - data.put("statusMessage", statusMessage); - data.put("targetStatus", targetStatus); - data.put("lastOperation", lastOperation); - data.put("latestRunningConfigurationId", latestRunningConfigurationId); - data.put("ttl", ttl); - data.put("createdAt", createdAt); - data.put("modifiedAt", modifiedAt); - data.put("submissionTime", submissionTime); - data.put("startTime", startTime); - data.put("completionTime", completionTime); - data.putPath("resourceGroup.resourceGroupId", resourceGroupId); + private static Deployments toDeployments(AiDeployment d, String resourceGroupId) { + Deployments data = Deployments.create(); + data.setId(d.getId()); + data.setDeploymentUrl(d.getDeploymentUrl()); + data.setConfigurationId(d.getConfigurationId()); + data.setConfigurationName(d.getConfigurationName()); + data.setExecutableId(d.getExecutableId()); + data.setScenarioId(d.getScenarioId()); + data.setStatus(d.getStatus().getValue()); + data.setStatusMessage(d.getStatusMessage()); + data.setTargetStatus(d.getTargetStatus().getValue()); + data.setLastOperation(d.getLastOperation() != null ? d.getLastOperation().getValue() : null); + data.setLatestRunningConfigurationId(d.getLatestRunningConfigurationId()); + data.setTtl(d.getTtl()); + data.put(Deployments.CREATED_AT, d.getCreatedAt()); + data.put(Deployments.MODIFIED_AT, d.getModifiedAt()); + data.put(Deployments.SUBMISSION_TIME, d.getSubmissionTime()); + data.put(Deployments.START_TIME, d.getStartTime()); + data.put(Deployments.COMPLETION_TIME, d.getCompletionTime()); + data.setResourceGroup(ResourceGroups.create(resourceGroupId)); return data; } + // CPD-ON } diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java index 1037b68..685089e 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java @@ -12,6 +12,7 @@ import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; import com.sap.cds.ql.cqn.CqnDelete; @@ -53,9 +54,9 @@ public void onRead(CdsReadEventContext context) { Map keys = analysis.targetKeys(); Map values = analysis.targetValues(); - String resourceGroupId = (String) keys.get("resourceGroupId"); + String resourceGroupId = (String) keys.get(ResourceGroups.RESOURCE_GROUP_ID); if (resourceGroupId == null) { - resourceGroupId = (String) values.get("resourceGroupId"); + resourceGroupId = (String) values.get(ResourceGroups.RESOURCE_GROUP_ID); } if (resourceGroupId != null) { @@ -63,8 +64,8 @@ public void onRead(CdsReadEventContext context) { context.setResult(List.of(toMap(rg))); } else { List labelSelector = null; - if (values.containsKey("tenantId")) { - String tenantId = (String) values.get("tenantId"); + if (values.containsKey(ResourceGroups.TENANT_ID)) { + String tenantId = (String) values.get(ResourceGroups.TENANT_ID); labelSelector = List.of(AICoreServiceImpl.TENANT_LABEL_KEY + "=" + tenantId); } BckndResourceGroupList result = @@ -80,12 +81,13 @@ public void onCreate(CdsCreateEventContext context) { List> results = new ArrayList<>(); for (Map entry : entries) { - String resourceGroupId = (String) entry.get("resourceGroupId"); + String resourceGroupId = (String) entry.get(ResourceGroups.RESOURCE_GROUP_ID); BckndResourceGroupsPostRequest request = BckndResourceGroupsPostRequest.create().resourceGroupId(resourceGroupId); @SuppressWarnings("unchecked") - List> labels = (List>) entry.get("labels"); + List> labels = + (List>) entry.get(ResourceGroups.LABELS); List mergedLabels = new ArrayList<>(); // User-supplied labels take precedence: if they include the tenant label key, we skip @@ -95,8 +97,8 @@ public void onCreate(CdsCreateEventContext context) { && labels.stream() .anyMatch(l -> AICoreServiceImpl.TENANT_LABEL_KEY.equals(l.get("key"))); - if (entry.containsKey("tenantId") && !userSuppliedTenantLabel) { - String tenantId = (String) entry.get("tenantId"); + if (entry.containsKey(ResourceGroups.TENANT_ID) && !userSuppliedTenantLabel) { + String tenantId = (String) entry.get(ResourceGroups.TENANT_ID); mergedLabels.add( BckndResourceGroupLabel.create() .key(AICoreServiceImpl.TENANT_LABEL_KEY) @@ -131,7 +133,7 @@ public void onUpdate(CdsUpdateEventContext context) { BckndResourceGroupPatchRequest patchRequest = BckndResourceGroupPatchRequest.create(); @SuppressWarnings("unchecked") - List> labels = (List>) data.get("labels"); + List> labels = (List>) data.get(ResourceGroups.LABELS); if (labels != null) { patchRequest.labels(toSdkLabels(labels)); } @@ -155,11 +157,11 @@ public void onDelete(CdsDeleteEventContext context) { } private String resolveResourceGroupId(Map keys) { - if (keys.containsKey("resourceGroupId")) { - return (String) keys.get("resourceGroupId"); + if (keys.containsKey(ResourceGroups.RESOURCE_GROUP_ID)) { + return (String) keys.get(ResourceGroups.RESOURCE_GROUP_ID); } - if (keys.containsKey("tenantId")) { - return service.resourceGroupForTenant((String) keys.get("tenantId")); + if (keys.containsKey(ResourceGroups.TENANT_ID)) { + return service.resourceGroupForTenant((String) keys.get(ResourceGroups.TENANT_ID)); } return service.getDefaultResourceGroup(); } @@ -174,24 +176,25 @@ private static List toSdkLabels(List labels = new ArrayList<>(rg.getLabels().size()); for (BckndResourceGroupLabel l : rg.getLabels()) { - CdsData lm = CdsData.create(); - lm.put("key", l.getKey()); - lm.put("value", l.getValue()); + var lm = + com.sap.cds.feature.aicore.generated.cds4j.aicore.BckndResourceGroupLabel.create(); + lm.setKey(l.getKey()); + lm.setValue(l.getValue()); labels.add(lm); if (AICoreServiceImpl.TENANT_LABEL_KEY.equals(l.getKey())) { - data.put("tenantId", l.getValue()); + data.setTenantId(l.getValue()); } } - data.put("labels", labels); + data.put(ResourceGroups.LABELS, labels); } return data; } From 077c4c36dbc8a2fe5f80b80cd3b86cc4db226b8d Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 13:50:56 +0200 Subject: [PATCH 3/6] fix: use explicit types and remove unnecessary cast in ConfigurationHandler - Use List instead of var for bindings - Use ParameterArgumentBinding instead of var for bm - Remove unnecessary (CdsData) cast since ParameterArgumentBinding extends CdsData --- .../feature/aicore/core/handler/ConfigurationHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java index 69861aa..42ff03a 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java @@ -123,14 +123,14 @@ private Configurations toConfigurations(AiConfiguration config, String resourceG data.setScenarioId(config.getScenarioId()); data.put(Configurations.CREATED_AT, config.getCreatedAt()); if (config.getParameterBindings() != null) { - List bindings = + List bindings = config.getParameterBindings().stream() .map( b -> { - var bm = ParameterArgumentBinding.create(); + ParameterArgumentBinding bm = ParameterArgumentBinding.create(); bm.setKey(b.getKey()); bm.setValue(b.getValue()); - return (CdsData) bm; + return bm; }) .toList(); data.put(Configurations.PARAMETER_BINDINGS, bindings); From 69905391a048d489d0c1531f117bf8217ba6083b Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 14:02:26 +0200 Subject: [PATCH 4/6] fix: use ArtifactArgumentBinding generated interface for inputArtifactBindings Replace raw CdsData with typed ArtifactArgumentBinding.create() and setters for the inputArtifactBindings mapping in ConfigurationHandler. --- .../aicore/core/handler/ConfigurationHandler.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java index 42ff03a..307fa70 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java @@ -11,6 +11,7 @@ import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ArtifactArgumentBinding; import com.sap.cds.feature.aicore.generated.cds4j.aicore.Configurations; import com.sap.cds.feature.aicore.generated.cds4j.aicore.ParameterArgumentBinding; import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; @@ -136,13 +137,13 @@ private Configurations toConfigurations(AiConfiguration config, String resourceG data.put(Configurations.PARAMETER_BINDINGS, bindings); } if (config.getInputArtifactBindings() != null) { - List bindings = + List bindings = config.getInputArtifactBindings().stream() .map( b -> { - CdsData bm = CdsData.create(); - bm.put(ParameterArgumentBinding.KEY, b.getKey()); - bm.put("artifactId", b.getArtifactId()); + ArtifactArgumentBinding bm = ArtifactArgumentBinding.create(); + bm.setKey(b.getKey()); + bm.setArtifactId(b.getArtifactId()); return bm; }) .toList(); From cc34f3bcb16d7f27e566a20e370e4e9398b6e7aa Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 14:19:47 +0200 Subject: [PATCH 5/6] refactor: use typed data parameters in handler method signatures Use CAP Java's handler data parameter injection for onCreate/onUpdate: - DeploymentHandler.onCreate(ctx, List) - DeploymentHandler.onUpdate(ctx, List) - ConfigurationHandler.onCreate(ctx, List) - ResourceGroupHandler.onCreate(ctx, List) This eliminates manual CQN entry extraction (context.getCqn().entries()) and enables typed getters (entry.getName() vs entry.get("name")). Update tests to pass data as the second parameter directly. --- .../core/handler/ConfigurationHandler.java | 32 ++++++-------- .../core/handler/DeploymentHandler.java | 44 +++++++------------ .../core/handler/ResourceGroupHandler.java | 17 +++---- .../core/handler/DeploymentHandlerTest.java | 13 +++--- .../handler/ResourceGroupHandlerTest.java | 23 ++++------ 5 files changed, 50 insertions(+), 79 deletions(-) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java index 307fa70..6d06eae 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java @@ -14,10 +14,10 @@ import com.sap.cds.feature.aicore.generated.cds4j.aicore.ArtifactArgumentBinding; import com.sap.cds.feature.aicore.generated.cds4j.aicore.Configurations; import com.sap.cds.feature.aicore.generated.cds4j.aicore.ParameterArgumentBinding; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ParameterArgumentBindingList; import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; -import com.sap.cds.ql.cqn.CqnInsert; import com.sap.cds.ql.cqn.CqnSelect; import com.sap.cds.reflect.CdsModel; import com.sap.cds.services.cds.CdsCreateEventContext; @@ -26,6 +26,7 @@ import com.sap.cds.services.handler.annotations.On; import com.sap.cds.services.handler.annotations.ServiceName; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import org.slf4j.Logger; @@ -74,42 +75,35 @@ public void onRead(CdsReadEventContext context) { } @On(event = CqnService.EVENT_CREATE, entity = AICoreService.CONFIGURATIONS) - public void onCreate(CdsCreateEventContext context) { - CqnInsert insert = context.getCqn(); - List> entries = insert.entries(); + public void onCreate(CdsCreateEventContext context, List entries) { List> results = new ArrayList<>(); - for (Map entry : entries) { + for (Configurations entry : entries) { String resourceGroupId = resolveResourceGroup(entry); - String name = (String) entry.get(Configurations.NAME); - String executableId = (String) entry.get(Configurations.EXECUTABLE_ID); - String scenarioId = (String) entry.get(Configurations.SCENARIO_ID); AiConfigurationBaseData request = AiConfigurationBaseData.create() - .name(name) - .executableId(executableId) - .scenarioId(scenarioId); + .name(entry.getName()) + .executableId(entry.getExecutableId()) + .scenarioId(entry.getScenarioId()); - @SuppressWarnings("unchecked") - List> paramBindings = - (List>) entry.get(Configurations.PARAMETER_BINDINGS); + Collection paramBindings = + entry.getParameterBindings(); if (paramBindings != null) { List sdkBindings = paramBindings.stream() .map( p -> AiParameterArgumentBinding.create() - .key((String) p.get(ParameterArgumentBinding.KEY)) - .value((String) p.get(ParameterArgumentBinding.VALUE))) + .key(p.getKey()) + .value(p.getValue())) .toList(); request.parameterBindings(sdkBindings); } var response = configurationApi.create(resourceGroupId, request); - CdsData result = CdsData.create(entry); - result.put(Configurations.ID, response.getId()); - results.add(result); + entry.setId(response.getId()); + results.add(entry); logger.debug( "Created configuration {} in resource group {}", response.getId(), resourceGroupId); } diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java index 2d3052a..931c2a4 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandler.java @@ -10,7 +10,6 @@ import com.sap.ai.sdk.core.model.AiDeploymentModificationRequest; import com.sap.ai.sdk.core.model.AiDeploymentResponseWithDetails; import com.sap.ai.sdk.core.model.AiDeploymentTargetStatus; -import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; import com.sap.cds.feature.aicore.generated.cds4j.aicore.Deployments; @@ -18,9 +17,7 @@ import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; import com.sap.cds.ql.cqn.CqnDelete; -import com.sap.cds.ql.cqn.CqnInsert; import com.sap.cds.ql.cqn.CqnSelect; -import com.sap.cds.ql.cqn.CqnUpdate; import com.sap.cds.reflect.CdsModel; import com.sap.cds.services.ErrorStatuses; import com.sap.cds.services.ServiceException; @@ -72,42 +69,36 @@ public void onRead(CdsReadEventContext context) { } @On(event = CqnService.EVENT_CREATE, entity = AICoreService.DEPLOYMENTS) - public void onCreate(CdsCreateEventContext context) { - CqnInsert insert = context.getCqn(); - List> entries = insert.entries(); + public void onCreate(CdsCreateEventContext context, List entries) { List> results = new ArrayList<>(); - for (Map entry : entries) { + for (Deployments entry : entries) { String resourceGroupId = resolveResourceGroup(entry); - String configurationId = (String) entry.get(Deployments.CONFIGURATION_ID); + String configurationId = entry.getConfigurationId(); AiDeploymentCreationRequest request = AiDeploymentCreationRequest.create().configurationId(configurationId); - if (entry.containsKey(Deployments.TTL)) { - request.ttl((String) entry.get(Deployments.TTL)); + if (entry.getTtl() != null) { + request.ttl(entry.getTtl()); } var response = deploymentApi.create(resourceGroupId, request); - CdsData result = CdsData.create(entry); - result.put(Deployments.ID, response.getId()); - result.put(Deployments.STATUS, response.getStatus().getValue()); - results.add(result); + entry.setId(response.getId()); + entry.setStatus(response.getStatus().getValue()); + results.add(entry); logger.debug("Created deployment {} in resource group {}", response.getId(), resourceGroupId); } context.setResult(results); } @On(event = CqnService.EVENT_UPDATE, entity = AICoreService.DEPLOYMENTS) - public void onUpdate(CdsUpdateEventContext context) { - CqnUpdate update = context.getCqn(); - List> entries = update.entries(); + public void onUpdate(CdsUpdateEventContext context, List entries) { if (entries.isEmpty()) { throw new ServiceException(ErrorStatuses.BAD_REQUEST, "No update payload provided"); } - Map data = entries.get(0); - if (!data.containsKey(Deployments.TARGET_STATUS) - && !data.containsKey(Deployments.CONFIGURATION_ID)) { + Deployments data = entries.get(0); + if (data.getTargetStatus() == null && data.getConfigurationId() == null) { throw new ServiceException( ErrorStatuses.BAD_REQUEST, "Update payload must contain 'targetStatus' or 'configurationId'"); @@ -115,24 +106,23 @@ public void onUpdate(CdsUpdateEventContext context) { CdsModel model = context.getModel(); CqnAnalyzer analyzer = CqnAnalyzer.create(model); - Map keys = analyzer.analyze(update).targetKeys(); + Map keys = analyzer.analyze(context.getCqn()).targetKeys(); String deploymentId = (String) keys.get(Deployments.ID); String resourceGroupId = resolveResourceGroup(merge(keys, data)); AiDeploymentModificationRequest modRequest = AiDeploymentModificationRequest.create(); - if (data.containsKey(Deployments.TARGET_STATUS)) { - String targetStatus = (String) data.get(Deployments.TARGET_STATUS); - modRequest.targetStatus(AiDeploymentTargetStatus.fromValue(targetStatus)); + if (data.getTargetStatus() != null) { + modRequest.targetStatus(AiDeploymentTargetStatus.fromValue(data.getTargetStatus())); } - if (data.containsKey(Deployments.CONFIGURATION_ID)) { - modRequest.configurationId((String) data.get(Deployments.CONFIGURATION_ID)); + if (data.getConfigurationId() != null) { + modRequest.configurationId(data.getConfigurationId()); } deploymentApi.modify(resourceGroupId, deploymentId, modRequest); logger.debug("Updated deployment {} in resource group {}", deploymentId, resourceGroupId); - context.setResult(List.of(CdsData.create(data))); + context.setResult(List.of(data)); } @On(event = CqnService.EVENT_DELETE, entity = AICoreService.DEPLOYMENTS) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java index 685089e..aa61f7f 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandler.java @@ -16,7 +16,6 @@ import com.sap.cds.ql.cqn.AnalysisResult; import com.sap.cds.ql.cqn.CqnAnalyzer; import com.sap.cds.ql.cqn.CqnDelete; -import com.sap.cds.ql.cqn.CqnInsert; import com.sap.cds.ql.cqn.CqnSelect; import com.sap.cds.ql.cqn.CqnUpdate; import com.sap.cds.reflect.CdsModel; @@ -75,19 +74,16 @@ public void onRead(CdsReadEventContext context) { } @On(event = CqnService.EVENT_CREATE, entity = AICoreService.RESOURCE_GROUPS) - public void onCreate(CdsCreateEventContext context) { - CqnInsert insert = context.getCqn(); - List> entries = insert.entries(); + public void onCreate(CdsCreateEventContext context, List entries) { List> results = new ArrayList<>(); - for (Map entry : entries) { - String resourceGroupId = (String) entry.get(ResourceGroups.RESOURCE_GROUP_ID); + for (ResourceGroups entry : entries) { + String resourceGroupId = entry.getResourceGroupId(); BckndResourceGroupsPostRequest request = BckndResourceGroupsPostRequest.create().resourceGroupId(resourceGroupId); @SuppressWarnings("unchecked") - List> labels = - (List>) entry.get(ResourceGroups.LABELS); + List> labels = (List>) entry.get(ResourceGroups.LABELS); List mergedLabels = new ArrayList<>(); // User-supplied labels take precedence: if they include the tenant label key, we skip @@ -97,12 +93,11 @@ public void onCreate(CdsCreateEventContext context) { && labels.stream() .anyMatch(l -> AICoreServiceImpl.TENANT_LABEL_KEY.equals(l.get("key"))); - if (entry.containsKey(ResourceGroups.TENANT_ID) && !userSuppliedTenantLabel) { - String tenantId = (String) entry.get(ResourceGroups.TENANT_ID); + if (entry.getTenantId() != null && !userSuppliedTenantLabel) { mergedLabels.add( BckndResourceGroupLabel.create() .key(AICoreServiceImpl.TENANT_LABEL_KEY) - .value(tenantId)); + .value(entry.getTenantId())); } if (labels != null) { diff --git a/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandlerTest.java b/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandlerTest.java index 9b4da06..00fe4a0 100644 --- a/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandlerTest.java +++ b/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/DeploymentHandlerTest.java @@ -10,7 +10,7 @@ import com.sap.ai.sdk.core.client.DeploymentApi; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; -import com.sap.cds.ql.cqn.CqnUpdate; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.Deployments; import com.sap.cds.services.ErrorStatuses; import com.sap.cds.services.ServiceException; import com.sap.cds.services.cds.CdsUpdateEventContext; @@ -28,7 +28,6 @@ class DeploymentHandlerTest { @Mock private AICoreServiceImpl service; @Mock private DeploymentApi deploymentApi; @Mock private CdsUpdateEventContext context; - @Mock private CqnUpdate update; private DeploymentHandler cut; @@ -40,10 +39,9 @@ void setup() { @Test void onUpdate_emptyEntries_throwsBadRequest() { - when(context.getCqn()).thenReturn(update); - when(update.entries()).thenReturn(List.of()); + List entries = List.of(); - assertThatThrownBy(() -> cut.onUpdate(context)) + assertThatThrownBy(() -> cut.onUpdate(context, entries)) .isInstanceOfSatisfying( ServiceException.class, e -> assertThat(e.getErrorStatus()).isEqualTo(ErrorStatuses.BAD_REQUEST)) @@ -54,10 +52,9 @@ void onUpdate_emptyEntries_throwsBadRequest() { @Test void onUpdate_payloadWithoutTargetStatusOrConfigurationId_throwsBadRequest() { - when(context.getCqn()).thenReturn(update); - when(update.entries()).thenReturn(List.of(Map.of("ttl", "1d"))); + List entries = List.of(Deployments.of(Map.of("ttl", "1d"))); - assertThatThrownBy(() -> cut.onUpdate(context)) + assertThatThrownBy(() -> cut.onUpdate(context, entries)) .isInstanceOfSatisfying( ServiceException.class, e -> assertThat(e.getErrorStatus()).isEqualTo(ErrorStatuses.BAD_REQUEST)) diff --git a/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandlerTest.java b/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandlerTest.java index 8c9743a..925737d 100644 --- a/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandlerTest.java +++ b/cds-feature-ai-core/src/test/java/com/sap/cds/feature/aicore/core/handler/ResourceGroupHandlerTest.java @@ -12,7 +12,7 @@ import com.sap.ai.sdk.core.model.BckndResourceGroupLabel; import com.sap.ai.sdk.core.model.BckndResourceGroupsPostRequest; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; -import com.sap.cds.ql.cqn.CqnInsert; +import com.sap.cds.feature.aicore.generated.cds4j.aicore.ResourceGroups; import com.sap.cds.services.cds.CdsCreateEventContext; import java.util.List; import java.util.Map; @@ -29,7 +29,6 @@ class ResourceGroupHandlerTest { @Mock private AICoreServiceImpl service; @Mock private ResourceGroupApi resourceGroupApi; @Mock private CdsCreateEventContext context; - @Mock private CqnInsert insert; private ResourceGroupHandler handler; @@ -42,10 +41,9 @@ void setUp() { @Test void onCreate_withTenantIdOnly_setsOnlyTenantLabel() { Map entry = Map.of("resourceGroupId", "rg-1", "tenantId", "tenant-a"); - when(context.getCqn()).thenReturn(insert); - when(insert.entries()).thenReturn(List.of(entry)); + List entries = List.of(ResourceGroups.of(entry)); - handler.onCreate(context); + handler.onCreate(context, entries); BckndResourceGroupsPostRequest request = captureCreateRequest(); assertThat(request.getResourceGroupId()).isEqualTo("rg-1"); @@ -62,10 +60,9 @@ void onCreate_withLabelsOnly_setsOnlyUserLabels() { "rg-2", "labels", List.of(Map.of("key", "env", "value", "prod"), Map.of("key", "team", "value", "ai"))); - when(context.getCqn()).thenReturn(insert); - when(insert.entries()).thenReturn(List.of(entry)); + List entries = List.of(ResourceGroups.of(entry)); - handler.onCreate(context); + handler.onCreate(context, entries); BckndResourceGroupsPostRequest request = captureCreateRequest(); assertThat(request.getResourceGroupId()).isEqualTo("rg-2"); @@ -84,10 +81,9 @@ void onCreate_withTenantIdAndLabels_keepsTenantLabelAndUserLabels() { "tenant-b", "labels", List.of(Map.of("key", "env", "value", "prod"))); - when(context.getCqn()).thenReturn(insert); - when(insert.entries()).thenReturn(List.of(entry)); + List entries = List.of(ResourceGroups.of(entry)); - handler.onCreate(context); + handler.onCreate(context, entries); BckndResourceGroupsPostRequest request = captureCreateRequest(); // Tenant label first, then user-supplied labels — and tenant label is NOT lost. @@ -107,10 +103,9 @@ void onCreate_userSuppliedTenantLabelTakesPrecedence() { "tenant-auto", "labels", List.of(Map.of("key", AICoreServiceImpl.TENANT_LABEL_KEY, "value", "tenant-user"))); - when(context.getCqn()).thenReturn(insert); - when(insert.entries()).thenReturn(List.of(entry)); + List entries = List.of(ResourceGroups.of(entry)); - handler.onCreate(context); + handler.onCreate(context, entries); BckndResourceGroupsPostRequest request = captureCreateRequest(); assertThat(request.getLabels()) From da0c8dd284b4ed75d6b4a969af07570c07a244ae Mon Sep 17 00:00:00 2001 From: Marvin Lindner Date: Wed, 27 May 2026 14:27:16 +0200 Subject: [PATCH 6/6] fix: remove unused CdsData import in ConfigurationHandler --- .../cds/feature/aicore/core/handler/ConfigurationHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java index 6d06eae..9fd641f 100644 --- a/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java +++ b/cds-feature-ai-core/src/main/java/com/sap/cds/feature/aicore/core/handler/ConfigurationHandler.java @@ -8,7 +8,6 @@ import com.sap.ai.sdk.core.model.AiConfigurationBaseData; import com.sap.ai.sdk.core.model.AiConfigurationList; import com.sap.ai.sdk.core.model.AiParameterArgumentBinding; -import com.sap.cds.CdsData; import com.sap.cds.feature.aicore.core.AICoreService; import com.sap.cds.feature.aicore.core.AICoreServiceImpl; import com.sap.cds.feature.aicore.generated.cds4j.aicore.ArtifactArgumentBinding;