2  * ============LICENSE_START=======================================================
 
   3  * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
 
   4  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
 
   5  * ================================================================================
 
   6  * Licensed under the Apache License, Version 2.0 (the "License");
 
   7  * you may not use this file except in compliance with the License.
 
   8  * You may obtain a copy of the License at
 
  10  *      http://www.apache.org/licenses/LICENSE-2.0
 
  12  * Unless required by applicable law or agreed to in writing, software
 
  13  * distributed under the License is distributed on an "AS IS" BASIS,
 
  14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  15  * See the License for the specific language governing permissions and
 
  16  * limitations under the License.
 
  18  * SPDX-License-Identifier: Apache-2.0
 
  19  * ============LICENSE_END=========================================================
 
  22 package org.onap.policy.clamp.acm.runtime.instantiation;
 
  24 import jakarta.validation.Valid;
 
  25 import jakarta.ws.rs.core.Response.Status;
 
  26 import java.util.HashMap;
 
  27 import java.util.List;
 
  29 import java.util.UUID;
 
  30 import java.util.stream.Collectors;
 
  31 import lombok.NonNull;
 
  32 import lombok.RequiredArgsConstructor;
 
  33 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
 
  34 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
 
  35 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
 
  36 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
 
  37 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 
  38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
 
  39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
 
  40 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
 
  41 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 
  42 import org.onap.policy.clamp.models.acm.concepts.LockState;
 
  43 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
 
  44 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 
  45 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
 
  46 import org.onap.policy.clamp.models.acm.concepts.SubState;
 
  47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
 
  48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
 
  49 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
 
  50 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
 
  51 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
 
  52 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
 
  53 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
 
  54 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
 
  55 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
 
  56 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
 
  57 import org.onap.policy.common.parameters.BeanValidationResult;
 
  58 import org.onap.policy.common.parameters.ObjectValidationResult;
 
  59 import org.onap.policy.common.parameters.ValidationStatus;
 
  60 import org.onap.policy.models.base.PfConceptKey;
 
  61 import org.onap.policy.models.base.PfKey;
 
  62 import org.onap.policy.models.base.PfModelRuntimeException;
 
  63 import org.slf4j.Logger;
 
  64 import org.slf4j.LoggerFactory;
 
  65 import org.springframework.data.domain.Pageable;
 
  66 import org.springframework.stereotype.Service;
 
  67 import org.springframework.transaction.annotation.Transactional;
 
  70  * This class is dedicated to the Instantiation of Commissioned automation composition.
 
  74 @RequiredArgsConstructor
 
  75 public class AutomationCompositionInstantiationProvider {
 
  76     private static final String DO_NOT_MATCH = " do not match with ";
 
  77     private static final String ELEMENT_ID_NOT_PRESENT = "Element id not present ";
 
  78     private static final String NOT_VALID_ORDER =
 
  79         "Not valid order %s; DeployState: %s; LockState: %s; SubState: %s; StateChangeResult: %s";
 
  81     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
 
  83     private final AutomationCompositionProvider automationCompositionProvider;
 
  84     private final AcDefinitionProvider acDefinitionProvider;
 
  85     private final AcInstanceStateResolver acInstanceStateResolver;
 
  86     private final SupervisionAcHandler supervisionAcHandler;
 
  87     private final ParticipantProvider participantProvider;
 
  88     private final AcRuntimeParameterGroup acRuntimeParameterGroup;
 
  89     private final EncryptionUtils encryptionUtils;
 
  92      * Create automation composition.
 
  94      * @param compositionId         The UUID of the automation composition definition
 
  95      * @param automationComposition the automation composition
 
  96      * @return the result of the instantiation operation
 
  98     public InstantiationResponse createAutomationComposition(UUID compositionId,
 
  99                                                              AutomationComposition automationComposition) {
 
 100         validateCompositionRequested(compositionId, automationComposition);
 
 101         var checkAutomationCompositionOpt =
 
 102             automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
 
 103         if (checkAutomationCompositionOpt.isPresent()) {
 
 104             throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 105                 automationComposition.getKey().asIdentifier() + " already defined");
 
 108         var validationResult = validateAutomationComposition(automationComposition);
 
 109         if (!validationResult.isValid()) {
 
 110             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
 
 112         encryptInstanceProperties(automationComposition, compositionId);
 
 113         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
 
 115         return createInstantiationResponse(automationComposition);
 
 118     private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
 
 119         var response = new InstantiationResponse();
 
 120         response.setInstanceId(automationComposition.getInstanceId());
 
 121         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
 
 126      * Update automation composition.
 
 128      * @param compositionId         The UUID of the automation composition definition
 
 129      * @param automationComposition the automation composition
 
 130      * @return the result of the update
 
 132     public InstantiationResponse updateAutomationComposition(UUID compositionId,
 
 133                                                              AutomationComposition automationComposition) {
 
 134         var instanceId = automationComposition.getInstanceId();
 
 135         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
 
 136         validateCompositionRequested(compositionId, acToUpdate);
 
 137         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
 
 138             acToUpdate.setElements(automationComposition.getElements());
 
 139             acToUpdate.setName(automationComposition.getName());
 
 140             acToUpdate.setVersion(automationComposition.getVersion());
 
 141             acToUpdate.setDescription(automationComposition.getDescription());
 
 142             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
 
 143             var validationResult = validateAutomationComposition(acToUpdate);
 
 144             if (!validationResult.isValid()) {
 
 145                 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
 
 147             encryptInstanceProperties(acToUpdate, compositionId);
 
 148             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
 
 149             return createInstantiationResponse(automationComposition);
 
 153         var deployOrder = DeployOrder.UPDATE;
 
 154         var subOrder = SubOrder.NONE;
 
 156         if (automationComposition.getCompositionTargetId() != null) {
 
 158             if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
 
 159                 subOrder = SubOrder.MIGRATE_PRECHECK;
 
 160                 deployOrder = DeployOrder.NONE;
 
 162                 deployOrder = DeployOrder.MIGRATE;
 
 165         var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
 
 166             acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
 
 167             acToUpdate.getStateChangeResult());
 
 168         return switch (result) {
 
 169             case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
 
 171             case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
 
 173             case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
 
 175             default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 176                 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
 
 181      * Update deployed AC Element properties.
 
 183      * @param automationComposition the automation composition
 
 184      * @param acToBeUpdated         the composition to be updated
 
 185      * @return the result of the update
 
 187     private InstantiationResponse updateDeployedAutomationComposition(
 
 188         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
 
 189         // save copy in case of a rollback
 
 190         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
 
 192         // Iterate and update the element property values
 
 193         for (var element : automationComposition.getElements().entrySet()) {
 
 194             var elementId = element.getKey();
 
 195             var dbAcElement = acToBeUpdated.getElements().get(elementId);
 
 196             if (dbAcElement == null) {
 
 197                 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
 
 199             AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
 
 202         var validationResult = validateAutomationComposition(acToBeUpdated);
 
 203         if (!validationResult.isValid()) {
 
 204             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
 
 206         updateAcForProperties(acToBeUpdated);
 
 208         var acToPublish = new AutomationComposition(acToBeUpdated);
 
 210         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
 
 212         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
 
 213         // Publish property update event to the participants
 
 214         supervisionAcHandler.update(acToPublish);
 
 215         return createInstantiationResponse(automationComposition);
 
 218     private InstantiationResponse migrateAutomationComposition(
 
 219         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
 
 221         if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
 
 222             throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 223                 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
 
 225         // make copy for rollback
 
 226         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
 
 228         // Iterate and update the element property values
 
 229         var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated);
 
 230         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
 
 232         updateAcForMigration(acToBeUpdated, acDefinition);
 
 234         var acToPublish = new AutomationComposition(acToBeUpdated);
 
 236         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
 
 238         var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
 
 239         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
 
 241         // Publish migrate event to the participants
 
 242         supervisionAcHandler.migrate(acToPublish);
 
 243         return createInstantiationResponse(ac);
 
 246     private void updateAcForMigration(AutomationComposition acToBeUpdated,
 
 247                                       AutomationCompositionDefinition acDefinition) {
 
 248         AcmUtils.setCascadedState(acToBeUpdated, DeployState.MIGRATING, LockState.LOCKED);
 
 249         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
 
 250         var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
 
 251         acToBeUpdated.setPhase(stage);
 
 254     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
 
 255         AcmUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
 
 256         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
 
 259     private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
 
 260         return acFromDb.getElements().keySet().stream()
 
 261             .filter(id -> acFromMigration.getElements().get(id) == null).toList();
 
 264     void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
 
 266         var compatibility = newDefinition.getCompatibility(dbElementDefinition);
 
 267         if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
 
 268             throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 269                 dbElementDefinition + " is not compatible with " + newDefinition);
 
 271         if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
 
 272             .equals(compatibility)) {
 
 273             LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
 
 274                 compatibility, dbElementDefinition);
 
 278     private InstantiationResponse migratePrecheckAc(
 
 279         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
 
 281         acToBeUpdated.setPrecheck(true);
 
 282         var copyAc = new AutomationComposition(acToBeUpdated);
 
 283         // Iterate and update the element property values
 
 284         updateElementsProperties(automationComposition, copyAc);
 
 286         // Publish migrate event to the participants
 
 287         supervisionAcHandler.migratePrecheck(copyAc);
 
 289         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
 
 290             SubState.MIGRATION_PRECHECKING);
 
 291         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
 
 293         return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
 
 296     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
 
 297         return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
 
 301      * Validate AutomationComposition.
 
 303      * @param automationComposition AutomationComposition to validate
 
 304      * @param compositionId         the composition id
 
 305      * @return the result of validation
 
 307     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
 
 308                                                                UUID compositionId) {
 
 310         var result = new BeanValidationResult("AutomationComposition", automationComposition);
 
 311         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
 
 312         if (acDefinitionOpt.isEmpty()) {
 
 313             result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
 
 314                 "Commissioned automation composition definition not found"));
 
 317         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
 
 318             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
 
 319                 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
 
 322         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
 
 323             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
 
 325         participantProvider.verifyParticipantState(participantIds);
 
 327         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
 
 328             acDefinitionOpt.get().getServiceTemplate(),
 
 329             acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
 
 331         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
 
 333         if (result.isValid()) {
 
 334             for (var element : automationComposition.getElements().values()) {
 
 335                 var name = element.getDefinition().getName();
 
 336                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
 
 337                 element.setParticipantId(participantId);
 
 345     private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
 
 346         if (encryptionUtils.encryptionEnabled()) {
 
 347             var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
 
 348             acDefinitionOpt.ifPresent(acDefinition
 
 349                 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
 
 354      * Get Automation Composition.
 
 356      * @param compositionId The UUID of the automation composition definition
 
 357      * @param instanceId    The UUID of the automation composition instance
 
 358      * @return the Automation Composition
 
 360     @Transactional(readOnly = true)
 
 361     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
 
 362         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
 
 363         if (!compositionId.equals(automationComposition.getCompositionId())
 
 364             && !compositionId.equals(automationComposition.getCompositionTargetId())) {
 
 365             throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 366                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
 
 368         return automationComposition;
 
 372      * Delete the automation composition with the given name and version.
 
 374      * @param compositionId The UUID of the automation composition definition
 
 375      * @param instanceId    The UUID of the automation composition instance
 
 376      * @return the result of the deletion
 
 378     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
 
 379         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
 
 380         var acDefinition = getAcDefinition(compositionId, automationComposition);
 
 381         var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
 
 383             automationComposition.getDeployState(), automationComposition.getLockState(),
 
 384             automationComposition.getSubState(), automationComposition.getStateChangeResult());
 
 385         if (!DeployOrder.DELETE.name().equals(result)) {
 
 386             var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
 
 387                 automationComposition.getDeployState(), automationComposition.getLockState(),
 
 388                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
 
 389             throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
 
 391         supervisionAcHandler.delete(automationComposition, acDefinition);
 
 392         return createInstantiationResponse(automationComposition);
 
 396      * Get the requested automation compositions.
 
 398      * @param name     the name of the automation composition to get, null for all automation compositions
 
 399      * @param version  the version of the automation composition to get, null for all automation compositions
 
 400      * @param pageable the Pageable
 
 401      * @return the automation compositions
 
 403     @Transactional(readOnly = true)
 
 404     public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
 
 405                                                             final String name, final String version,
 
 406                                                             @NonNull final Pageable pageable) {
 
 407         var automationCompositions = new AutomationCompositions();
 
 408         automationCompositions.setAutomationCompositionList(
 
 409             automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
 
 411         return automationCompositions;
 
 415      * Handle Composition Instance State.
 
 417      * @param compositionId         the compositionId
 
 418      * @param instanceId            the instanceId
 
 419      * @param acInstanceStateUpdate the AcInstanceStateUpdate
 
 421     public void compositionInstanceState(UUID compositionId, UUID instanceId,
 
 422                                          @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
 
 423         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
 
 424         var acDefinition = getAcDefinition(compositionId, automationComposition);
 
 425         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
 
 426             acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
 
 427             automationComposition.getDeployState(), automationComposition.getLockState(),
 
 428             automationComposition.getSubState(), automationComposition.getStateChangeResult());
 
 431                 supervisionAcHandler.deploy(automationComposition, acDefinition);
 
 435                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
 
 439                 supervisionAcHandler.lock(automationComposition, acDefinition);
 
 443                 supervisionAcHandler.unlock(automationComposition, acDefinition);
 
 447                 supervisionAcHandler.prepare(automationComposition, acDefinition);
 
 451                 supervisionAcHandler.review(automationComposition);
 
 455                 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
 
 456                     automationComposition.getDeployState(), automationComposition.getLockState(),
 
 457                     automationComposition.getSubState(), automationComposition.getStateChangeResult());
 
 458                 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
 
 463      * Rollback AC Instance.
 
 465      * @param instanceId the instanceId
 
 466      * @return the instantiation response
 
 468     public InstantiationResponse rollback(UUID instanceId) {
 
 469         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
 
 470         var automationCompositionToRollback =
 
 471             automationCompositionProvider.getAutomationCompositionRollback(instanceId.toString());
 
 473         if (DeployState.DEPLOYED.equals(automationComposition.getDeployState())
 
 474               && SubState.NONE.equals(automationComposition.getSubState())
 
 475               && StateChangeResult.NO_ERROR.equals(automationComposition.getStateChangeResult())) {
 
 476             automationComposition.setCompositionId(UUID.fromString(automationCompositionToRollback.getCompositionId()));
 
 477             Map<UUID, AutomationCompositionElement> elements = new HashMap<>();
 
 478             automationCompositionToRollback.getElements().forEach((String uuid, Object acElement) ->
 
 479                 elements.put(UUID.fromString(uuid), (AutomationCompositionElement) acElement));
 
 480             automationComposition.setElements(elements);
 
 481             automationComposition.setStateChangeResult(StateChangeResult.NO_ERROR);
 
 482             AcmUtils.setCascadedState(automationComposition, DeployState.MIGRATION_REVERTING, LockState.LOCKED);
 
 483             automationCompositionProvider.updateAutomationComposition(automationComposition);
 
 484             return createInstantiationResponse(automationComposition);
 
 486             throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback");
 
 490     private List<UUID> updateElementsProperties(AutomationComposition automationComposition,
 
 491                                                 AutomationComposition acToBeUpdated) {
 
 492         for (var element : automationComposition.getElements().entrySet()) {
 
 493             var elementId = element.getKey();
 
 494             var dbAcElement = acToBeUpdated.getElements().get(elementId);
 
 495             // Add additional elements if present for migration
 
 496             if (dbAcElement == null) {
 
 497                 LOGGER.info("New Ac element {} added in Migration", elementId);
 
 498                 acToBeUpdated.getElements().put(elementId, element.getValue());
 
 500                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
 
 501                 var newDefinition = element.getValue().getDefinition().asConceptKey();
 
 502                 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
 
 503                 checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
 
 504                 dbAcElement.setDefinition(element.getValue().getDefinition());
 
 507         // Remove element which is not present in the new Ac instance
 
 508         var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
 
 509         elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
 
 511         var validationResult =
 
 512             validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
 
 513         if (!validationResult.isValid()) {
 
 514             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
 
 516         acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
 
 517         return elementsRemoved;
 
 520     private static void validateCompositionRequested(UUID compositionId,
 
 521                                                      AutomationComposition automationComposition) {
 
 522         if (!compositionId.equals(automationComposition.getCompositionId())) {
 
 523             throw new PfModelRuntimeException(Status.BAD_REQUEST,
 
 524                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
 
 528     private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
 
 529                                                             AutomationComposition automationComposition) {
 
 530         validateCompositionRequested(compositionId, automationComposition);
 
 531         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
 
 533         var participantIds = acDefinition.getElementStateMap().values().stream()
 
 534             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
 
 536         participantProvider.verifyParticipantState(participantIds);