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