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