98b59aed7a205014da960d620164a3a942382812
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2021 Nordix Foundation.
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 com.google.gson.Gson;
25 import com.google.gson.internal.LinkedTreeMap;
26 import com.google.gson.reflect.TypeToken;
27 import java.lang.reflect.Type;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.UUID;
34 import java.util.function.Function;
35 import java.util.function.UnaryOperator;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.Response.Status;
40 import lombok.AllArgsConstructor;
41 import org.onap.policy.clamp.acm.runtime.commissioning.CommissioningProvider;
42 import org.onap.policy.clamp.acm.runtime.supervision.SupervisionHandler;
43 import org.onap.policy.clamp.common.acm.exception.AutomationCompositionException;
44 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
45 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
46 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
47 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
48 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
49 import org.onap.policy.clamp.models.acm.concepts.Participant;
50 import org.onap.policy.clamp.models.acm.messages.rest.GenericNameVersion;
51 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AutomationCompositionOrderStateResponse;
52 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AutomationCompositionPrimed;
53 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.AutomationCompositionPrimedResponse;
54 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstancePropertiesResponse;
55 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationCommand;
56 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.InstantiationResponse;
57 import org.onap.policy.clamp.models.acm.persistence.provider.AutomationCompositionProvider;
58 import org.onap.policy.clamp.models.acm.persistence.provider.ParticipantProvider;
59 import org.onap.policy.common.parameters.BeanValidationResult;
60 import org.onap.policy.common.parameters.ObjectValidationResult;
61 import org.onap.policy.common.parameters.ValidationResult;
62 import org.onap.policy.common.parameters.ValidationStatus;
63 import org.onap.policy.models.base.PfModelException;
64 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
65 import org.onap.policy.models.tosca.authorative.concepts.ToscaNameVersion;
66 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
67 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
68 import org.springframework.stereotype.Service;
69 import org.springframework.transaction.annotation.Transactional;
70
71 /**
72  * This class is dedicated to the Instantiation of Commissioned automation composition.
73  */
74 @Service
75 @Transactional
76 @AllArgsConstructor
77 public class AutomationCompositionInstantiationProvider {
78     private static final String AUTOMATION_COMPOSITION_NODE_TYPE = "org.onap.policy.clamp.acm.AutomationComposition";
79     private static final String AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE = "AutomationCompositionElement";
80     private static final String PARTICIPANT_ID_PROPERTY_KEY = "participant_id";
81     private static final String PARTICIPANT_TYPE_PROPERTY_KEY = "participantType";
82     private static final String AC_ELEMENT_NAME = "name";
83     private static final String AC_ELEMENT_VERSION = "version";
84     private static final String HYPHEN = "-";
85
86     private static final Gson GSON = new Gson();
87
88     private final AutomationCompositionProvider automationCompositionProvider;
89     private final CommissioningProvider commissioningProvider;
90     private final SupervisionHandler supervisionHandler;
91     private final ParticipantProvider participantProvider;
92     private static final String ENTRY = "entry ";
93
94     /**
95      * Creates Instance Properties and automation composition.
96      *
97      * @param serviceTemplate the service template
98      * @return the result of the instantiation operation
99      * @throws PfModelException on creation errors
100      */
101     public InstancePropertiesResponse createInstanceProperties(ToscaServiceTemplate serviceTemplate)
102         throws PfModelException {
103
104         String instanceName = serviceTemplate.getName();
105         AutomationComposition automationComposition = new AutomationComposition();
106         Map<UUID, AutomationCompositionElement> automationCompositionElements = new HashMap<>();
107
108         ToscaServiceTemplate toscaServiceTemplate = commissioningProvider.getAllToscaServiceTemplate().get(0);
109
110         Map<String, ToscaNodeTemplate> persistedNodeTemplateMap =
111             toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates();
112
113         Map<String, ToscaNodeTemplate> nodeTemplates = deepCloneNodeTemplate(serviceTemplate);
114
115         nodeTemplates.forEach((key, template) -> {
116             ToscaNodeTemplate newNodeTemplate = new ToscaNodeTemplate();
117             String name = key + "-" + instanceName;
118             String version = template.getVersion();
119             String description = template.getDescription() + " " + instanceName;
120             newNodeTemplate.setName(name);
121             newNodeTemplate.setVersion(version);
122             newNodeTemplate.setDescription(description);
123             newNodeTemplate.setProperties(new HashMap<>(template.getProperties()));
124             newNodeTemplate.setType(template.getType());
125             newNodeTemplate.setTypeVersion(template.getTypeVersion());
126             newNodeTemplate.setMetadata(template.getMetadata());
127
128             crateNewAutomationCompositionInstance(instanceName, automationComposition, automationCompositionElements,
129                 template, newNodeTemplate);
130
131             persistedNodeTemplateMap.put(name, newNodeTemplate);
132         });
133
134         AutomationCompositions automationCompositions = new AutomationCompositions();
135
136         serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().putAll(persistedNodeTemplateMap);
137
138         automationComposition.setElements(automationCompositionElements);
139         automationCompositions.getAutomationCompositionList().add(automationComposition);
140
141         return saveInstancePropertiesAndAutomationComposition(serviceTemplate, automationCompositions);
142     }
143
144     /**
145      * Updates Instance Properties and Automation Composition Instance.
146      *
147      * @param name the name of the automation composition to update
148      * @param version the version of the automation composition to update
149      * @param serviceTemplate tosca service template body
150      * @return InstancePropertiesResponse response from updating instance properties
151      * @throws PfModelException exception if incorrect instance name
152      */
153     public InstancePropertiesResponse updatesInstanceProperties(
154             String name, String version, ToscaServiceTemplate serviceTemplate) throws PfModelException {
155
156         if (name.length() < 3) {
157             throw new PfModelException(Status.BAD_REQUEST, "Instance Name cannot be empty or less than 3 characters!");
158         }
159
160         Map<String, ToscaNodeTemplate> nodeTemplates = deepCloneNodeTemplate(serviceTemplate);
161         Map<String, ToscaNodeTemplate> updatedNodeTemplates = new HashMap<>();
162
163         String instanceName = serviceTemplate.getName();
164
165         nodeTemplates.forEach((key, template) -> {
166             ToscaNodeTemplate toscaNodeTemplate = new ToscaNodeTemplate();
167
168             String updatedName = updateInstanceNameDescription(instanceName, name, key);
169             String updatedDescription = updateInstanceNameDescription(
170                     instanceName, name, template.getDescription());
171
172             toscaNodeTemplate.setName(updatedName);
173             toscaNodeTemplate.setDescription(updatedDescription);
174             toscaNodeTemplate.setCapabilities(template.getCapabilities());
175             toscaNodeTemplate.setRequirements(template.getRequirements());
176             toscaNodeTemplate.setMetadata(template.getMetadata());
177             toscaNodeTemplate.setProperties(template.getProperties());
178             toscaNodeTemplate.setDerivedFrom(template.getDerivedFrom());
179             toscaNodeTemplate.setVersion(template.getVersion());
180             toscaNodeTemplate.setType(template.getType());
181             toscaNodeTemplate.setTypeVersion(template.getTypeVersion());
182
183             String updatedKey = updateInstanceNameDescription(instanceName, name, key);
184
185             updatedNodeTemplates.put(updatedKey, toscaNodeTemplate);
186         });
187
188         serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().clear();
189         serviceTemplate.getToscaTopologyTemplate().getNodeTemplates().putAll(updatedNodeTemplates);
190
191         AutomationCompositions automationCompositions = updateAutomationComposition(
192                 name, instanceName);
193
194         deleteInstanceProperties(name, version);
195
196         return saveInstancePropertiesAndAutomationComposition(serviceTemplate, automationCompositions);
197     }
198
199     /**
200      * Deletes Instance Properties.
201      *
202      * @param name the name of the automation composition to delete
203      * @param version the version of the automation composition to delete
204      * @return the result of the deletion
205      * @throws PfModelException on deletion errors
206      */
207     public InstantiationResponse deleteInstanceProperties(String name, String version) throws PfModelException {
208
209         String instanceName = getInstancePropertyName(name, version);
210
211         Map<String, ToscaNodeTemplate> filteredToscaNodeTemplateMap = new HashMap<>();
212
213         ToscaServiceTemplate toscaServiceTemplate = commissioningProvider.getAllToscaServiceTemplate().get(0);
214
215         toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates().forEach((key, nodeTemplate) -> {
216             if (!nodeTemplate.getName().contains(instanceName)) {
217                 filteredToscaNodeTemplateMap.put(key, nodeTemplate);
218             }
219         });
220
221         List<ToscaNodeTemplate> filteredToscaNodeTemplateList =
222             toscaServiceTemplate.getToscaTopologyTemplate().getNodeTemplates().values().stream()
223                 .filter(nodeTemplate -> nodeTemplate.getName().contains(instanceName)).collect(Collectors.toList());
224
225         InstantiationResponse response = this.deleteAutomationComposition(name, version);
226
227         automationCompositionProvider.deleteInstanceProperties(filteredToscaNodeTemplateMap,
228             filteredToscaNodeTemplateList);
229
230         return response;
231     }
232
233     /**
234      * Create automation compositions.
235      *
236      * @param automationCompositions the automation composition
237      * @return the result of the instantiation operation
238      * @throws PfModelException on creation errors
239      */
240     public InstantiationResponse createAutomationCompositions(AutomationCompositions automationCompositions)
241         throws PfModelException {
242         for (AutomationComposition automationComposition : automationCompositions.getAutomationCompositionList()) {
243             var checkAutomationCompositionOpt =
244                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
245             if (checkAutomationCompositionOpt.isPresent()) {
246                 throw new PfModelException(Response.Status.BAD_REQUEST,
247                     automationComposition.getKey().asIdentifier() + " already defined");
248             }
249         }
250         BeanValidationResult validationResult = validateAutomationCompositions(automationCompositions);
251         if (!validationResult.isValid()) {
252             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
253         }
254         automationCompositionProvider.saveAutomationCompositions(automationCompositions.getAutomationCompositionList());
255
256         var response = new InstantiationResponse();
257         response.setAffectedAutomationCompositions(automationCompositions.getAutomationCompositionList().stream()
258             .map(ac -> ac.getKey().asIdentifier()).collect(Collectors.toList()));
259
260         return response;
261     }
262
263     /**
264      * Update automation compositions.
265      *
266      * @param automationCompositions the automation composition
267      * @return the result of the instantiation operation
268      * @throws PfModelException on update errors
269      */
270     public InstantiationResponse updateAutomationCompositions(AutomationCompositions automationCompositions)
271         throws PfModelException {
272         BeanValidationResult validationResult = validateAutomationCompositions(automationCompositions);
273         if (!validationResult.isValid()) {
274             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
275         }
276         automationCompositionProvider.saveAutomationCompositions(automationCompositions.getAutomationCompositionList());
277
278         var response = new InstantiationResponse();
279         response.setAffectedAutomationCompositions(automationCompositions.getAutomationCompositionList().stream()
280             .map(ac -> ac.getKey().asIdentifier()).collect(Collectors.toList()));
281
282         return response;
283     }
284
285     /**
286      * Validate AutomationCompositions.
287      *
288      * @param automationCompositions AutomationCompositions to validate
289      * @return the result of validation
290      * @throws PfModelException if automationCompositions is not valid
291      */
292     private BeanValidationResult validateAutomationCompositions(AutomationCompositions automationCompositions)
293         throws PfModelException {
294
295         var result = new BeanValidationResult("AutomationCompositions", automationCompositions);
296
297         for (AutomationComposition automationComposition : automationCompositions.getAutomationCompositionList()) {
298             var subResult = new BeanValidationResult(ENTRY + automationComposition.getDefinition().getName(),
299                 automationComposition);
300
301             List<ToscaNodeTemplate> toscaNodeTemplates = commissioningProvider.getAutomationCompositionDefinitions(
302                 automationComposition.getDefinition().getName(), automationComposition.getDefinition().getVersion());
303
304             if (toscaNodeTemplates.isEmpty()) {
305                 subResult.addResult(
306                     new ObjectValidationResult("AutomationComposition", automationComposition.getDefinition().getName(),
307                         ValidationStatus.INVALID, "Commissioned automation composition definition not found"));
308             } else if (toscaNodeTemplates.size() > 1) {
309                 subResult.addResult(
310                     new ObjectValidationResult("AutomationComposition", automationComposition.getDefinition().getName(),
311                         ValidationStatus.INVALID, "Commissioned automation composition definition not valid"));
312             } else {
313
314                 List<ToscaNodeTemplate> acElementDefinitions =
315                     commissioningProvider.getAutomationCompositionElementDefinitions(toscaNodeTemplates.get(0));
316
317                 // @formatter:off
318                 Map<String, ToscaConceptIdentifier> definitions = acElementDefinitions
319                         .stream()
320                         .map(nodeTemplate -> nodeTemplate.getKey().asIdentifier())
321                         .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity()));
322                 // @formatter:on
323
324                 for (AutomationCompositionElement element : automationComposition.getElements().values()) {
325                     subResult.addResult(validateDefinition(definitions, element.getDefinition()));
326                 }
327             }
328             result.addResult(subResult);
329         }
330         return result;
331     }
332
333     /**
334      * Validate ToscaConceptIdentifier, checking if exist in ToscaConceptIdentifiers map.
335      *
336      * @param definitions map of all ToscaConceptIdentifiers
337      * @param definition ToscaConceptIdentifier to validate
338      * @return the validation result
339      */
340     private ValidationResult validateDefinition(Map<String, ToscaConceptIdentifier> definitions,
341         ToscaConceptIdentifier definition) {
342         var result = new BeanValidationResult(ENTRY + definition.getName(), definition);
343         ToscaConceptIdentifier identifier = definitions.get(definition.getName());
344         if (identifier == null) {
345             result.setResult(ValidationStatus.INVALID, "Not found");
346         } else if (!identifier.equals(definition)) {
347             result.setResult(ValidationStatus.INVALID, "Version not matching");
348         }
349         return (result.isClean() ? null : result);
350     }
351
352     /**
353      * Delete the automation composition with the given name and version.
354      *
355      * @param name the name of the automation composition to delete
356      * @param version the version of the automation composition to delete
357      * @return the result of the deletion
358      * @throws PfModelException on deletion errors
359      */
360     public InstantiationResponse deleteAutomationComposition(String name, String version) throws PfModelException {
361         var automationCompositionOpt = automationCompositionProvider.findAutomationComposition(name, version);
362         if (automationCompositionOpt.isEmpty()) {
363             throw new PfModelException(Response.Status.NOT_FOUND, "Automation composition not found");
364         }
365         var automationComposition = automationCompositionOpt.get();
366         if (!AutomationCompositionState.UNINITIALISED.equals(automationComposition.getState())) {
367             throw new PfModelException(Response.Status.BAD_REQUEST,
368                 "Automation composition state is still " + automationComposition.getState());
369         }
370         var response = new InstantiationResponse();
371         response.setAffectedAutomationCompositions(
372             List.of(automationCompositionProvider.deleteAutomationComposition(name, version).getKey().asIdentifier()));
373         return response;
374     }
375
376     /**
377      * Get the requested automation compositions.
378      *
379      * @param name the name of the automation composition to get, null for all automation compositions
380      * @param version the version of the automation composition to get, null for all automation compositions
381      * @return the automation compositions
382      * @throws PfModelException on errors getting automation compositions
383      */
384     @Transactional(readOnly = true)
385     public AutomationCompositions getAutomationCompositions(String name, String version) throws PfModelException {
386         var automationCompositions = new AutomationCompositions();
387         automationCompositions
388             .setAutomationCompositionList(automationCompositionProvider.getAutomationCompositions(name, version));
389
390         return automationCompositions;
391     }
392
393     /**
394      * Issue a command to automation compositions, setting their ordered state.
395      *
396      * @param command the command to issue to automation compositions
397      * @return the result of the initiation command
398      * @throws PfModelException on errors setting the ordered state on the automation compositions
399      * @throws AutomationCompositionException on ordered state invalid
400      */
401     public InstantiationResponse issueAutomationCompositionCommand(InstantiationCommand command)
402         throws AutomationCompositionException, PfModelException {
403
404         if (command.getOrderedState() == null) {
405             throw new AutomationCompositionException(Status.BAD_REQUEST,
406                 "ordered state invalid or not specified on command");
407         }
408
409         var participants = participantProvider.getParticipants();
410         if (participants.isEmpty()) {
411             throw new AutomationCompositionException(Status.BAD_REQUEST, "No participants registered");
412         }
413         var validationResult = new BeanValidationResult("InstantiationCommand", command);
414         List<AutomationComposition> automationCompositions =
415             new ArrayList<>(command.getAutomationCompositionIdentifierList().size());
416         for (ToscaConceptIdentifier id : command.getAutomationCompositionIdentifierList()) {
417             var automationCompositionOpt = automationCompositionProvider.findAutomationComposition(id);
418             if (automationCompositionOpt.isEmpty()) {
419                 validationResult.addResult("ToscaConceptIdentifier", id, ValidationStatus.INVALID,
420                     "AutomationComposition with id " + id + " not found");
421             } else {
422                 var automationComposition = automationCompositionOpt.get();
423                 automationComposition.setCascadedOrderedState(command.getOrderedState());
424                 automationCompositions.add(automationComposition);
425             }
426         }
427         if (validationResult.isValid()) {
428             validationResult = validateIssueAutomationCompositions(automationCompositions, participants);
429         }
430         if (!validationResult.isValid()) {
431             throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
432         }
433         automationCompositionProvider.saveAutomationCompositions(automationCompositions);
434
435         supervisionHandler.triggerAutomationCompositionSupervision(command.getAutomationCompositionIdentifierList());
436         var response = new InstantiationResponse();
437         response.setAffectedAutomationCompositions(command.getAutomationCompositionIdentifierList());
438
439         return response;
440     }
441
442     private BeanValidationResult validateIssueAutomationCompositions(List<AutomationComposition> automationCompositions,
443         List<Participant> participants) {
444         var result = new BeanValidationResult("AutomationCompositions", automationCompositions);
445
446         Map<ToscaConceptIdentifier, Participant> participantMap = participants.stream()
447             .collect(Collectors.toMap(participant -> participant.getKey().asIdentifier(), Function.identity()));
448
449         for (AutomationComposition automationComposition : automationCompositions) {
450
451             for (var element : automationComposition.getElements().values()) {
452
453                 var subResult = new BeanValidationResult(ENTRY + element.getDefinition().getName(), element);
454                 Participant p = participantMap.get(element.getParticipantId());
455                 if (p == null) {
456                     subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
457                         element.getDefinition().getName(), ValidationStatus.INVALID,
458                         "Participant with ID " + element.getParticipantId() + " is not registered"));
459                 } else if (!p.getParticipantType().equals(element.getParticipantType())) {
460                     subResult.addResult(new ObjectValidationResult(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE,
461                         element.getDefinition().getName(), ValidationStatus.INVALID,
462                         "Participant with ID " + element.getParticipantType() + " - " + element.getParticipantId()
463                             + " is not registered"));
464                 }
465                 result.addResult(subResult);
466             }
467
468         }
469
470         return result;
471     }
472
473     /**
474      * Gets a list of automation compositions with it's ordered state.
475      *
476      * @param name the name of the automation composition to get, null for all automation compositions
477      * @param version the version of the automation composition to get, null for all automation compositions
478      * @return a list of Instantiation Command
479      * @throws PfModelException on errors getting automation compositions
480      */
481     @Transactional(readOnly = true)
482     public AutomationCompositionOrderStateResponse getInstantiationOrderState(String name, String version)
483         throws PfModelException {
484
485         List<AutomationComposition> automationCompositions =
486             automationCompositionProvider.getAutomationCompositions(name, version);
487
488         var response = new AutomationCompositionOrderStateResponse();
489
490         automationCompositions.forEach(automationComposition -> {
491             var genericNameVersion = new GenericNameVersion();
492             genericNameVersion.setName(automationComposition.getName());
493             genericNameVersion.setVersion(automationComposition.getVersion());
494             response.getAutomationCompositionIdentifierList().add(genericNameVersion);
495         });
496
497         return response;
498     }
499
500     /**
501      * Saves Instance Properties and automation composition.
502      * Gets a list of automation compositions which are primed or de-primed.
503      *
504      * @param name the name of the automation composition to get, null for all automation compositions
505      * @param version the version of the automation composition to get, null for all automation compositions
506      * @return a list of Instantiation Command
507      * @throws PfModelException on errors getting automation compositions
508      */
509     @Transactional(readOnly = true)
510     public AutomationCompositionPrimedResponse getAutomationCompositionPriming(String name, String version)
511         throws PfModelException {
512
513         List<AutomationComposition> automationCompositions =
514             automationCompositionProvider.getAutomationCompositions(name, version);
515
516         var response = new AutomationCompositionPrimedResponse();
517
518         automationCompositions.forEach(automationComposition -> {
519             var primed = new AutomationCompositionPrimed();
520             primed.setName(automationComposition.getName());
521             primed.setVersion(automationComposition.getVersion());
522             primed.setPrimed(automationComposition.getPrimed());
523             response.getPrimedAutomationCompositionsList().add(primed);
524         });
525
526         return response;
527     }
528
529     /**
530      * Creates instance element name.
531      *
532      * @param serviceTemplate the service template
533      * @param automationCompositions a list of automation compositions
534      * @return the result of the instance properties and instantiation operation
535      * @throws PfModelException on creation errors
536      */
537     private InstancePropertiesResponse saveInstancePropertiesAndAutomationComposition(
538         ToscaServiceTemplate serviceTemplate, AutomationCompositions automationCompositions) throws PfModelException {
539
540         for (var automationComposition : automationCompositions.getAutomationCompositionList()) {
541             var checkAutomationCompositionOpt =
542                 automationCompositionProvider.findAutomationComposition(automationComposition.getKey().asIdentifier());
543             if (checkAutomationCompositionOpt.isPresent()) {
544                 throw new PfModelException(Response.Status.BAD_REQUEST, "Automation composition with id "
545                     + automationComposition.getKey().asIdentifier() + " already defined");
546             }
547         }
548         Map<String, ToscaNodeTemplate> toscaSavedNodeTemplate =
549             automationCompositionProvider.saveInstanceProperties(serviceTemplate);
550         automationCompositionProvider.saveAutomationCompositions(automationCompositions.getAutomationCompositionList());
551         List<ToscaConceptIdentifier> affectedAutomationCompositions = automationCompositions
552             .getAutomationCompositionList().stream().map(ac -> ac.getKey().asIdentifier()).collect(Collectors.toList());
553
554         List<ToscaConceptIdentifier> toscaAffectedProperties = toscaSavedNodeTemplate.values().stream()
555             .map(template -> template.getKey().asIdentifier()).collect(Collectors.toList());
556
557         var response = new InstancePropertiesResponse();
558         response.setAffectedInstanceProperties(Stream.of(affectedAutomationCompositions, toscaAffectedProperties)
559             .flatMap(Collection::stream).collect(Collectors.toList()));
560
561         return response;
562     }
563
564     /**
565      * Crates a new automation composition instance.
566      *
567      * @param instanceName automation composition Instance name
568      * @param automationComposition empty automation composition
569      * @param automationCompositionElements new automation composition Element map
570      * @param template original Cloned Tosca Node Template
571      * @param newNodeTemplate new Tosca Node Template
572      */
573     private void crateNewAutomationCompositionInstance(String instanceName, AutomationComposition automationComposition,
574         Map<UUID, AutomationCompositionElement> automationCompositionElements, ToscaNodeTemplate template,
575         ToscaNodeTemplate newNodeTemplate) {
576         if (template.getType().equals(AUTOMATION_COMPOSITION_NODE_TYPE)) {
577             automationComposition.setDefinition(getAutomationCompositionDefinition(newNodeTemplate));
578         }
579
580         if (template.getType().contains(AUTOMATION_COMPOSITION_NODE_ELEMENT_TYPE)) {
581             AutomationCompositionElement automationCompositionElement =
582                 getAutomationCompositionElement(newNodeTemplate);
583             automationCompositionElements.put(automationCompositionElement.getId(), automationCompositionElement);
584         }
585
586         automationComposition.setName(instanceName);
587         automationComposition.setVersion(template.getVersion());
588         automationComposition.setDescription("Automation composition " + instanceName);
589         automationComposition.setState(AutomationCompositionState.UNINITIALISED);
590         automationComposition.setOrderedState(AutomationCompositionOrderedState.UNINITIALISED);
591     }
592
593     /**
594      * Get's the instance property name of the automation composition.
595      *
596      * @param name the name of the automation composition to get, null for all automation compositions
597      * @param version the version of the automation composition to get, null for all automation compositions
598      * @return the instance name of the automation composition instance properties
599      * @throws PfModelException on errors getting automation compositions
600      */
601     private String getInstancePropertyName(String name, String version) throws PfModelException {
602         List<String> toscaDefinitionsNames =
603             automationCompositionProvider.getAutomationCompositions(name, version).stream()
604                 .map(AutomationComposition::getDefinition).map(ToscaNameVersion::getName).collect(Collectors.toList());
605
606         return toscaDefinitionsNames.stream().reduce("", (s1, s2) -> {
607
608             if (s2.contains(HYPHEN)) {
609                 String[] instances = s2.split(HYPHEN);
610
611                 return HYPHEN + instances[1];
612             }
613
614             return s1;
615         });
616     }
617
618     /**
619      * Retrieves automation composition Definition.
620      *
621      * @param template tosca node template
622      * @return automation composition definition
623      */
624     private ToscaConceptIdentifier getAutomationCompositionDefinition(ToscaNodeTemplate template) {
625         ToscaConceptIdentifier definition = new ToscaConceptIdentifier();
626         definition.setName(template.getName());
627         definition.setVersion(template.getVersion());
628         return definition;
629     }
630
631     /**
632      * Retrieves automation composition Element.
633      *
634      * @param template tosca node template
635      * @return a automation composition element
636      */
637     @SuppressWarnings("unchecked")
638     private AutomationCompositionElement getAutomationCompositionElement(ToscaNodeTemplate template) {
639         AutomationCompositionElement automationCompositionElement = new AutomationCompositionElement();
640         ToscaConceptIdentifier definition = new ToscaConceptIdentifier();
641         definition.setName(template.getName());
642         definition.setVersion(template.getVersion());
643         automationCompositionElement.setDefinition(definition);
644         LinkedTreeMap<String, Object> participantId =
645             (LinkedTreeMap<String, Object>) template.getProperties().get(PARTICIPANT_ID_PROPERTY_KEY);
646         if (participantId != null) {
647             ToscaConceptIdentifier participantIdProperty = new ToscaConceptIdentifier();
648             participantIdProperty.setName(String.valueOf(participantId.get(AC_ELEMENT_NAME)));
649             participantIdProperty.setVersion(String.valueOf(participantId.get(AC_ELEMENT_VERSION)));
650             automationCompositionElement.setParticipantId(participantIdProperty);
651         }
652         LinkedTreeMap<String, Object> participantType =
653             (LinkedTreeMap<String, Object>) template.getProperties().get(PARTICIPANT_TYPE_PROPERTY_KEY);
654         if (participantType != null) {
655             ToscaConceptIdentifier participantTypeProperty = new ToscaConceptIdentifier();
656             participantTypeProperty.setName(String.valueOf(participantType.get(AC_ELEMENT_NAME)));
657             participantTypeProperty.setVersion(participantType.get(AC_ELEMENT_VERSION).toString());
658             automationCompositionElement.setParticipantType(participantTypeProperty);
659         }
660         return automationCompositionElement;
661     }
662
663     /**
664      * Deep clones ToscaNodeTemplate.
665      *
666      * @param serviceTemplate ToscaServiceTemplate
667      * @return a cloned Hash Map of ToscaNodeTemplate
668      */
669     private Map<String, ToscaNodeTemplate> deepCloneNodeTemplate(ToscaServiceTemplate serviceTemplate) {
670         String jsonString = GSON.toJson(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates());
671         Type type = new TypeToken<HashMap<String, ToscaNodeTemplate>>() {}.getType();
672         return GSON.fromJson(jsonString, type);
673     }
674
675     /**
676      * Updates Automation composition instance name.
677      *
678      * @param oldInstanceName previous saved instance name
679      * @param newInstanceName new instance name to replace the previous one
680      * @return AutomationCompositions updated
681      * @throws PfModelException exception to compositions is not defined
682      */
683     private AutomationCompositions updateAutomationComposition(
684             String oldInstanceName, String newInstanceName) throws PfModelException {
685
686         List<AutomationComposition> filteredAcmList = automationCompositionProvider.getAutomationCompositions()
687                 .stream().filter(acm -> acm.getName().contains(oldInstanceName)).collect(Collectors.toList());
688
689         if (filteredAcmList.isEmpty()) {
690             throw new PfModelException(Status.BAD_REQUEST, "Automation compositions not defined!");
691         }
692
693         AutomationComposition automationComposition = filteredAcmList.get(0);
694
695         automationComposition.getDefinition()
696                 .setName(updateInstanceNameDescription(newInstanceName, oldInstanceName,
697                         automationComposition.getDefinition().getName()));
698
699         automationComposition.setName(newInstanceName);
700
701         automationComposition.setDescription(
702                 updateInstanceNameDescription(newInstanceName, oldInstanceName,
703                         automationComposition.getDescription()));
704
705         automationComposition.getElements().forEach((uuid, automationCompositionElement) -> {
706             automationCompositionElement.getDefinition()
707                     .setName(updateInstanceNameDescription(newInstanceName, oldInstanceName,
708                             automationCompositionElement.getDefinition().getName()));
709         });
710
711         AutomationCompositions automationCompositions = new AutomationCompositions();
712         automationCompositions.getAutomationCompositionList().add(automationComposition);
713
714         return automationCompositions;
715
716     }
717
718     /**
719      * Updates instance and description.
720      *
721      * @param newInstanceName new instance name to replace the previous one
722      * @param oldInstanceName previous saved instance name
723      * @param value to be updated
724      * @return String updated instance name or description
725      */
726     private String updateInstanceNameDescription(
727             String newInstanceName, String oldInstanceName, String value) {
728         String toBeReplaced = value.substring(value.indexOf(oldInstanceName));
729
730         String replace = value.replace(toBeReplaced, newInstanceName);
731
732         return replace;
733     }
734 }