dfeb7f39ccd354a793b53061623554181a6c9b30
[policy/clamp.git] /
1 /*-
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.acm.runtime.instantiation;
23
24 import jakarta.validation.Valid;
25 import jakarta.ws.rs.core.Response.Status;
26 import java.util.List;
27 import java.util.UUID;
28 import java.util.stream.Collectors;
29 import lombok.NonNull;
30 import lombok.RequiredArgsConstructor;
31 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
32 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
33 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
34 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
39 import org.onap.policy.clamp.models.acm.concepts.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
41 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
44 import org.onap.policy.clamp.models.acm.concepts.SubState;
45 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
46 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
49 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
50 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
51 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
52 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
53 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
54 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
55 import org.onap.policy.common.parameters.BeanValidationResult;
56 import org.onap.policy.common.parameters.ObjectValidationResult;
57 import org.onap.policy.common.parameters.ValidationStatus;
58 import org.onap.policy.models.base.PfConceptKey;
59 import org.onap.policy.models.base.PfKey;
60 import org.onap.policy.models.base.PfModelRuntimeException;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63 import org.springframework.data.domain.Pageable;
64 import org.springframework.stereotype.Service;
65 import org.springframework.transaction.annotation.Transactional;
66
67 /**
68  * This class is dedicated to the Instantiation of Commissioned automation composition.
69  */
70 @Service
71 @Transactional
72 @RequiredArgsConstructor
73 public class AutomationCompositionInstantiationProvider {
74     private static final String DO_NOT_MATCH = " do not match with ";
75     private static final String ELEMENT_ID_NOT_PRESENT = "Element id not present ";
76     private static final String NOT_VALID_ORDER =
77         "Not valid order %s; DeployState: %s; LockState: %s; SubState: %s; StateChangeResult: %s";
78
79     private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionInstantiationProvider.class);
80
81     private final AutomationCompositionProvider automationCompositionProvider;
82     private final AcDefinitionProvider acDefinitionProvider;
83     private final AcInstanceStateResolver acInstanceStateResolver;
84     private final SupervisionAcHandler supervisionAcHandler;
85     private final ParticipantProvider participantProvider;
86     private final AcRuntimeParameterGroup acRuntimeParameterGroup;
87     private final EncryptionUtils encryptionUtils;
88
89     /**
90      * Create automation composition.
91      *
92      * @param compositionId         The UUID of the automation composition definition
93      * @param automationComposition the automation composition
94      * @return the result of the instantiation operation
95      */
96     public InstantiationResponse createAutomationComposition(UUID compositionId,
97                                                              AutomationComposition automationComposition) {
98         validateCompositionRequested(compositionId, automationComposition);
99         var checkAutomationCompositionOpt =
100             automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
101         if (checkAutomationCompositionOpt.isPresent()) {
102             throw new PfModelRuntimeException(Status.BAD_REQUEST,
103                 automationComposition.getKey().asIdentifier() + " already defined");
104         }
105
106         var validationResult = validateAutomationComposition(automationComposition);
107         if (!validationResult.isValid()) {
108             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
109         }
110         encryptInstanceProperties(automationComposition, compositionId);
111         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
112
113         return createInstantiationResponse(automationComposition);
114     }
115
116     private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
117         var response = new InstantiationResponse();
118         response.setInstanceId(automationComposition.getInstanceId());
119         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
120         return response;
121     }
122
123     /**
124      * Update automation composition.
125      *
126      * @param compositionId         The UUID of the automation composition definition
127      * @param automationComposition the automation composition
128      * @return the result of the update
129      */
130     public InstantiationResponse updateAutomationComposition(UUID compositionId,
131                                                              AutomationComposition automationComposition) {
132         var instanceId = automationComposition.getInstanceId();
133         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
134         validateCompositionRequested(compositionId, acToUpdate);
135         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
136             acToUpdate.setElements(automationComposition.getElements());
137             acToUpdate.setName(automationComposition.getName());
138             acToUpdate.setVersion(automationComposition.getVersion());
139             acToUpdate.setDescription(automationComposition.getDescription());
140             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
141             var validationResult = validateAutomationComposition(acToUpdate);
142             if (!validationResult.isValid()) {
143                 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
144             }
145             encryptInstanceProperties(acToUpdate, compositionId);
146             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
147             return createInstantiationResponse(automationComposition);
148
149         }
150
151         var deployOrder = DeployOrder.UPDATE;
152         var subOrder = SubOrder.NONE;
153
154         if (automationComposition.getCompositionTargetId() != null) {
155
156             if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
157                 subOrder = SubOrder.MIGRATE_PRECHECK;
158                 deployOrder = DeployOrder.NONE;
159             } else {
160                 deployOrder = DeployOrder.MIGRATE;
161             }
162         }
163         var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
164             acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
165             acToUpdate.getStateChangeResult());
166         return switch (result) {
167             case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
168
169             case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
170
171             case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
172
173             default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
174                 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
175         };
176     }
177
178     /**
179      * Update deployed AC Element properties.
180      *
181      * @param automationComposition the automation composition
182      * @param acToBeUpdated         the composition to be updated
183      * @return the result of the update
184      */
185     private InstantiationResponse updateDeployedAutomationComposition(
186         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
187         // save copy in case of a rollback
188         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
189
190         // Iterate and update the element property values
191         for (var element : automationComposition.getElements().entrySet()) {
192             var elementId = element.getKey();
193             var dbAcElement = acToBeUpdated.getElements().get(elementId);
194             if (dbAcElement == null) {
195                 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
196             }
197             AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
198         }
199
200         var validationResult = validateAutomationComposition(acToBeUpdated);
201         if (!validationResult.isValid()) {
202             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
203         }
204         updateAcForProperties(acToBeUpdated);
205
206         var acToPublish = new AutomationComposition(acToBeUpdated);
207
208         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
209
210         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
211         // Publish property update event to the participants
212         supervisionAcHandler.update(acToPublish);
213         return createInstantiationResponse(automationComposition);
214     }
215
216     private InstantiationResponse migrateAutomationComposition(
217         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
218
219         if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
220             throw new PfModelRuntimeException(Status.BAD_REQUEST,
221                 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
222         }
223         // make copy for rollback
224         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
225
226         // Iterate and update the element property values
227         var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated);
228         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
229
230         updateAcForMigration(acToBeUpdated, acDefinition, DeployState.MIGRATING);
231
232         var acToPublish = new AutomationComposition(acToBeUpdated);
233
234         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
235
236         var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
237         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
238
239         // Publish migrate event to the participants
240         supervisionAcHandler.migrate(acToPublish);
241         return createInstantiationResponse(ac);
242     }
243
244     private void updateAcForMigration(AutomationComposition acToBeUpdated,
245                                       AutomationCompositionDefinition acDefinition, DeployState deployState) {
246         AcmUtils.setCascadedState(acToBeUpdated, deployState, LockState.LOCKED);
247         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
248         var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
249         acToBeUpdated.setPhase(stage);
250     }
251
252     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
253         AcmUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
254         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
255     }
256
257     private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
258         return acFromDb.getElements().keySet().stream()
259             .filter(id -> acFromMigration.getElements().get(id) == null).toList();
260     }
261
262     void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
263                             UUID instanceId) {
264         var compatibility = newDefinition.getCompatibility(dbElementDefinition);
265         if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
266             throw new PfModelRuntimeException(Status.BAD_REQUEST,
267                 dbElementDefinition + " is not compatible with " + newDefinition);
268         }
269         if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
270             .equals(compatibility)) {
271             LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
272                 compatibility, dbElementDefinition);
273         }
274     }
275
276     private InstantiationResponse migratePrecheckAc(
277         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
278
279         acToBeUpdated.setPrecheck(true);
280         var copyAc = new AutomationComposition(acToBeUpdated);
281         // Iterate and update the element property values
282         updateElementsProperties(automationComposition, copyAc);
283
284         // Publish migrate event to the participants
285         supervisionAcHandler.migratePrecheck(copyAc);
286
287         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
288             SubState.MIGRATION_PRECHECKING);
289         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
290
291         return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
292     }
293
294     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
295         return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
296     }
297
298     /**
299      * Validate AutomationComposition.
300      *
301      * @param automationComposition AutomationComposition to validate
302      * @param compositionId         the composition id
303      * @return the result of validation
304      */
305     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
306                                                                UUID compositionId) {
307
308         var result = new BeanValidationResult("AutomationComposition", automationComposition);
309         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
310         if (acDefinitionOpt.isEmpty()) {
311             result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
312                 "Commissioned automation composition definition not found"));
313             return result;
314         }
315         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
316             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
317                 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
318             return result;
319         }
320         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
321             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
322
323         participantProvider.verifyParticipantState(participantIds);
324
325         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
326             acDefinitionOpt.get().getServiceTemplate(),
327             acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
328
329         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
330
331         if (result.isValid()) {
332             for (var element : automationComposition.getElements().values()) {
333                 var name = element.getDefinition().getName();
334                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
335                 element.setParticipantId(participantId);
336             }
337         }
338
339         return result;
340     }
341
342
343     private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
344         if (encryptionUtils.encryptionEnabled()) {
345             var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
346             acDefinitionOpt.ifPresent(acDefinition
347                 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
348         }
349     }
350
351     /**
352      * Get Automation Composition.
353      *
354      * @param compositionId The UUID of the automation composition definition
355      * @param instanceId    The UUID of the automation composition instance
356      * @return the Automation Composition
357      */
358     @Transactional(readOnly = true)
359     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
360         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
361         if (!compositionId.equals(automationComposition.getCompositionId())
362             && !compositionId.equals(automationComposition.getCompositionTargetId())) {
363             throw new PfModelRuntimeException(Status.BAD_REQUEST,
364                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
365         }
366         return automationComposition;
367     }
368
369     /**
370      * Delete the automation composition with the given name and version.
371      *
372      * @param compositionId The UUID of the automation composition definition
373      * @param instanceId    The UUID of the automation composition instance
374      * @return the result of the deletion
375      */
376     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
377         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
378         var acDefinition = getAcDefinition(compositionId, automationComposition);
379         var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
380             null, null,
381             automationComposition.getDeployState(), automationComposition.getLockState(),
382             automationComposition.getSubState(), automationComposition.getStateChangeResult());
383         if (!DeployOrder.DELETE.name().equals(result)) {
384             var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
385                 automationComposition.getDeployState(), automationComposition.getLockState(),
386                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
387             throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
388         }
389         supervisionAcHandler.delete(automationComposition, acDefinition);
390         return createInstantiationResponse(automationComposition);
391     }
392
393     /**
394      * Get the requested automation compositions.
395      *
396      * @param name     the name of the automation composition to get, null for all automation compositions
397      * @param version  the version of the automation composition to get, null for all automation compositions
398      * @param pageable the Pageable
399      * @return the automation compositions
400      */
401     @Transactional(readOnly = true)
402     public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
403                                                             final String name, final String version,
404                                                             @NonNull final Pageable pageable) {
405         var automationCompositions = new AutomationCompositions();
406         automationCompositions.setAutomationCompositionList(
407             automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
408
409         return automationCompositions;
410     }
411
412     /**
413      * Handle Composition Instance State.
414      *
415      * @param compositionId         the compositionId
416      * @param instanceId            the instanceId
417      * @param acInstanceStateUpdate the AcInstanceStateUpdate
418      */
419     public void compositionInstanceState(UUID compositionId, UUID instanceId,
420                                          @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
421         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
422         var acDefinition = getAcDefinition(compositionId, automationComposition);
423         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
424             acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
425             automationComposition.getDeployState(), automationComposition.getLockState(),
426             automationComposition.getSubState(), automationComposition.getStateChangeResult());
427         switch (result) {
428             case "DEPLOY":
429                 supervisionAcHandler.deploy(automationComposition, acDefinition);
430                 break;
431
432             case "UNDEPLOY":
433                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
434                 break;
435
436             case "LOCK":
437                 supervisionAcHandler.lock(automationComposition, acDefinition);
438                 break;
439
440             case "UNLOCK":
441                 supervisionAcHandler.unlock(automationComposition, acDefinition);
442                 break;
443
444             case "PREPARE":
445                 supervisionAcHandler.prepare(automationComposition, acDefinition);
446                 break;
447
448             case "REVIEW":
449                 supervisionAcHandler.review(automationComposition);
450                 break;
451
452             default:
453                 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
454                     automationComposition.getDeployState(), automationComposition.getLockState(),
455                     automationComposition.getSubState(), automationComposition.getStateChangeResult());
456                 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
457         }
458     }
459
460     /**
461      * Rollback AC Instance.
462      *
463      * @param compositionId The UUID of the automation composition definition
464      * @param instanceId    The UUID of the automation composition instance
465      */
466     public void rollback(UUID compositionId, UUID instanceId) {
467         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
468         validateCompositionRequested(compositionId, automationComposition);
469
470         if (!DeployOrder.MIGRATION_REVERT.name().equals(acInstanceStateResolver.resolve(
471                 DeployOrder.MIGRATION_REVERT, LockOrder.NONE,
472                 SubOrder.NONE, automationComposition.getDeployState(), automationComposition.getLockState(),
473                 automationComposition.getSubState(), automationComposition.getStateChangeResult()))) {
474             throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback");
475         }
476
477         var automationCompositionToRollback =
478             automationCompositionProvider.getAutomationCompositionRollback(instanceId);
479         var acToBeUpdated = new AutomationComposition(automationComposition);
480         acToBeUpdated.setCompositionTargetId(automationCompositionToRollback.getCompositionId());
481         acToBeUpdated.setElements(automationCompositionToRollback.getElements().values().stream()
482                 .collect(Collectors.toMap(AutomationCompositionElement::getId, AutomationCompositionElement::new)));
483
484         var acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionTargetId());
485         var validationResult =
486                 validateAutomationComposition(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
487         if (!validationResult.isValid()) {
488             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
489         }
490
491         updateAcForMigration(acToBeUpdated, acDefinition, DeployState.MIGRATION_REVERTING);
492         var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated);
493         automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
494         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
495         supervisionAcHandler.migrate(acToBeUpdated);
496     }
497
498     private List<UUID> updateElementsProperties(AutomationComposition automationComposition,
499                                                 AutomationComposition acToBeUpdated) {
500         for (var element : automationComposition.getElements().entrySet()) {
501             var elementId = element.getKey();
502             var dbAcElement = acToBeUpdated.getElements().get(elementId);
503             // Add additional elements if present for migration
504             if (dbAcElement == null) {
505                 LOGGER.info("New Ac element {} added in Migration", elementId);
506                 acToBeUpdated.getElements().put(elementId, element.getValue());
507             } else {
508                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
509                 var newDefinition = element.getValue().getDefinition().asConceptKey();
510                 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
511                 checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
512                 dbAcElement.setDefinition(element.getValue().getDefinition());
513             }
514         }
515         // Remove element which is not present in the new Ac instance
516         var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
517         elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
518
519         var validationResult =
520             validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
521         if (!validationResult.isValid()) {
522             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
523         }
524         acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
525         return elementsRemoved;
526     }
527
528     private static void validateCompositionRequested(UUID compositionId,
529                                                      AutomationComposition automationComposition) {
530         if (!compositionId.equals(automationComposition.getCompositionId())) {
531             throw new PfModelRuntimeException(Status.BAD_REQUEST,
532                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
533         }
534     }
535
536     private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
537                                                             AutomationComposition automationComposition) {
538         validateCompositionRequested(compositionId, automationComposition);
539         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
540
541         var participantIds = acDefinition.getElementStateMap().values().stream()
542             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
543
544         participantProvider.verifyParticipantState(participantIds);
545         return acDefinition;
546     }
547 }