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