2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved.
4 * ================================================================================
5 * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.clamp.models.acm.persistence.provider;
25 import jakarta.ws.rs.core.Response;
26 import jakarta.ws.rs.core.Response.Status;
27 import java.util.List;
28 import java.util.Optional;
30 import java.util.UUID;
31 import java.util.stream.Collectors;
32 import lombok.AllArgsConstructor;
33 import lombok.NonNull;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionRollback;
37 import org.onap.policy.clamp.models.acm.concepts.DeployState;
38 import org.onap.policy.clamp.models.acm.concepts.LockState;
39 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
40 import org.onap.policy.clamp.models.acm.concepts.SubState;
41 import org.onap.policy.clamp.models.acm.persistence.concepts.JpaAutomationComposition;
42 import org.onap.policy.clamp.models.acm.persistence.concepts.JpaAutomationCompositionRollback;
43 import org.onap.policy.clamp.models.acm.persistence.repository.AutomationCompositionElementRepository;
44 import org.onap.policy.clamp.models.acm.persistence.repository.AutomationCompositionRepository;
45 import org.onap.policy.clamp.models.acm.persistence.repository.AutomationCompositionRollbackRepository;
46 import org.onap.policy.clamp.models.acm.utils.AcmUtils;
47 import org.onap.policy.common.parameters.BeanValidationResult;
48 import org.onap.policy.common.parameters.ValidationStatus;
49 import org.onap.policy.models.base.PfConceptKey;
50 import org.onap.policy.models.base.PfKey;
51 import org.onap.policy.models.base.PfModelRuntimeException;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.data.domain.Example;
56 import org.springframework.data.domain.Page;
57 import org.springframework.data.domain.Pageable;
58 import org.springframework.stereotype.Service;
59 import org.springframework.transaction.annotation.Isolation;
60 import org.springframework.transaction.annotation.Transactional;
63 * This class provides information on automation composition concepts in the database to callers.
68 public class AutomationCompositionProvider {
69 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionProvider.class);
70 private static final String DO_NOT_MATCH = " do not match with ";
72 private final AutomationCompositionRepository automationCompositionRepository;
73 private final AutomationCompositionElementRepository acElementRepository;
74 private final AutomationCompositionRollbackRepository acRollbackRepository;
77 * Get automation composition.
79 * @param instanceId the ID of the automation composition to get
80 * @return the automation composition found
82 @Transactional(readOnly = true)
83 public AutomationComposition getAutomationComposition(final UUID instanceId) {
84 var result = automationCompositionRepository.findById(instanceId.toString());
85 if (result.isEmpty()) {
86 throw new PfModelRuntimeException(Status.NOT_FOUND, "AutomationComposition not found");
88 return result.get().toAuthorative();
92 * Find automation composition.
94 * @param instanceId the ID of the automation composition to get
95 * @return the automation composition found
97 @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
98 public Optional<AutomationComposition> findAutomationComposition(final UUID instanceId) {
99 var result = automationCompositionRepository.findById(instanceId.toString());
100 return result.stream().map(JpaAutomationComposition::toAuthorative).findFirst();
105 * Create automation composition.
107 * @param automationComposition the automation composition to create
108 * @return the created automation composition
110 public AutomationComposition createAutomationComposition(final AutomationComposition automationComposition) {
111 automationComposition.setInstanceId(UUID.randomUUID());
112 AcmUtils.setCascadedState(automationComposition, DeployState.UNDEPLOYED, LockState.NONE);
113 var result = automationCompositionRepository.save(ProviderUtils.getJpaAndValidate(automationComposition,
114 JpaAutomationComposition::new, "automation composition"));
116 // Return the saved automation composition
117 return result.toAuthorative();
121 * Update automation composition.
123 * @param automationComposition the automation composition to update
124 * @return the updated automation composition
126 public AutomationComposition updateAutomationComposition(
127 @NonNull final AutomationComposition automationComposition) {
128 var result = automationCompositionRepository.save(ProviderUtils.getJpaAndValidate(automationComposition,
129 JpaAutomationComposition::new, "automation composition"));
130 automationCompositionRepository.flush();
131 // Return the saved automation composition
132 return result.toAuthorative();
136 * Get all automation compositions by compositionId.
138 * @param compositionId the compositionId of the automation composition definition
139 * @return all automation compositions found
141 @Transactional(readOnly = true)
142 public List<AutomationComposition> getAcInstancesByCompositionId(UUID compositionId) {
144 .asEntityList(automationCompositionRepository.findByCompositionId(compositionId.toString()));
148 * Get all automation compositions by targetCompositionId.
150 * @param targetCompositionId the target composition ID of the AC definition
151 * @return all automation compositions found
153 @Transactional(readOnly = true)
154 public List<AutomationComposition> getAcInstancesByTargetCompositionId(UUID targetCompositionId) {
155 return ProviderUtils.asEntityList(automationCompositionRepository
156 .findByCompositionTargetId(targetCompositionId.toString()));
160 * Get all automation compositions in transition.
162 * @return all automation compositions found
164 @Transactional(readOnly = true)
165 public Set<UUID> getAcInstancesInTransition() {
166 var jpaList = automationCompositionRepository.findByDeployStateIn(List.of(DeployState.DEPLOYING,
167 DeployState.UNDEPLOYING, DeployState.DELETING, DeployState.UPDATING, DeployState.MIGRATING,
168 DeployState.MIGRATION_REVERTING));
169 jpaList.addAll(automationCompositionRepository.findByLockStateIn(
170 List.of(LockState.LOCKING, LockState.UNLOCKING)));
171 jpaList.addAll(automationCompositionRepository.findBySubStateIn(
172 List.of(SubState.PREPARING, SubState.MIGRATION_PRECHECKING, SubState.REVIEWING)));
173 return jpaList.stream().map(JpaAutomationComposition::getInstanceId)
174 .map(UUID::fromString).collect(Collectors.toSet());
178 * Get automation compositions.
180 * @param name the name of the automation composition to get, null to get all automation compositions
181 * @param version the version of the automation composition to get, null to get all automation compositions
182 * @param pageable the Pageable
183 * @return the automation compositions found
185 @Transactional(readOnly = true)
186 public List<AutomationComposition> getAutomationCompositions(@NonNull final UUID compositionId, final String name,
187 final String version,
188 @NonNull final Pageable pageable) {
189 return ProviderUtils.asEntityList(automationCompositionRepository
190 .findAll(createExample(compositionId, name, version), pageable).toList());
193 private Example<JpaAutomationComposition> createExample(final UUID compositionId, final String name,
194 final String version) {
195 var example = new JpaAutomationComposition();
196 example.setCompositionId(compositionId != null ? compositionId.toString() : null);
197 example.setName(name);
198 example.setVersion(version);
199 example.setInstanceId(null);
200 example.setElements(null);
201 example.setDeployState(null);
202 example.setLockState(null);
204 return Example.of(example);
208 * Delete a automation composition.
210 * @param instanceId the ID of the automation composition to get
211 * @return the automation composition deleted
213 public AutomationComposition deleteAutomationComposition(@NonNull final UUID instanceId) {
214 var jpaDeleteAutomationComposition = automationCompositionRepository.findById(instanceId.toString());
215 if (jpaDeleteAutomationComposition.isEmpty()) {
216 var errorMessage = "delete of automation composition \"" + instanceId
217 + "\" failed, automation composition does not exist";
218 throw new PfModelRuntimeException(Response.Status.NOT_FOUND, errorMessage);
221 if (acRollbackRepository.existsById(instanceId.toString())) {
222 acRollbackRepository.deleteById(instanceId.toString());
224 automationCompositionRepository.deleteById(instanceId.toString());
226 return jpaDeleteAutomationComposition.get().toAuthorative();
230 * Delete AutomationCompositionElement.
232 * @param elementId the AutomationCompositionElement Id
234 public void deleteAutomationCompositionElement(@NonNull final UUID elementId) {
235 acElementRepository.deleteById(elementId.toString());
239 * Validate ElementIds.
241 * @param automationComposition the AutomationComposition
242 * @return the BeanValidationResult
244 public BeanValidationResult validateElementIds(final AutomationComposition automationComposition) {
245 var result = new BeanValidationResult(
246 "UUID elements " + automationComposition.getName(), automationComposition);
248 var ids = automationComposition
249 .getElements().values().stream().map(AutomationCompositionElement::getId).toList();
250 var elements = acElementRepository.findAllById(ids.stream().map(UUID::toString).toList());
251 if (automationComposition.getInstanceId() == null) {
252 for (var element : elements) {
254 element.getDescription(), element.getElementId(), ValidationStatus.INVALID, "UUID already used");
257 var instanceId = automationComposition.getInstanceId().toString();
258 for (var element : elements) {
259 if (!instanceId.equals(element.getInstanceId())) {
261 element.getDescription(), element.getElementId(), ValidationStatus.INVALID,
262 "UUID already used");
270 * Save a copy of an automation composition to the copy table in case of a rollback.
272 * @param automationComposition the composition to be copied
274 public void copyAcElementsBeforeUpdate(AutomationComposition automationComposition) {
275 var copy = new AutomationCompositionRollback(automationComposition);
276 var jpaCopy = new JpaAutomationCompositionRollback(copy);
277 acRollbackRepository.save(jpaCopy);
278 acRollbackRepository.flush();
282 * Get the copied automation composition from the RollbackRepository.
284 * @param instanceId the id of the ac instance
285 * @return the acRollback object
287 public AutomationCompositionRollback getAutomationCompositionRollback(UUID instanceId) {
288 var result = acRollbackRepository.findById(instanceId.toString());
289 if (result.isEmpty()) {
290 throw new PfModelRuntimeException(Status.NOT_FOUND, "Instance not found for rollback");
292 return result.get().toAuthorative();
296 * validate that compositionId into the Instance Endpoint is correct.
298 * @param compositionId the compositionId
299 * @param automationComposition the AutomationComposition
301 public static void validateInstanceEndpoint(UUID compositionId,
302 AutomationComposition automationComposition) {
304 if (!compositionId.equals(automationComposition.getCompositionId())) {
305 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
306 automationComposition.getCompositionId() + DO_NOT_MATCH + compositionId);
311 * Validate that name and version of an AutomationComposition is not already used.
313 * @param acIdentifier the name and version of an AutomationComposition
315 @Transactional(readOnly = true)
316 public void validateNameVersion(ToscaConceptIdentifier acIdentifier) {
317 var acOpt = automationCompositionRepository
318 .findOne(createExample(null, acIdentifier.getName(), acIdentifier.getVersion()))
319 .map(JpaAutomationComposition::toAuthorative);
320 if (acOpt.isPresent()) {
321 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST, acIdentifier + " already defined");
326 * Check Compatibility of name version between elements.
328 * @param newDefinition the new name version
329 * @param dbElementDefinition the name version from db
330 * @param instanceId the instanceId
332 public static void checkCompatibility(PfConceptKey newDefinition, PfConceptKey dbElementDefinition,
334 var compatibility = newDefinition.getCompatibility(dbElementDefinition);
335 if (PfKey.Compatibility.DIFFERENT.equals(compatibility)) {
336 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
337 dbElementDefinition + " is not compatible with " + newDefinition);
339 if (PfKey.Compatibility.MAJOR.equals(compatibility) || PfKey.Compatibility.MINOR
340 .equals(compatibility)) {
341 LOGGER.warn("Migrate {}: Version {} has {} compatibility with {} ", instanceId, newDefinition,
342 compatibility, dbElementDefinition);
347 * Retrieves a list of AutomationComposition instances filtered by the specified state change results
348 * and deployment states. The result can be paginated and sorted based on the provided parameters.
350 * @param instanceIds a list of instance UUIDs
351 * @param stateChangeResults a list of StateChangeResult values to filter the AutomationComposition instances
352 * @param deployStates a list of DeployState values to filter the AutomationComposition instances
353 * @param pageable the pagination information including page size and page number
354 * @return a list of AutomationComposition instances that match the specified filters
356 public List<AutomationComposition> getAcInstancesByFilter(
357 @NonNull final List<String> instanceIds,
358 @NonNull final List<StateChangeResult> stateChangeResults,
359 @NonNull final List<DeployState> deployStates,
360 @NonNull final Pageable pageable) {
361 Page<JpaAutomationComposition> page;
363 if (!instanceIds.isEmpty()) {
364 page = filterWithInstanceIds(instanceIds, stateChangeResults, deployStates, pageable);
365 } else if (stateChangeResults.isEmpty() && deployStates.isEmpty()) {
366 page = automationCompositionRepository.findAll(pageable);
367 } else if (!stateChangeResults.isEmpty() && deployStates.isEmpty()) {
368 page = automationCompositionRepository.findByStateChangeResultIn(stateChangeResults, pageable);
369 } else if (stateChangeResults.isEmpty()) {
370 page = automationCompositionRepository.findByDeployStateIn(deployStates, pageable);
372 page = automationCompositionRepository.findByStateChangeResultInAndDeployStateIn(
373 stateChangeResults, deployStates, pageable);
376 return ProviderUtils.asEntityList(page.toList());
379 private Page<JpaAutomationComposition> filterWithInstanceIds(final List<String> instanceIds,
380 final List<StateChangeResult> stages,
381 final List<DeployState> deployStates,
382 final Pageable pageable) {
383 if (stages.isEmpty() && deployStates.isEmpty()) {
384 return automationCompositionRepository.findByInstanceIdIn(instanceIds, pageable);
385 } else if (!stages.isEmpty() && deployStates.isEmpty()) {
386 return automationCompositionRepository.findByInstanceIdInAndStateChangeResultIn(
387 instanceIds, stages, pageable);
388 } else if (stages.isEmpty()) {
389 return automationCompositionRepository.findByInstanceIdInAndDeployStateIn(
390 instanceIds, deployStates, pageable);
392 return automationCompositionRepository.findByInstanceIdInAndStateChangeResultInAndDeployStateIn(
393 instanceIds, stages, deployStates, pageable);