fdb9cd28e37469806a10d332eca3f3ae5ddee8ae
[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 = DeployState.MIGRATION_REVERTING.equals(deployState)
255                 ?  AcmStageUtils.getLastStage(acFromDb, acDefinition.getServiceTemplate())
256                 : AcmStageUtils.getFirstStage(acFromDb, acDefinition.getServiceTemplate());
257         acFromDb.setPhase(stage);
258     }
259
260     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
261         AcmStateUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
262         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
263     }
264
265     private List<AutomationCompositionElement> getElementRemoved(AutomationComposition acFromDb,
266                                                                  AutomationComposition acFromMigration) {
267         return acFromDb.getElements().values().stream()
268             .filter(element -> acFromMigration.getElements().get(element.getId()) == null).toList();
269     }
270
271
272     private InstantiationResponse migratePrecheckAc(
273             AutomationComposition automationComposition, AutomationComposition acToBeUpdated,
274             AutomationCompositionDefinition acDefinition) {
275
276         acToBeUpdated.setPrecheck(true);
277         LOGGER.info("Running migrate precheck for id: {}", automationComposition.getInstanceId());
278         var copyAc = new AutomationComposition(acToBeUpdated);
279         var acDefinitionTarget = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
280         AcDefinitionProvider.checkPrimedComposition(acDefinitionTarget);
281         // Iterate and update the element property values
282         updateElementsProperties(automationComposition, copyAc, acDefinitionTarget, acDefinition);
283
284         // Publish migrate event to the participants
285         supervisionAcHandler.migratePrecheck(copyAc, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
286
287         AcmStateUtils.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     /**
295      * Validate AutomationComposition.
296      *
297      * @param automationComposition AutomationComposition to validate
298      * @param acDefinition         the Composition Definition
299      * @return the result of validation
300      */
301     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
302             AutomationCompositionDefinition acDefinition, int removeElement) {
303         var result = new BeanValidationResult("AutomationComposition", automationComposition);
304         participantProvider.checkRegisteredParticipant(acDefinition);
305         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
306                 acDefinition.getServiceTemplate(),
307             acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName(), removeElement));
308
309         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
310
311         return result;
312     }
313
314
315     private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
316         if (encryptionUtils.encryptionEnabled()) {
317             var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
318             acDefinitionOpt.ifPresent(acDefinition
319                 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
320         }
321     }
322
323     /**
324      * Get Automation Composition.
325      *
326      * @param compositionId The UUID of the automation composition definition
327      * @param instanceId    The UUID of the automation composition instance
328      * @return the Automation Composition
329      */
330     @Transactional(readOnly = true)
331     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
332         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
333         if (!compositionId.equals(automationComposition.getCompositionId())
334             && !compositionId.equals(automationComposition.getCompositionTargetId())) {
335             throw new PfModelRuntimeException(Status.BAD_REQUEST,
336                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
337         }
338         return automationComposition;
339     }
340
341     /**
342      * Delete the automation composition with the given name and version.
343      *
344      * @param compositionId The UUID of the automation composition definition
345      * @param instanceId    The UUID of the automation composition instance
346      * @return the result of the deletion
347      */
348     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
349         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
350         var acDefinition = getAcDefinition(compositionId, automationComposition);
351         LOGGER.info("Delete automation composition request received for name: {} and version: {}",
352                 automationComposition.getName(), automationComposition.getVersion());
353         var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
354             null, null,
355             automationComposition.getDeployState(), automationComposition.getLockState(),
356             automationComposition.getSubState(), automationComposition.getStateChangeResult());
357         if (!DeployOrder.DELETE.name().equals(result)) {
358             var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
359                 automationComposition.getDeployState(), automationComposition.getLockState(),
360                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
361             throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
362         }
363         supervisionAcHandler.delete(automationComposition, acDefinition);
364         return createInstantiationResponse(automationComposition);
365     }
366
367     /**
368      * Get the requested automation compositions.
369      *
370      * @param name     the name of the automation composition to get, null for all automation compositions
371      * @param version  the version of the automation composition to get, null for all automation compositions
372      * @param pageable the Pageable
373      * @return the automation compositions
374      */
375     @Transactional(readOnly = true)
376     public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
377                                                             final String name, final String version,
378                                                             @NonNull final Pageable pageable) {
379         var automationCompositions = new AutomationCompositions();
380         automationCompositions.setAutomationCompositionList(
381             automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
382
383         return automationCompositions;
384     }
385
386     /**
387      * Handle Composition Instance State.
388      *
389      * @param compositionId         the compositionId
390      * @param instanceId            the instanceId
391      * @param acInstanceStateUpdate the AcInstanceStateUpdate
392      */
393     public void compositionInstanceState(UUID compositionId, UUID instanceId,
394                                          @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
395         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
396         var acDefinition = getAcDefinition(compositionId, automationComposition);
397         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
398             acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
399             automationComposition.getDeployState(), automationComposition.getLockState(),
400             automationComposition.getSubState(), automationComposition.getStateChangeResult());
401         switch (result) {
402             case "DEPLOY":
403                 supervisionAcHandler.deploy(automationComposition, acDefinition);
404                 break;
405
406             case "UNDEPLOY":
407                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
408                 break;
409
410             case "LOCK":
411                 supervisionAcHandler.lock(automationComposition, acDefinition);
412                 break;
413
414             case "UNLOCK":
415                 supervisionAcHandler.unlock(automationComposition, acDefinition);
416                 break;
417
418             case "PREPARE":
419                 supervisionAcHandler.prepare(automationComposition, acDefinition);
420                 break;
421
422             case "REVIEW":
423                 supervisionAcHandler.review(automationComposition, acDefinition);
424                 break;
425
426             default:
427                 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
428                     automationComposition.getDeployState(), automationComposition.getLockState(),
429                     automationComposition.getSubState(), automationComposition.getStateChangeResult());
430                 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
431         }
432     }
433
434     /**
435      * Rollback AC Instance.
436      *
437      * @param compositionId The UUID of the automation composition definition
438      * @param instanceId    The UUID of the automation composition instance
439      */
440     public void rollback(UUID compositionId, UUID instanceId) {
441         LOGGER.info("Rollback automation composition request received for CompositionID: {} and InstanceID: {}",
442                 compositionId, instanceId);
443         var acFromDb = automationCompositionProvider.getAutomationComposition(instanceId);
444         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acFromDb);
445
446         if (!DeployOrder.MIGRATION_REVERT.name().equals(acInstanceStateResolver.resolve(
447                 DeployOrder.MIGRATION_REVERT, LockOrder.NONE,
448                 SubOrder.NONE, acFromDb.getDeployState(), acFromDb.getLockState(),
449                 acFromDb.getSubState(), acFromDb.getStateChangeResult()))) {
450             throw new PfModelRuntimeException(Status.BAD_REQUEST, "Invalid state for rollback");
451         }
452
453         var automationCompositionToRollback =
454             automationCompositionProvider.getAutomationCompositionRollback(instanceId);
455         var acFromDbCopy = new AutomationComposition(acFromDb);
456         acFromDbCopy.setElements(automationCompositionToRollback.getElements().values().stream()
457                 .collect(Collectors.toMap(AutomationCompositionElement::getId, AutomationCompositionElement::new)));
458
459         var acDefinition = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionId());
460         var validationResult = validateAutomationComposition(acFromDbCopy, acDefinition, 0);
461         if (!validationResult.isValid()) {
462             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
463         }
464         // Include new elements from migration for the participant undeploy
465         for (var element : acFromDb.getElements().values()) {
466             if (MigrationState.NEW.equals(element.getMigrationState())) {
467                 acFromDbCopy.getElements().put(element.getId(), element);
468             }
469             if (MigrationState.REMOVED.equals(element.getMigrationState())) {
470                 acFromDbCopy.getElements().get(element.getId()).setMigrationState(MigrationState.REMOVED);
471             }
472         }
473
474         updateAcForMigration(acFromDbCopy, acDefinition, DeployState.MIGRATION_REVERTING);
475         automationCompositionProvider.updateAutomationComposition(acFromDbCopy);
476         var acDefinitionTarget = acDefinitionProvider.getAcDefinition(acFromDbCopy.getCompositionTargetId());
477         supervisionAcHandler.migrate(acFromDbCopy, acDefinition.getRevisionId(), acDefinitionTarget.getRevisionId());
478     }
479
480     private void updateElementsProperties(AutomationComposition automationComposition,
481             AutomationComposition acFromDb, AutomationCompositionDefinition acDefinitionTarget,
482                                           AutomationCompositionDefinition acDefinition) {
483         for (var element : automationComposition.getElements().entrySet()) {
484             var elementId = element.getKey();
485             var dbAcElement = acFromDb.getElements().get(elementId);
486             if (dbAcElement == null) {
487                 LOGGER.info("New Ac element {} added in Migration", elementId);
488                 element.getValue().setMigrationState(MigrationState.NEW);
489                 acFromDb.getElements().put(elementId, element.getValue());
490             } else {
491                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
492                 var newDefinition = element.getValue().getDefinition().asConceptKey();
493                 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
494                 AutomationCompositionProvider.checkCompatibility(
495                         newDefinition, dbElementDefinition, automationComposition.getInstanceId());
496                 dbAcElement.setDefinition(element.getValue().getDefinition());
497             }
498         }
499         // Update migrationState for the removed elements
500         var elementsRemoved = getElementRemoved(acFromDb, automationComposition);
501         elementsRemoved.forEach(element -> acFromDb.getElements().get(element.getId())
502                 .setMigrationState(MigrationState.REMOVED));
503
504         var validationResult = validateAutomationComposition(acFromDb, acDefinitionTarget, elementsRemoved.size());
505         if (!validationResult.isValid()) {
506             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
507         } else {
508             associateParticipantId(acFromDb, acDefinitionTarget, acDefinition);
509         }
510         acFromDb.setCompositionTargetId(automationComposition.getCompositionTargetId());
511     }
512
513     private void associateParticipantId(AutomationComposition acFromDb,
514                                         AutomationCompositionDefinition acDefinitionTarget,
515                                         AutomationCompositionDefinition oldAcDefinition) {
516         for (var element : acFromDb.getElements().values()) {
517             var name = element.getDefinition().getName();
518             var migrationState = element.getMigrationState();
519             if (MigrationState.DEFAULT.equals(migrationState) || MigrationState.NEW.equals(migrationState)) {
520                 var participantId = acDefinitionTarget.getElementStateMap().get(name).getParticipantId();
521                 element.setParticipantId(participantId);
522             } else if (MigrationState.REMOVED.equals(migrationState) && oldAcDefinition != null) {
523                 var participantId = oldAcDefinition.getElementStateMap().get(name).getParticipantId();
524                 element.setParticipantId(participantId);
525             }
526         }
527     }
528
529     private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
530                                                             AutomationComposition automationComposition) {
531         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
532         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
533         participantProvider.checkRegisteredParticipant(acDefinition);
534         return acDefinition;
535     }
536
537     /**
538      * Retrieves a list of AutomationComposition instances filtered by the specified state change results
539      * and deployment states. The result can be paginated and sorted based on the provided parameters.
540      *
541      * @param instanceIds a list of instance UUIDs
542      * @param stateChangeResults a list of StateChangeResult values to filter the AutomationComposition instances
543      * @param deployStates a list of DeployState values to filter the AutomationComposition instances
544      * @param pageable the pagination information including page size and page number
545      * @return a list of AutomationComposition instances that match the specified filters
546      */
547     public AutomationCompositions getAcInstancesByFilter(
548         final String instanceIds, final String stateChangeResults, final String deployStates,
549         final Pageable pageable) {
550
551         LOGGER.info("Get automation compositions request received with filters");
552         List<String> acIds = new ArrayList<>();
553         if (instanceIds != null) {
554             Arrays.stream(instanceIds.split(",")).forEach(acId -> acIds.add(acId.trim()));
555         }
556
557         List<StateChangeResult> stateChangeResultList = new ArrayList<>();
558         if (stateChangeResults != null) {
559             Arrays.stream(stateChangeResults.split(","))
560                 .forEach(stateChangeResult -> stateChangeResultList.add(StateChangeResult.valueOf(stateChangeResult)));
561         }
562
563         List<DeployState> deployStateList = new ArrayList<>();
564         if (deployStates != null) {
565             Arrays.stream(deployStates.split(","))
566                 .forEach(deployState -> deployStateList.add(DeployState.valueOf(deployState)));
567         }
568
569         var instances = automationCompositionProvider.getAcInstancesByFilter(acIds, stateChangeResultList,
570             deployStateList, pageable);
571         return new AutomationCompositions(instances);
572     }
573 }