389f555d0402d50ee8e673d5a29e48b18b65c8cf
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
4  * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.clamp.acm.runtime.instantiation;
23
24 import jakarta.validation.Valid;
25 import jakarta.ws.rs.core.Response.Status;
26 import java.util.List;
27 import java.util.UUID;
28 import java.util.stream.Collectors;
29 import lombok.NonNull;
30 import lombok.RequiredArgsConstructor;
31 import org.onap.policy.clamp.acm.runtime.main.parameters.AcRuntimeParameterGroup;
32 import org.onap.policy.clamp.acm.runtime.main.utils.EncryptionUtils;
33 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionAcHandler;
34 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionDefinition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
39 import org.onap.policy.clamp.models.acm.concepts.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
41 import org.onap.policy.clamp.models.acm.concepts.NodeTemplateState;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
44 import org.onap.policy.clamp.models.acm.concepts.SubState;
45 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AcInstanceStateUpdate;
46 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
47 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
49 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.SubOrder;
50 import org.onap.policy.clamp.models.acm.persistence.provider.AcDefinitionProvider;
51 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
52 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
53 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
54 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
55 import org.onap.policy.common.parameters.BeanValidationResult;
56 import org.onap.policy.common.parameters.ObjectValidationResult;
57 import org.onap.policy.common.parameters.ValidationStatus;
58 import org.onap.policy.models.base.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         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
97         automationCompositionProvider.validateNameVersion(automationComposition.getKey().asIdentifier());
98
99         var validationResult = validateAutomationComposition(automationComposition);
100         if (!validationResult.isValid()) {
101             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
102         }
103         encryptInstanceProperties(automationComposition, compositionId);
104         automationComposition = automationCompositionProvider.createAutomationComposition(automationComposition);
105
106         return createInstantiationResponse(automationComposition);
107     }
108
109     private InstantiationResponse createInstantiationResponse(AutomationComposition automationComposition) {
110         var response = new InstantiationResponse();
111         response.setInstanceId(automationComposition.getInstanceId());
112         response.setAffectedAutomationComposition(automationComposition.getKey().asIdentifier());
113         return response;
114     }
115
116     /**
117      * Update automation composition.
118      *
119      * @param compositionId         The UUID of the automation composition definition
120      * @param automationComposition the automation composition
121      * @return the result of the update
122      */
123     public InstantiationResponse updateAutomationComposition(UUID compositionId,
124                                                              AutomationComposition automationComposition) {
125         var instanceId = automationComposition.getInstanceId();
126         var acToUpdate = automationCompositionProvider.getAutomationComposition(instanceId);
127         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, acToUpdate);
128         if (DeployState.UNDEPLOYED.equals(acToUpdate.getDeployState())) {
129             acToUpdate.setElements(automationComposition.getElements());
130             acToUpdate.setName(automationComposition.getName());
131             acToUpdate.setVersion(automationComposition.getVersion());
132             acToUpdate.setDescription(automationComposition.getDescription());
133             acToUpdate.setDerivedFrom(automationComposition.getDerivedFrom());
134             var validationResult = validateAutomationComposition(acToUpdate);
135             if (!validationResult.isValid()) {
136                 throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
137             }
138             encryptInstanceProperties(acToUpdate, compositionId);
139             automationComposition = automationCompositionProvider.updateAutomationComposition(acToUpdate);
140             return createInstantiationResponse(automationComposition);
141
142         }
143
144         var deployOrder = DeployOrder.UPDATE;
145         var subOrder = SubOrder.NONE;
146
147         if (automationComposition.getCompositionTargetId() != null) {
148
149             if (Boolean.TRUE.equals(automationComposition.getPrecheck())) {
150                 subOrder = SubOrder.MIGRATE_PRECHECK;
151                 deployOrder = DeployOrder.NONE;
152             } else {
153                 deployOrder = DeployOrder.MIGRATE;
154             }
155         }
156         var result = acInstanceStateResolver.resolve(deployOrder, LockOrder.NONE, subOrder,
157             acToUpdate.getDeployState(), acToUpdate.getLockState(), acToUpdate.getSubState(),
158             acToUpdate.getStateChangeResult());
159         return switch (result) {
160             case "UPDATE" -> updateDeployedAutomationComposition(automationComposition, acToUpdate);
161
162             case "MIGRATE" -> migrateAutomationComposition(automationComposition, acToUpdate);
163
164             case "MIGRATE_PRECHECK" -> migratePrecheckAc(automationComposition, acToUpdate);
165
166             default -> throw new PfModelRuntimeException(Status.BAD_REQUEST,
167                 "Not allowed to " + deployOrder + " in the state " + acToUpdate.getDeployState());
168         };
169     }
170
171     /**
172      * Update deployed AC Element properties.
173      *
174      * @param automationComposition the automation composition
175      * @param acToBeUpdated         the composition to be updated
176      * @return the result of the update
177      */
178     private InstantiationResponse updateDeployedAutomationComposition(
179         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
180         // save copy in case of a rollback
181         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
182
183         // Iterate and update the element property values
184         for (var element : automationComposition.getElements().entrySet()) {
185             var elementId = element.getKey();
186             var dbAcElement = acToBeUpdated.getElements().get(elementId);
187             if (dbAcElement == null) {
188                 throw new PfModelRuntimeException(Status.BAD_REQUEST, ELEMENT_ID_NOT_PRESENT + elementId);
189             }
190             AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
191         }
192
193         var validationResult = validateAutomationComposition(acToBeUpdated);
194         if (!validationResult.isValid()) {
195             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
196         }
197         updateAcForProperties(acToBeUpdated);
198
199         var acToPublish = new AutomationComposition(acToBeUpdated);
200
201         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionId());
202
203         automationComposition = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
204         // Publish property update event to the participants
205         supervisionAcHandler.update(acToPublish);
206         return createInstantiationResponse(automationComposition);
207     }
208
209     private InstantiationResponse migrateAutomationComposition(
210         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
211
212         if (!DeployState.DEPLOYED.equals(acToBeUpdated.getDeployState())) {
213             throw new PfModelRuntimeException(Status.BAD_REQUEST,
214                 "Not allowed to migrate in the state " + acToBeUpdated.getDeployState());
215         }
216         // make copy for rollback
217         automationCompositionProvider.copyAcElementsBeforeUpdate(acToBeUpdated);
218
219         // Iterate and update the element property values
220         var elementsRemoved = updateElementsProperties(automationComposition, acToBeUpdated);
221         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionTargetId());
222
223         updateAcForMigration(acToBeUpdated, acDefinition, DeployState.MIGRATING);
224
225         var acToPublish = new AutomationComposition(acToBeUpdated);
226
227         encryptInstanceProperties(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
228
229         var ac = automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
230         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
231
232         // Publish migrate event to the participants
233         supervisionAcHandler.migrate(acToPublish);
234         return createInstantiationResponse(ac);
235     }
236
237     private void updateAcForMigration(AutomationComposition acToBeUpdated,
238                                       AutomationCompositionDefinition acDefinition, DeployState deployState) {
239         AcmUtils.setCascadedState(acToBeUpdated, deployState, LockState.LOCKED);
240         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
241         var stage = ParticipantUtils.getFirstStage(acToBeUpdated, acDefinition.getServiceTemplate());
242         acToBeUpdated.setPhase(stage);
243     }
244
245     private void updateAcForProperties(AutomationComposition acToBeUpdated) {
246         AcmUtils.setCascadedState(acToBeUpdated, DeployState.UPDATING, acToBeUpdated.getLockState());
247         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
248     }
249
250     private List<UUID> getElementRemoved(AutomationComposition acFromDb, AutomationComposition acFromMigration) {
251         return acFromDb.getElements().keySet().stream()
252             .filter(id -> acFromMigration.getElements().get(id) == null).toList();
253     }
254
255     private InstantiationResponse migratePrecheckAc(
256         AutomationComposition automationComposition, AutomationComposition acToBeUpdated) {
257
258         acToBeUpdated.setPrecheck(true);
259         var copyAc = new AutomationComposition(acToBeUpdated);
260         // Iterate and update the element property values
261         updateElementsProperties(automationComposition, copyAc);
262
263         // Publish migrate event to the participants
264         supervisionAcHandler.migratePrecheck(copyAc);
265
266         AcmUtils.setCascadedState(acToBeUpdated, DeployState.DEPLOYED, LockState.LOCKED,
267             SubState.MIGRATION_PRECHECKING);
268         acToBeUpdated.setStateChangeResult(StateChangeResult.NO_ERROR);
269
270         return createInstantiationResponse(automationCompositionProvider.updateAutomationComposition(acToBeUpdated));
271     }
272
273     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition) {
274         return validateAutomationComposition(automationComposition, automationComposition.getCompositionId());
275     }
276
277     /**
278      * Validate AutomationComposition.
279      *
280      * @param automationComposition AutomationComposition to validate
281      * @param compositionId         the composition id
282      * @return the result of validation
283      */
284     private BeanValidationResult validateAutomationComposition(AutomationComposition automationComposition,
285                                                                UUID compositionId) {
286
287         var result = new BeanValidationResult("AutomationComposition", automationComposition);
288         var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
289         if (acDefinitionOpt.isEmpty()) {
290             result.addResult(new ObjectValidationResult("ServiceTemplate", compositionId, ValidationStatus.INVALID,
291                 "Commissioned automation composition definition not found"));
292             return result;
293         }
294         if (!AcTypeState.PRIMED.equals(acDefinitionOpt.get().getState())) {
295             result.addResult(new ObjectValidationResult("ServiceTemplate.state", acDefinitionOpt.get().getState(),
296                 ValidationStatus.INVALID, "Commissioned automation composition definition not primed"));
297             return result;
298         }
299         var participantIds = acDefinitionOpt.get().getElementStateMap().values().stream()
300             .map(NodeTemplateState::getParticipantId).collect(Collectors.toSet());
301
302         participantProvider.verifyParticipantState(participantIds);
303
304         result.addResult(AcmUtils.validateAutomationComposition(automationComposition,
305             acDefinitionOpt.get().getServiceTemplate(),
306             acRuntimeParameterGroup.getAcmParameters().getToscaCompositionName()));
307
308         result.addResult(automationCompositionProvider.validateElementIds(automationComposition));
309
310         if (result.isValid()) {
311             for (var element : automationComposition.getElements().values()) {
312                 var name = element.getDefinition().getName();
313                 var participantId = acDefinitionOpt.get().getElementStateMap().get(name).getParticipantId();
314                 element.setParticipantId(participantId);
315             }
316         }
317
318         return result;
319     }
320
321
322     private void encryptInstanceProperties(AutomationComposition automationComposition, UUID compositionId) {
323         if (encryptionUtils.encryptionEnabled()) {
324             var acDefinitionOpt = acDefinitionProvider.findAcDefinition(compositionId);
325             acDefinitionOpt.ifPresent(acDefinition
326                 -> encryptionUtils.findAndEncryptSensitiveData(acDefinition, automationComposition));
327         }
328     }
329
330     /**
331      * Get Automation Composition.
332      *
333      * @param compositionId The UUID of the automation composition definition
334      * @param instanceId    The UUID of the automation composition instance
335      * @return the Automation Composition
336      */
337     @Transactional(readOnly = true)
338     public AutomationComposition getAutomationComposition(@NonNull UUID compositionId, UUID instanceId) {
339         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
340         if (!compositionId.equals(automationComposition.getCompositionId())
341             && !compositionId.equals(automationComposition.getCompositionTargetId())) {
342             throw new PfModelRuntimeException(Status.BAD_REQUEST,
343                 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
344         }
345         return automationComposition;
346     }
347
348     /**
349      * Delete the automation composition with the given name and version.
350      *
351      * @param compositionId The UUID of the automation composition definition
352      * @param instanceId    The UUID of the automation composition instance
353      * @return the result of the deletion
354      */
355     public InstantiationResponse deleteAutomationComposition(UUID compositionId, UUID instanceId) {
356         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
357         var acDefinition = getAcDefinition(compositionId, automationComposition);
358         var result = acInstanceStateResolver.resolve(DeployOrder.DELETE,
359             null, null,
360             automationComposition.getDeployState(), automationComposition.getLockState(),
361             automationComposition.getSubState(), automationComposition.getStateChangeResult());
362         if (!DeployOrder.DELETE.name().equals(result)) {
363             var msg = String.format(NOT_VALID_ORDER, DeployOrder.DELETE,
364                 automationComposition.getDeployState(), automationComposition.getLockState(),
365                 automationComposition.getSubState(), automationComposition.getStateChangeResult());
366             throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
367         }
368         supervisionAcHandler.delete(automationComposition, acDefinition);
369         return createInstantiationResponse(automationComposition);
370     }
371
372     /**
373      * Get the requested automation compositions.
374      *
375      * @param name     the name of the automation composition to get, null for all automation compositions
376      * @param version  the version of the automation composition to get, null for all automation compositions
377      * @param pageable the Pageable
378      * @return the automation compositions
379      */
380     @Transactional(readOnly = true)
381     public AutomationCompositions getAutomationCompositions(@NonNull final UUID compositionId,
382                                                             final String name, final String version,
383                                                             @NonNull final Pageable pageable) {
384         var automationCompositions = new AutomationCompositions();
385         automationCompositions.setAutomationCompositionList(
386             automationCompositionProvider.getAutomationCompositions(compositionId, name, version, pageable));
387
388         return automationCompositions;
389     }
390
391     /**
392      * Handle Composition Instance State.
393      *
394      * @param compositionId         the compositionId
395      * @param instanceId            the instanceId
396      * @param acInstanceStateUpdate the AcInstanceStateUpdate
397      */
398     public void compositionInstanceState(UUID compositionId, UUID instanceId,
399                                          @Valid AcInstanceStateUpdate acInstanceStateUpdate) {
400         var automationComposition = automationCompositionProvider.getAutomationComposition(instanceId);
401         var acDefinition = getAcDefinition(compositionId, automationComposition);
402         var result = acInstanceStateResolver.resolve(acInstanceStateUpdate.getDeployOrder(),
403             acInstanceStateUpdate.getLockOrder(), acInstanceStateUpdate.getSubOrder(),
404             automationComposition.getDeployState(), automationComposition.getLockState(),
405             automationComposition.getSubState(), automationComposition.getStateChangeResult());
406         switch (result) {
407             case "DEPLOY":
408                 supervisionAcHandler.deploy(automationComposition, acDefinition);
409                 break;
410
411             case "UNDEPLOY":
412                 supervisionAcHandler.undeploy(automationComposition, acDefinition);
413                 break;
414
415             case "LOCK":
416                 supervisionAcHandler.lock(automationComposition, acDefinition);
417                 break;
418
419             case "UNLOCK":
420                 supervisionAcHandler.unlock(automationComposition, acDefinition);
421                 break;
422
423             case "PREPARE":
424                 supervisionAcHandler.prepare(automationComposition, acDefinition);
425                 break;
426
427             case "REVIEW":
428                 supervisionAcHandler.review(automationComposition);
429                 break;
430
431             default:
432                 var msg = String.format(NOT_VALID_ORDER, acInstanceStateUpdate,
433                     automationComposition.getDeployState(), automationComposition.getLockState(),
434                     automationComposition.getSubState(), automationComposition.getStateChangeResult());
435                 throw new PfModelRuntimeException(Status.BAD_REQUEST, msg);
436         }
437     }
438
439     /**
440      * Rollback AC Instance.
441      *
442      * @param compositionId The UUID of the automation composition definition
443      * @param instanceId    The UUID of the automation composition instance
444      */
445     public void rollback(UUID compositionId, UUID 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 acDefinition = acDefinitionProvider.getAcDefinition(acToBeUpdated.getCompositionTargetId());
464         var validationResult =
465                 validateAutomationComposition(acToBeUpdated, acToBeUpdated.getCompositionTargetId());
466         if (!validationResult.isValid()) {
467             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
468         }
469
470         updateAcForMigration(acToBeUpdated, acDefinition, DeployState.MIGRATION_REVERTING);
471         var elementsRemoved = getElementRemoved(automationComposition, acToBeUpdated);
472         automationCompositionProvider.updateAutomationComposition(acToBeUpdated);
473         elementsRemoved.forEach(automationCompositionProvider::deleteAutomationCompositionElement);
474         supervisionAcHandler.migrate(acToBeUpdated);
475     }
476
477     private List<UUID> updateElementsProperties(AutomationComposition automationComposition,
478                                                 AutomationComposition acToBeUpdated) {
479         for (var element : automationComposition.getElements().entrySet()) {
480             var elementId = element.getKey();
481             var dbAcElement = acToBeUpdated.getElements().get(elementId);
482             // Add additional elements if present for migration
483             if (dbAcElement == null) {
484                 LOGGER.info("New Ac element {} added in Migration", elementId);
485                 acToBeUpdated.getElements().put(elementId, element.getValue());
486             } else {
487                 AcmUtils.recursiveMerge(dbAcElement.getProperties(), element.getValue().getProperties());
488                 var newDefinition = element.getValue().getDefinition().asConceptKey();
489                 var dbElementDefinition = dbAcElement.getDefinition().asConceptKey();
490                 AutomationCompositionProvider
491                         .checkCompatibility(newDefinition, dbElementDefinition, automationComposition.getInstanceId());
492                 dbAcElement.setDefinition(element.getValue().getDefinition());
493             }
494         }
495         // Remove element which is not present in the new Ac instance
496         var elementsRemoved = getElementRemoved(acToBeUpdated, automationComposition);
497         elementsRemoved.forEach(uuid -> acToBeUpdated.getElements().remove(uuid));
498
499         var validationResult =
500             validateAutomationComposition(acToBeUpdated, automationComposition.getCompositionTargetId());
501         if (!validationResult.isValid()) {
502             throw new PfModelRuntimeException(Status.BAD_REQUEST, validationResult.getResult());
503         }
504         acToBeUpdated.setCompositionTargetId(automationComposition.getCompositionTargetId());
505         return elementsRemoved;
506     }
507
508     private AutomationCompositionDefinition getAcDefinition(UUID compositionId,
509                                                             AutomationComposition automationComposition) {
510         AutomationCompositionProvider.validateInstanceEndpoint(compositionId, automationComposition);
511         var acDefinition = acDefinitionProvider.getAcDefinition(automationComposition.getCompositionId());
512         participantProvider.checkRegisteredParticipant(acDefinition);
513         return acDefinition;
514     }
515 }