aee8b65ca34ae783da2140a5c425b5a6fc9442bf
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / tosca / CommonCsarGenerator.java
1 /*
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2023 Nordix Foundation. 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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  * ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.be.tosca;
21
22 import static org.openecomp.sdc.be.dao.api.ActionStatus.ARTIFACT_PAYLOAD_NOT_FOUND_DURING_CSAR_CREATION;
23 import static org.openecomp.sdc.be.dao.api.ActionStatus.ERROR_DURING_CSAR_CREATION;
24 import static org.openecomp.sdc.be.tosca.ComponentCache.MergeStrategy.overwriteIfSameVersions;
25 import static org.openecomp.sdc.be.tosca.FJToVavrHelper.Try0.fromEither;
26 import static org.openecomp.sdc.be.tosca.FJToVavrHelper.Try0.javaListToVavrList;
27 import static org.openecomp.sdc.common.api.Constants.ADDITIONAL_TYPE_DEFINITIONS;
28
29 import com.google.common.primitives.Bytes;
30 import fj.F;
31 import fj.data.Either;
32 import io.vavr.Tuple2;
33 import io.vavr.control.Try;
34 import java.io.BufferedOutputStream;
35 import java.io.ByteArrayInputStream;
36 import java.io.File;
37 import java.io.IOException;
38 import java.nio.charset.StandardCharsets;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.text.SimpleDateFormat;
42 import java.util.ArrayList;
43 import java.util.Date;
44 import java.util.EnumMap;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Map.Entry;
50 import java.util.Objects;
51 import java.util.Optional;
52 import java.util.Set;
53 import java.util.TimeZone;
54 import java.util.function.Function;
55 import java.util.function.Predicate;
56 import java.util.function.Supplier;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59 import java.util.stream.Collectors;
60 import java.util.stream.Stream;
61 import java.util.zip.ZipEntry;
62 import java.util.zip.ZipInputStream;
63 import java.util.zip.ZipOutputStream;
64 import org.apache.commons.collections.CollectionUtils;
65 import org.apache.commons.collections.MapUtils;
66 import org.apache.commons.io.output.ByteArrayOutputStream;
67 import org.apache.commons.lang.StringUtils;
68 import org.apache.commons.lang3.tuple.ImmutableTriple;
69 import org.apache.commons.lang3.tuple.Triple;
70 import org.apache.commons.text.WordUtils;
71 import org.onap.sdc.tosca.services.YamlUtil;
72 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
73 import org.openecomp.sdc.be.config.CategoryBaseTypeConfig;
74 import org.openecomp.sdc.be.config.ConfigurationManager;
75 import org.openecomp.sdc.be.dao.api.ActionStatus;
76 import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
77 import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
78 import org.openecomp.sdc.be.dao.cassandra.SdcSchemaFilesCassandraDao;
79 import org.openecomp.sdc.be.data.model.ToscaImportByModel;
80 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
81 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
82 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
83 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
84 import org.openecomp.sdc.be.impl.ComponentsUtils;
85 import org.openecomp.sdc.be.model.ArtifactDefinition;
86 import org.openecomp.sdc.be.model.Component;
87 import org.openecomp.sdc.be.model.ComponentInstance;
88 import org.openecomp.sdc.be.model.InterfaceDefinition;
89 import org.openecomp.sdc.be.model.LifecycleStateEnum;
90 import org.openecomp.sdc.be.model.Resource;
91 import org.openecomp.sdc.be.model.Service;
92 import org.openecomp.sdc.be.model.category.CategoryDefinition;
93 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
94 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
95 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
96 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
97 import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
98 import org.openecomp.sdc.be.plugins.CsarEntryGenerator;
99 import org.openecomp.sdc.be.resources.data.DAOArtifactData;
100 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
101 import org.openecomp.sdc.be.utils.TypeUtils;
102 import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
103 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
104 import org.openecomp.sdc.common.impl.ExternalConfiguration;
105 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
106 import org.openecomp.sdc.common.util.GeneralUtility;
107 import org.openecomp.sdc.common.zip.ZipUtils;
108 import org.openecomp.sdc.exception.ResponseFormat;
109 import org.slf4j.Logger;
110 import org.slf4j.LoggerFactory;
111 import org.springframework.beans.factory.annotation.Autowired;
112 import org.yaml.snakeyaml.DumperOptions;
113 import org.yaml.snakeyaml.Yaml;
114
115 /**
116  * Generates a Network Service CSAR based on a SERVICE component and wraps it in a SDC CSAR entry.
117  */
118 @org.springframework.stereotype.Component("commonCsarGenerator")
119 public class CommonCsarGenerator {
120
121     private static final Logger LOGGER = LoggerFactory.getLogger(CommonCsarGenerator.class);
122     public static final String ARTIFACTS_PATH = "Artifacts/";
123     private static final String RESOURCES_PATH = "Resources/";
124     private static final String PATH_DELIMITER = "/";
125     private static final String SERVICE_MANIFEST = "NS.mf";
126     private static final String ARTIFACT_NAME_UNIQUE_ID = "ArtifactName {}, unique ID {}";
127     private static final String TOSCA_META_PATH_FILE_NAME = "TOSCA-Metadata/TOSCA.meta";
128     private static final String TOSCA_META_VERSION = "1.0";
129     private static final String CSAR_VERSION = "1.1";
130     private static final String SDC_VERSION = ExternalConfiguration.getAppVersion();
131     public static final String NODES_YML = "nodes.yml";
132     private static final String CONFORMANCE_LEVEL = ConfigurationManager.getConfigurationManager().getConfiguration().getToscaConformanceLevel();
133     private final ToscaOperationFacade toscaOperationFacade;
134     private final ComponentsUtils componentsUtils;
135     private final ToscaExportHandler toscaExportUtils;
136     private final List<CsarEntryGenerator> generators;
137     private final ArtifactCassandraDao artifactCassandraDao;
138     private final String versionFirstThreeOctets;
139     private final SdcSchemaFilesCassandraDao sdcSchemaFilesCassandraDao;
140     private final ModelOperation modelOperation;
141
142     @Autowired
143     public CommonCsarGenerator(
144         final ToscaOperationFacade toscaOperationFacade,
145         final ComponentsUtils componentsUtils,
146         final ToscaExportHandler toscaExportUtils,
147         final List<CsarEntryGenerator> generators,
148         final ArtifactCassandraDao artifactCassandraDao,
149         final SdcSchemaFilesCassandraDao sdcSchemaFilesCassandraDao,
150         final ModelOperation modelOperation) {
151         this.toscaOperationFacade = toscaOperationFacade;
152         this.componentsUtils = componentsUtils;
153         this.toscaExportUtils = toscaExportUtils;
154         this.generators = generators;
155         this.artifactCassandraDao = artifactCassandraDao;
156         this.versionFirstThreeOctets = readVersionFirstThreeOctets();
157         this.sdcSchemaFilesCassandraDao = sdcSchemaFilesCassandraDao;
158         this.modelOperation = modelOperation;
159     }
160
161     private String readVersionFirstThreeOctets() {
162         if (StringUtils.isEmpty(SDC_VERSION)) {
163             return "";
164         }
165         // change regex to avoid DoS sonar issue
166         Matcher matcher = Pattern.compile("(?!\\.)(\\d{1,9}(\\.\\d{1,9}){1,9})(?![\\d\\.])").matcher(SDC_VERSION);
167         matcher.find();
168         return matcher.group(0);
169     }
170
171     /**
172      * Generates a Network Service CSAR based on a SERVICE component that has category configured in
173      * CategoriesToGenerateNsd enum and wraps it in a SDC CSAR entry.
174      *
175      * @param component the component to create the NS CSAR from
176      * @return an entry to be added in the Component CSAR by SDC
177      */
178
179     public Either<ZipOutputStream, ResponseFormat> generateCsarZip(Component component,
180                                                                    boolean getFromCS,
181                                                                    ZipOutputStream zip,
182                                                                    boolean isInCertificationRequest,
183                                                                    boolean isAsdPackage,
184                                                                    String definitionsPath,
185                                                                    boolean addDependencies,
186                                                                    boolean isSkipImports) throws IOException {
187         ArtifactDefinition artifactDef = component.getToscaArtifacts().get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
188         Either<ToscaRepresentation, ResponseFormat> toscaRepresentation = fetchToscaRepresentation(component, getFromCS, artifactDef, isSkipImports);
189
190         // This should not be done but in order to keep the refactoring small enough we stop here.
191         // TODO: Refactor the rest of this function
192         byte[] mainYaml;
193         List<Triple<String, String, Component>> dependencies;
194         if (toscaRepresentation.isLeft()) {
195             mainYaml = toscaRepresentation.left().value().getMainYaml();
196             dependencies = toscaRepresentation.left().value().getDependencies().getOrElse(new ArrayList<>());
197         } else {
198             return Either.right(toscaRepresentation.right().value());
199         }
200         final String fileName = artifactDef.getArtifactName();
201         final byte[] toscaBlock0Byte =
202             createToscaBlock0(TOSCA_META_VERSION, CSAR_VERSION, component.getCreatorFullName(), fileName, isAsdPackage, definitionsPath).getBytes();
203         zip.putNextEntry(new ZipEntry(TOSCA_META_PATH_FILE_NAME));
204         zip.write(toscaBlock0Byte);
205         zip.putNextEntry(new ZipEntry(definitionsPath + fileName));
206         zip.write(mainYaml);
207         LifecycleStateEnum lifecycleState = component.getLifecycleState();
208         addServiceMf(component, zip, lifecycleState, isInCertificationRequest, fileName, mainYaml, definitionsPath);
209         if (addDependencies) {
210             //US798487 - Abstraction of complex types
211             if (hasToWriteComponentSubstitutionType(component)) {
212                 LOGGER.debug("Component {} is complex - generating abstract type for it..", component.getName());
213                 dependencies.addAll(writeComponentInterface(component, zip, fileName, definitionsPath));
214             }
215             //UID <cassandraId,filename,component>
216             Either<ZipOutputStream, ResponseFormat> zipOutputStreamOrResponseFormat =
217                 getZipOutputStreamResponseFormatEither(zip, dependencies, definitionsPath);
218             if (zipOutputStreamOrResponseFormat != null && zipOutputStreamOrResponseFormat.isRight()) {
219                 return zipOutputStreamOrResponseFormat;
220             }
221         }
222         if (component.getModel() == null) {
223             //retrieve SDC.zip from Cassandra
224             Either<byte[], ResponseFormat> latestSchemaFiles = getLatestSchemaFilesFromCassandra();
225             if (latestSchemaFiles.isRight()) {
226                 LOGGER.error("Error retrieving SDC Schema files from cassandra");
227                 return Either.right(latestSchemaFiles.right().value());
228             }
229             final byte[] schemaFileZip = latestSchemaFiles.left().value();
230             final List<String> nodesFromPackage = findNonRootNodesFromPackage(dependencies);
231             //add files from retrieved SDC.zip to Definitions folder in CSAR
232             addSchemaFilesFromCassandra(zip, schemaFileZip, nodesFromPackage, definitionsPath);
233         } else {
234             //retrieve schema files by model from Cassandra
235             addSchemaFilesByModel(zip, component.getModel(), definitionsPath, addDependencies);
236         }
237         Either<CsarDefinition, ResponseFormat> collectedComponentCsarDefinition = collectComponentCsarDefinition(component);
238         if (collectedComponentCsarDefinition.isRight()) {
239             return Either.right(collectedComponentCsarDefinition.right().value());
240         }
241         if (generators != null) {
242             for (CsarEntryGenerator generator : generators) {
243                 LOGGER.debug("Invoking CsarEntryGenerator: {}", generator.getClass().getName());
244                 for (Map.Entry<String, byte[]> pluginGeneratedFile : generator.generateCsarEntries(component).entrySet()) {
245                     zip.putNextEntry(new ZipEntry(pluginGeneratedFile.getKey()));
246                     zip.write(pluginGeneratedFile.getValue());
247                 }
248             }
249         }
250         return writeAllFilesToCsar(component, collectedComponentCsarDefinition.left().value(), zip, isInCertificationRequest);
251     }
252
253     private Either<ToscaRepresentation, ResponseFormat> fetchToscaRepresentation(Component component, boolean getFromCS,
254                                                                                  ArtifactDefinition artifactDef, boolean isSkipImports) {
255         LifecycleStateEnum lifecycleState = component.getLifecycleState();
256         boolean shouldBeFetchedFromCassandra =
257             getFromCS || !(lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKIN || lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
258         Either<ToscaRepresentation, ResponseFormat> toscaRepresentation =
259             shouldBeFetchedFromCassandra ? fetchToscaRepresentation(artifactDef) : generateToscaRepresentation(component, isSkipImports);
260         return toscaRepresentation.left()
261             .bind(iff(myd -> !myd.getDependencies().isDefined(), myd -> fetchToscaTemplateDependencies(myd.getMainYaml(), component)));
262     }
263
264     private Either<ToscaRepresentation, ResponseFormat> fetchToscaTemplateDependencies(byte[] mainYml, Component component) {
265         return toscaExportUtils.getDependencies(component).right().map(toscaError -> {
266             LOGGER.debug("Failed to retrieve dependencies for component {}, error {}", component.getUniqueId(), toscaError);
267             return componentsUtils.getResponseFormat(componentsUtils.convertFromToscaError(toscaError));
268         }).left().map(tt -> ToscaRepresentation.make(mainYml, tt));
269     }
270
271     private Either<ToscaRepresentation, ResponseFormat> fetchToscaRepresentation(ArtifactDefinition artifactDef) {
272         return getFromCassandra(artifactDef.getEsId()).right().map(as -> {
273             LOGGER.debug(ARTIFACT_NAME_UNIQUE_ID, artifactDef.getArtifactName(), artifactDef.getUniqueId());
274             return componentsUtils.getResponseFormat(as);
275         }).left().map(ToscaRepresentation::make);
276     }
277
278     private Either<byte[], ActionStatus> getFromCassandra(String cassandraId) {
279         return artifactCassandraDao.getArtifact(cassandraId).right().map(operationstatus -> {
280             LOGGER.info("Failed to fetch artifact from Cassandra by id {} error {}.", cassandraId, operationstatus);
281             StorageOperationStatus storageStatus = DaoStatusConverter.convertCassandraStatusToStorageStatus(operationstatus);
282             return componentsUtils.convertFromStorageResponse(storageStatus);
283         }).left().map(DAOArtifactData::getDataAsArray);
284     }
285
286     private static <L, R> F<L, Either<L, R>> iff(Predicate<L> p, Function<L, Either<L, R>> ifTrue) {
287         return l -> p.test(l) ? ifTrue.apply(l) : Either.left(l);
288     }
289
290     private static <A, B> F<A, B> iff(Predicate<A> p, Supplier<B> s, Function<A, B> orElse) {
291         return a -> p.test(a) ? s.get() : orElse.apply(a);
292     }
293
294     private void addServiceMf(Component component, ZipOutputStream zip, LifecycleStateEnum lifecycleState, boolean isInCertificationRequest,
295                               String fileName, byte[] mainYaml, String definitionsPath) throws IOException {
296         // add mf
297         if ((component.getComponentType() == ComponentTypeEnum.SERVICE) && (lifecycleState != LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
298             String serviceName = component.getName();
299             String createdBy = component.getCreatorUserId();
300             String serviceVersion;
301             if (isInCertificationRequest) {
302                 int tmp = Integer.valueOf(component.getVersion().split("\\.")[0]) + 1;
303                 serviceVersion = String.valueOf(tmp) + ".0";
304             } else {
305                 serviceVersion = component.getVersion();
306             }
307             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
308             format.setTimeZone(TimeZone.getTimeZone("UTC"));
309             Date date = new Date();
310             String releaseTime = format.format(date);
311             if (component.getCategories() == null || component.getCategories().get(0) == null) {
312                 return;
313             }
314             String serviceType = component.getCategories().get(0).getName();
315             String description = component.getDescription();
316             String serviceTemplate = definitionsPath + fileName;
317             String hash = GeneralUtility.calculateMD5Base64EncodedByByteArray(mainYaml);
318             String nsMfBlock0 = createNsMfBlock0(serviceName, createdBy, serviceVersion, releaseTime, serviceType, description, serviceTemplate,
319                 hash);
320             byte[] nsMfBlock0Byte = nsMfBlock0.getBytes();
321             zip.putNextEntry(new ZipEntry(SERVICE_MANIFEST));
322             zip.write(nsMfBlock0Byte);
323         }
324     }
325
326     private String createNsMfBlock0(String serviceName, String createdBy, String serviceVersion, String releaseTime, String serviceType,
327                                     String description, String serviceTemplate, String hash) {
328         final String block0template = "metadata??\n" + "ns_product_name: %s\n" + "ns_provider_id: %s\n" + "ns_package_version: %s\n" +
329             "ns_release_data_time: %s\n" + "ns_type: %s\n" + "ns_package_description: %s\n\n" + "Source: %s\n" + "Algorithm: MD5\n" + "Hash: %s\n\n";
330         return String.format(block0template, serviceName, createdBy, serviceVersion, releaseTime, serviceType, description, serviceTemplate, hash);
331     }
332
333     private boolean hasToWriteComponentSubstitutionType(final Component component) {
334         final Map<String, CategoryBaseTypeConfig> serviceNodeTypesConfig =
335             ConfigurationManager.getConfigurationManager().getConfiguration().getServiceBaseNodeTypes();
336         List<CategoryDefinition> categories = component.getCategories();
337         if (CollectionUtils.isNotEmpty(categories) && MapUtils.isNotEmpty(serviceNodeTypesConfig)
338             && serviceNodeTypesConfig.get(categories.get(0).getName()) != null) {
339             boolean doNotExtendBaseType = serviceNodeTypesConfig.get(categories.get(0).getName()).isDoNotExtendBaseType();
340             if (doNotExtendBaseType) {
341                 return false;
342             }
343         }
344         if (component instanceof Service) {
345             return !ModelConverter.isAtomicComponent(component) && ((Service) component).isSubstituteCandidate();
346         }
347         return !ModelConverter.isAtomicComponent(component);
348     }
349
350     private Either<ZipOutputStream, ResponseFormat> writeComponentInterface(Either<ToscaRepresentation, ToscaError> interfaceRepresentation,
351                                                                             ZipOutputStream zip, String fileName, String definitionsPath) {
352         // TODO: This should not be done but we need this to keep the refactoring small enough to be easily reviewable
353         return writeComponentInterface(interfaceRepresentation, fileName, ZipWriter.live(zip), definitionsPath)
354             .map(void0 -> Either.<ZipOutputStream, ResponseFormat>left(zip)).recover(th -> {
355                 LOGGER.error("#writeComponentInterface - zip writing failed with error: ", th);
356                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
357             }).get();
358     }
359
360     private Try<Void> writeComponentInterface(
361         Either<ToscaRepresentation, ToscaError> interfaceRepresentation, String fileName, ZipWriter zw, String definitionsPath) {
362         Either<byte[], ToscaError> yml = interfaceRepresentation.left()
363             .map(ToscaRepresentation::getMainYaml);
364         return fromEither(yml, ToscaErrorException::new).flatMap(zw.write(definitionsPath + ToscaExportHandler.getInterfaceFilename(fileName)));
365     }
366
367     private List<Triple<String, String, Component>> writeComponentInterface(final Component component, final ZipOutputStream zip,
368                                                                             final String fileName, final String definitionsPath) {
369         final Either<ToscaRepresentation, ToscaError> interfaceRepresentation = toscaExportUtils.exportComponentInterface(component, false);
370         writeComponentInterface(interfaceRepresentation, zip, fileName, definitionsPath);
371         return interfaceRepresentation.left().value().getDependencies().getOrElse(new ArrayList<>());
372     }
373
374     private Either<ZipOutputStream, ResponseFormat> getZipOutputStreamResponseFormatEither(ZipOutputStream zip,
375                                                                                            List<Triple<String, String, Component>> dependencies,
376                                                                                            String definitionsPath)
377         throws IOException {
378         ComponentCache
379             innerComponentsCache = ComponentCache.overwritable(overwriteIfSameVersions()).onMerge((oldValue, newValue) ->
380             LOGGER.warn("Overwriting component invariantID {} of version {} with a newer version {}", oldValue.getId(),
381                 oldValue.getComponentVersion(),
382                 newValue.getComponentVersion()));
383         if (dependencies != null && !dependencies.isEmpty()) {
384             for (Triple<String, String, Component> d : dependencies) {
385                 String cassandraId = d.getMiddle();
386                 Component childComponent = d.getRight();
387                 Either<byte[], ResponseFormat> entryData = getEntryData(cassandraId, childComponent).right()
388                     .map(componentsUtils::getResponseFormat);
389                 if (entryData.isRight()) {
390                     return Either.right(entryData.right().value());
391                 }
392                 //fill innerComponentsCache
393                 String fileName = d.getLeft();
394                 innerComponentsCache.put(cassandraId, fileName, childComponent);
395                 addInnerComponentsToCache(innerComponentsCache, childComponent);
396             }
397             //add inner components to CSAR
398             return addInnerComponentsToCSAR(zip, innerComponentsCache, definitionsPath);
399         }
400         return null;
401     }
402
403     private Either<ZipOutputStream, ResponseFormat> addInnerComponentsToCSAR(ZipOutputStream zip, ComponentCache innerComponentsCache,
404                                                                              String definitionsPath)
405         throws IOException {
406         for (ImmutableTriple<String, String, Component> ict : innerComponentsCache.iterable()) {
407             Component innerComponent = ict.getRight();
408             String icFileName = ict.getMiddle();
409             // add component to zip
410             Either<Tuple2<byte[], ZipEntry>, ResponseFormat> zipEntry = toZipEntry(ict, definitionsPath);
411             // TODO: this should not be done, we should instead compose this either further,
412
413             // but in order to keep this refactoring small, we'll stop here.
414             if (zipEntry.isRight()) {
415                 return Either.right(zipEntry.right().value());
416             }
417             Tuple2<byte[], ZipEntry> value = zipEntry.left().value();
418             zip.putNextEntry(value._2);
419             zip.write(value._1);
420             // add component interface to zip
421             if (hasToWriteComponentSubstitutionType(innerComponent)) {
422                 writeComponentInterface(innerComponent, zip, icFileName, definitionsPath);
423             }
424         }
425         return null;
426     }
427
428     private Either<Tuple2<byte[], ZipEntry>, ResponseFormat> toZipEntry(ImmutableTriple<String, String, Component> cachedEntry,
429                                                                         String definitionsPath) {
430         String cassandraId = cachedEntry.getLeft();
431         String fileName = cachedEntry.getMiddle();
432         Component innerComponent = cachedEntry.getRight();
433         return getEntryData(cassandraId, innerComponent).right().map(status -> {
434             LOGGER.debug("Failed adding to zip component {}, error {}", cassandraId, status);
435             return componentsUtils.getResponseFormat(status);
436         }).left().map(content -> new Tuple2<>(content, new ZipEntry(definitionsPath + fileName)));
437     }
438
439     private void addInnerComponentsToCache(ComponentCache componentCache, Component childComponent) {
440         javaListToVavrList(childComponent.getComponentInstances()).filter(ci -> componentCache.notCached(ci.getComponentUid())).forEach(ci -> {
441             // all resource must be only once!
442             Either<Resource, StorageOperationStatus> resource = toscaOperationFacade.getToscaElement(ci.getComponentUid());
443             Component componentRI = checkAndAddComponent(componentCache, ci, resource);
444             //if not atomic - insert inner components as well
445
446             // TODO: This could potentially create a StackOverflowException if the call stack
447
448             // happens to be too large. Tail-recursive optimization should be used here.
449             if (!ModelConverter.isAtomicComponent(componentRI)) {
450                 addInnerComponentsToCache(componentCache, componentRI);
451             }
452         });
453     }
454
455     private Component checkAndAddComponent(ComponentCache componentCache, ComponentInstance ci, Either<Resource, StorageOperationStatus> resource) {
456         if (resource.isRight()) {
457             LOGGER.debug("Failed to fetch resource with id {} for instance {}", ci.getComponentUid(), ci.getName());
458         }
459         Component componentRI = resource.left().value();
460         Map<String, ArtifactDefinition> childToscaArtifacts = componentRI.getToscaArtifacts();
461         ArtifactDefinition childArtifactDefinition = childToscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
462         if (childArtifactDefinition != null) {
463             //add to cache
464             componentCache.put(childArtifactDefinition.getEsId(), childArtifactDefinition.getArtifactName(), componentRI);
465         }
466         return componentRI;
467     }
468
469     private Either<byte[], ActionStatus> getEntryData(String cassandraId, Component childComponent) {
470         if (cassandraId == null || cassandraId.isEmpty()) {
471             return toscaExportUtils.exportComponent(childComponent).right().map(toscaErrorToActionStatus(childComponent)).left()
472                 .map(ToscaRepresentation::getMainYaml);
473         } else {
474             return getFromCassandra(cassandraId);
475         }
476     }
477
478     private F<ToscaError, ActionStatus> toscaErrorToActionStatus(Component childComponent) {
479         return toscaError -> {
480             LOGGER.debug("Failed to export tosca template for child component {} error {}", childComponent.getUniqueId(), toscaError);
481             return componentsUtils.convertFromToscaError(toscaError);
482         };
483     }
484
485     private Either<byte[], ResponseFormat> getLatestSchemaFilesFromCassandra() {
486         String fto = versionFirstThreeOctets;
487         return sdcSchemaFilesCassandraDao.getSpecificSchemaFiles(fto, CONFORMANCE_LEVEL).right().map(schemaFilesFetchDBError(fto)).left()
488             .bind(iff(List::isEmpty, () -> schemaFileFetchError(fto), s -> Either.left(s.iterator().next().getPayloadAsArray())));
489     }
490
491     private F<CassandraOperationStatus, ResponseFormat> schemaFilesFetchDBError(String firstThreeOctets) {
492         return cos -> {
493             LOGGER.debug("Failed to get the schema files SDC-Version: {} Conformance-Level {}. Please fix DB table accordingly.", firstThreeOctets,
494                 CONFORMANCE_LEVEL);
495             StorageOperationStatus sos = DaoStatusConverter.convertCassandraStatusToStorageStatus(cos);
496             return componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(sos));
497         };
498     }
499
500     private Either<byte[], ResponseFormat> schemaFileFetchError(String firstThreeOctets) {
501         LOGGER.debug("Failed to get the schema files SDC-Version: {} Conformance-Level {}", firstThreeOctets, CONFORMANCE_LEVEL);
502         return Either.right(componentsUtils.getResponseFormat(ActionStatus.TOSCA_SCHEMA_FILES_NOT_FOUND, firstThreeOctets, CONFORMANCE_LEVEL));
503     }
504
505     /**
506      * Create a list of all derived nodes found on the package
507      *
508      * @param dependencies all node dependencies
509      * @return a list of nodes
510      */
511     private List<String> findNonRootNodesFromPackage(final List<Triple<String, String, Component>> dependencies) {
512         final List<String> nodes = new ArrayList<>();
513         if (CollectionUtils.isNotEmpty(dependencies)) {
514             final String NATIVE_ROOT = "tosca.nodes.Root";
515             dependencies.forEach(dependency -> {
516                 if (dependency.getRight() instanceof Resource) {
517                     final Resource resource = (Resource) dependency.getRight();
518                     if (CollectionUtils.isNotEmpty(resource.getDerivedList())) {
519                         resource.getDerivedList().stream().filter(node -> !nodes.contains(node) && !NATIVE_ROOT.equalsIgnoreCase(node))
520                             .forEach(node -> nodes.add(node));
521                     }
522                 }
523             });
524         }
525         return nodes;
526     }
527
528     /**
529      * Writes to a CSAR zip from casandra schema data
530      *
531      * @param zipOutputStream  stores the input stream content
532      * @param schemaFileZip    zip data from Cassandra
533      * @param nodesFromPackage list of all nodes found on the onboarded package
534      */
535     private void addSchemaFilesFromCassandra(final ZipOutputStream zipOutputStream, final byte[] schemaFileZip, final List<String> nodesFromPackage,
536                                              final String definitionsPath) {
537         final int initSize = 2048;
538         LOGGER.debug("Starting copy from Schema file zip to CSAR zip");
539         try (final ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(
540             schemaFileZip)); final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
541             byteArrayOutputStream, initSize)) {
542             ZipEntry entry;
543             while ((entry = zipInputStream.getNextEntry()) != null) {
544                 ZipUtils.checkForZipSlipInRead(entry);
545                 final String entryName = entry.getName();
546                 int readSize = initSize;
547                 final byte[] entryData = new byte[initSize];
548                 if (shouldZipEntryBeHandled(entryName)) {
549                     if (NODES_YML.equalsIgnoreCase(entryName)) {
550                         handleNode(zipInputStream, byteArrayOutputStream, nodesFromPackage);
551                     } else {
552                         while ((readSize = zipInputStream.read(entryData, 0, readSize)) != -1) {
553                             bufferedOutputStream.write(entryData, 0, readSize);
554                         }
555                         bufferedOutputStream.flush();
556                     }
557                     byteArrayOutputStream.flush();
558                     zipOutputStream.putNextEntry(new ZipEntry(definitionsPath + entryName));
559                     zipOutputStream.write(byteArrayOutputStream.toByteArray());
560                     zipOutputStream.flush();
561                     byteArrayOutputStream.reset();
562                 }
563             }
564         } catch (final Exception e) {
565             LOGGER.error("Error while writing the SDC schema file to the CSAR", e);
566             throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
567         }
568         LOGGER.debug("Finished copy from Schema file zip to CSAR zip");
569     }
570
571     /**
572      * Handles the nodes.yml zip entry, updating the nodes.yml to avoid duplicated nodes on it.
573      *
574      * @param zipInputStream        the zip entry to be read
575      * @param byteArrayOutputStream an output stream in which the data is written into a byte array.
576      * @param nodesFromPackage      list of all nodes found on the onboarded package
577      */
578     private void handleNode(final ZipInputStream zipInputStream, final ByteArrayOutputStream byteArrayOutputStream,
579                             final List<String> nodesFromPackage) throws IOException {
580         final Map<String, Object> nodesFromArtifactFile = readYamlZipEntry(zipInputStream);
581         final Map<String, Object> nodesYaml = updateNodeYml(nodesFromPackage, nodesFromArtifactFile);
582         updateZipEntry(byteArrayOutputStream, nodesYaml);
583     }
584
585     /**
586      * Updates the zip entry from the given parameters
587      *
588      * @param byteArrayOutputStream an output stream in which the data is written into a byte array.
589      * @param nodesYaml             a Map of nodes to be written
590      */
591     private void updateZipEntry(final ByteArrayOutputStream byteArrayOutputStream, final Map<String, Object> nodesYaml) throws IOException {
592         if (MapUtils.isNotEmpty(nodesYaml)) {
593             byteArrayOutputStream.write(new YamlUtil().objectToYaml(nodesYaml).getBytes());
594         }
595     }
596
597     /**
598      * Filters and removes all duplicated nodes found
599      *
600      * @param nodesFromPackage      a List of all derived nodes found on the given package
601      * @param nodesFromArtifactFile represents the nodes.yml file stored in Cassandra
602      * @return a nodes Map updated
603      */
604     private Map<String, Object> updateNodeYml(final List<String> nodesFromPackage, final Map<String, Object> nodesFromArtifactFile) {
605         if (MapUtils.isNotEmpty(nodesFromArtifactFile)) {
606             final String nodeTypeBlock = TypeUtils.ToscaTagNamesEnum.NODE_TYPES.getElementName();
607             final Map<String, Object> nodeTypes = (Map<String, Object>) nodesFromArtifactFile.get(nodeTypeBlock);
608             nodesFromPackage.stream().filter(nodeTypes::containsKey).forEach(nodeTypes::remove);
609             nodesFromArtifactFile.replace(nodeTypeBlock, nodeTypes);
610         }
611         return nodesFromArtifactFile;
612     }
613
614     /**
615      * Writes a new zip entry
616      *
617      * @param zipInputStream the zip entry to be read
618      * @return a map of the given zip entry
619      */
620     private Map<String, Object> readYamlZipEntry(final ZipInputStream zipInputStream) throws IOException {
621         final int initSize = 2048;
622         final StringBuilder zipEntry = new StringBuilder();
623         final byte[] buffer = new byte[initSize];
624         int read = 0;
625         while ((read = zipInputStream.read(buffer, 0, initSize)) >= 0) {
626             zipEntry.append(new String(buffer, 0, read));
627         }
628         return (Map<String, Object>) new Yaml().load(zipEntry.toString());
629     }
630
631     /**
632      * Checks if the zip entry should or should not be added to the CSAR based on the given global type list
633      *
634      * @param entryName the zip entry name
635      * @return true if the zip entry should be handled
636      */
637     private boolean shouldZipEntryBeHandled(final String entryName) {
638         return ConfigurationManager.getConfigurationManager().getConfiguration().getGlobalCsarImports().stream()
639             .anyMatch(entry -> entry.contains(entryName));
640     }
641
642     private void addSchemaFilesByModel(final ZipOutputStream zipOutputStream, final String modelName,
643                                        final String definitionsPath, final boolean isSingleImportsFile) {
644         try {
645             final List<ToscaImportByModel> modelDefaultImportList = modelOperation.findAllModelImports(modelName, true);
646             final Set<Path> writtenEntryPathList = new HashSet<>();
647             final var defsPath = Path.of(definitionsPath);
648             Map<Path, byte[]> contentToMerge = new HashMap<>();
649             for (final ToscaImportByModel toscaImportByModel : modelDefaultImportList) {
650                 var importPath = Path.of(toscaImportByModel.getFullPath());
651                 if (!isSingleImportsFile) {
652                     if (ADDITIONAL_TYPE_DEFINITIONS.equals(Paths.get(String.valueOf(importPath)).normalize().toString())) {
653                         final Path entryPath = defsPath.resolve(importPath);
654                         contentToMerge.put(entryPath, toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8));
655                     } else {
656                         if (writtenEntryPathList.contains(defsPath.resolve(importPath))) {
657                             importPath = ToscaDefaultImportHelper.addModelAsFilePrefix(importPath, toscaImportByModel.getModelId());
658                         }
659                         final Path entryPath = defsPath.resolve(importPath);
660                         writtenEntryPathList.add(entryPath);
661                         contentToMerge.put(entryPath, toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8));
662                     }
663                 } else {
664                     if (writtenEntryPathList.contains(defsPath.resolve(importPath))) {
665                         importPath = ToscaDefaultImportHelper.addModelAsFilePrefix(importPath, toscaImportByModel.getModelId());
666                     }
667                     final Path entryPath = defsPath.resolve(importPath);
668                     zipOutputStream.putNextEntry(new ZipEntry(entryPath.toString()));
669                     writtenEntryPathList.add(entryPath);
670                     final byte[] content = toscaImportByModel.getContent().getBytes(StandardCharsets.UTF_8);
671                     zipOutputStream.write(content, 0, content.length);
672                     zipOutputStream.closeEntry();
673                 }
674             }
675             if (!isSingleImportsFile) {
676                 byte[] mergingContent = new byte[0];
677                 for (Map.Entry<Path, byte[]> entry : contentToMerge.entrySet()) {
678                     if (ADDITIONAL_TYPE_DEFINITIONS.equals(Paths.get(String.valueOf(entry.getKey())).normalize().toString())) {
679                         mergingContent = mergeContent(mergingContent, entry.getValue());
680                     } else {
681                         final var zipEntry = new ZipEntry(entry.getKey().toString());
682                         zipOutputStream.putNextEntry(zipEntry);
683                         writtenEntryPathList.add(entry.getKey());
684                         mergingContent = mergeContent(mergingContent, entry.getValue());
685                         zipOutputStream.write(mergingContent, 0, mergingContent.length);
686                         zipOutputStream.closeEntry();
687                     }
688                 }
689             }
690         } catch (final IOException e) {
691             LOGGER.error(String.valueOf(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR), CsarUtils.class.getName(),
692                 "Error while writing the schema files by model to the CSAR", e);
693             throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.CSAR_TOSCA_IMPORTS_ERROR));
694         }
695     }
696
697     private byte[] mergeContent(final byte[] first, final byte[] second) {
698         byte[] merged = new byte[0];
699         final Map<String, Object> firstMap = new Yaml().load(new String(first));
700         final Map<String, Object> secondMap = new Yaml().load(new String(second));
701         if (MapUtils.isNotEmpty(secondMap)) {
702             final DumperOptions options = new DumperOptions();
703             options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
704             final Yaml yaml = new Yaml(options);
705             for (final Entry<String, Object> secondMapEntry : secondMap.entrySet()) {
706                 final Map<String, Object> newMap = new HashMap<>();
707                 if (secondMapEntry.getKey().endsWith("_types")) {
708                     if (MapUtils.isNotEmpty(firstMap) && firstMap.containsKey(secondMapEntry.getKey())) {
709                         final Map<String, Object> secondMapEntryValue = (Map<String, Object>) secondMapEntry.getValue();
710                         final Map<String, Object> firstMapValue = (Map<String, Object>) firstMap.get(secondMapEntry.getKey());
711                         secondMapEntryValue.putAll(firstMapValue);
712                         newMap.put(secondMapEntry.getKey(), secondMapEntryValue);
713                     } else {
714                         newMap.put(secondMapEntry.getKey(), secondMapEntry.getValue());
715                     }
716                 } else {
717                     newMap.put(secondMapEntry.getKey(), secondMapEntry.getValue());
718                 }
719                 merged = Bytes.concat(merged, yaml.dumpAsMap(newMap).getBytes());
720             }
721         }
722         return merged;
723     }
724
725     private Either<CsarDefinition, ResponseFormat> collectComponentCsarDefinition(Component component) {
726         ComponentArtifacts componentArtifacts = new ComponentArtifacts();
727         Component updatedComponent = component;
728
729         //get service to receive the AII artifacts uploaded to the service
730         if (updatedComponent.getComponentType() == ComponentTypeEnum.SERVICE) {
731             Either<Service, StorageOperationStatus> getServiceResponse = toscaOperationFacade.getToscaElement(updatedComponent.getUniqueId());
732
733             if (getServiceResponse.isRight()) {
734                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getServiceResponse.right().value());
735                 return Either.right(componentsUtils.getResponseFormat(actionStatus));
736             }
737
738             updatedComponent = getServiceResponse.left().value();
739         }
740
741         //find the artifacts of the main component, it would have its composed instances artifacts in a separate folder
742         ComponentTypeArtifacts componentInstanceArtifacts = new ComponentTypeArtifacts();
743         ArtifactsInfo artifactsInfo = collectComponentArtifacts(updatedComponent);
744         componentInstanceArtifacts.setComponentArtifacts(artifactsInfo);
745         componentArtifacts.setMainTypeAndCIArtifacts(componentInstanceArtifacts);
746
747         Map<String, ComponentTypeArtifacts> resourceTypeArtifacts = componentArtifacts
748             .getComponentTypeArtifacts();    //artifacts mapped by the component type(tosca name+version)
749         //get the component instances
750         List<ComponentInstance> componentInstances = updatedComponent.getComponentInstances();
751         if (componentInstances != null) {
752             for (ComponentInstance componentInstance : componentInstances) {
753                 //call recursive to find artifacts for all the path
754                 Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts = collectComponentInstanceArtifacts(
755                     updatedComponent, componentInstance, resourceTypeArtifacts, componentInstanceArtifacts);
756                 if (collectComponentInstanceArtifacts.isRight()) {
757                     return Either.right(collectComponentInstanceArtifacts.right().value());
758                 }
759             }
760         }
761
762         if (LOGGER.isDebugEnabled()) {
763             printResult(componentArtifacts, updatedComponent.getName());
764         }
765
766         return Either.left(new CsarDefinition(componentArtifacts));
767     }
768
769     private void printResult(ComponentArtifacts componentArtifacts, String name) {
770         StringBuilder result = new StringBuilder();
771         result.append("Artifacts of main component " + name + "\n");
772         ComponentTypeArtifacts componentInstanceArtifacts = componentArtifacts.getMainTypeAndCIArtifacts();
773         printArtifacts(componentInstanceArtifacts);
774         result.append("Type Artifacts\n");
775         for (Map.Entry<String, ComponentTypeArtifacts> typeArtifacts : componentArtifacts.getComponentTypeArtifacts().entrySet()) {
776             result.append("Folder " + typeArtifacts.getKey() + "\n");
777             result.append(printArtifacts(typeArtifacts.getValue()));
778         }
779
780         if (LOGGER.isDebugEnabled()) {
781             LOGGER.debug(result.toString());
782         }
783     }
784
785     private String printArtifacts(ComponentTypeArtifacts componentInstanceArtifacts) {
786         StringBuilder result = new StringBuilder();
787         ArtifactsInfo artifactsInfo = componentInstanceArtifacts.getComponentArtifacts();
788         Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componentArtifacts = artifactsInfo.getArtifactsInfo();
789         printArtifacts(componentArtifacts);
790         result = result.append("Resources\n");
791         for (Map.Entry<String, ArtifactsInfo> resourceInstance : componentInstanceArtifacts.getComponentInstancesArtifacts().entrySet()) {
792             result.append("Folder" + resourceInstance.getKey() + "\n");
793             result.append(printArtifacts(resourceInstance.getValue().getArtifactsInfo()));
794         }
795
796         return result.toString();
797     }
798
799     private String printArtifacts(Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componetArtifacts) {
800         StringBuilder result = new StringBuilder();
801         for (Map.Entry<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactGroup : componetArtifacts.entrySet()) {
802             result.append("    " + artifactGroup.getKey().getType());
803             for (Map.Entry<String, List<ArtifactDefinition>> groupArtifacts : artifactGroup.getValue().entrySet()) {
804                 result.append("        " + groupArtifacts.getKey());
805                 for (ArtifactDefinition artifact : groupArtifacts.getValue()) {
806                     result.append("            " + artifact.getArtifactDisplayName());
807                 }
808             }
809         }
810
811         return result.toString();
812     }
813
814     private Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts(Component parentComponent, ComponentInstance componentInstance,
815                                                                               Map<String, ComponentTypeArtifacts> resourcesTypeArtifacts,
816                                                                               ComponentTypeArtifacts instanceArtifactsLocation) {
817         //1. get the component instance component
818         String componentUid;
819         if (componentInstance.getOriginType() == OriginTypeEnum.ServiceProxy) {
820             componentUid = componentInstance.getSourceModelUid();
821         } else {
822             componentUid = componentInstance.getComponentUid();
823         }
824         Either<Component, StorageOperationStatus> component = toscaOperationFacade.getToscaElement(componentUid);
825         if (component.isRight()) {
826             LOGGER.error("Failed to fetch resource with id {} for instance {}", componentUid, parentComponent.getUUID());
827             return Either.right(componentsUtils.getResponseFormat(ActionStatus.ASSET_NOT_FOUND_DURING_CSAR_CREATION,
828                 parentComponent.getComponentType().getValue(), parentComponent.getUUID(),
829                 componentInstance.getOriginType().getComponentType().getValue(), componentUid));
830         }
831         Component fetchedComponent = component.left().value();
832
833         //2. fill the artifacts for the current component parent type
834         String toscaComponentName =
835             componentInstance.getToscaComponentName() + "_v" + componentInstance.getComponentVersion();
836
837         // if there are no artifacts for this component type we need to fetch and build them
838         ComponentTypeArtifacts componentParentArtifacts = Optional
839             .ofNullable(resourcesTypeArtifacts.get(toscaComponentName))
840             .orElseGet(() -> collectComponentTypeArtifacts(fetchedComponent));
841
842         if (componentParentArtifacts.getComponentArtifacts().isNotEmpty()) {
843             resourcesTypeArtifacts.put(toscaComponentName, componentParentArtifacts);
844         }
845
846         //3. find the artifacts specific to the instance
847         Map<String, List<ArtifactDefinition>> componentInstanceSpecificInformationalArtifacts =
848             getComponentInstanceSpecificArtifacts(componentInstance.getArtifacts(),
849                 componentParentArtifacts.getComponentArtifacts().getArtifactsInfo(), ArtifactGroupTypeEnum.INFORMATIONAL);
850         Map<String, List<ArtifactDefinition>> componentInstanceSpecificDeploymentArtifacts =
851             getComponentInstanceSpecificArtifacts(componentInstance.getDeploymentArtifacts(),
852                 componentParentArtifacts.getComponentArtifacts().getArtifactsInfo(), ArtifactGroupTypeEnum.DEPLOYMENT);
853
854         //4. add the instances artifacts to the component type
855         ArtifactsInfo artifactsInfo = new ArtifactsInfo();
856         if (!componentInstanceSpecificInformationalArtifacts.isEmpty()) {
857             artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.INFORMATIONAL, componentInstanceSpecificInformationalArtifacts);
858         }
859         if (!componentInstanceSpecificDeploymentArtifacts.isEmpty()) {
860             artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.DEPLOYMENT, componentInstanceSpecificDeploymentArtifacts);
861         }
862         if (!artifactsInfo.isEmpty()) {
863             instanceArtifactsLocation.addComponentInstancesArtifacts(componentInstance.getNormalizedName(), artifactsInfo);
864         }
865
866         //5. do the same for all the component instances
867         List<ComponentInstance> componentInstances = fetchedComponent.getComponentInstances();
868         if (componentInstances != null) {
869             for (ComponentInstance childComponentInstance : componentInstances) {
870                 Either<Boolean, ResponseFormat> collectComponentInstanceArtifacts = collectComponentInstanceArtifacts(
871                     fetchedComponent, childComponentInstance, resourcesTypeArtifacts, componentParentArtifacts);
872                 if (collectComponentInstanceArtifacts.isRight()) {
873                     return collectComponentInstanceArtifacts;
874                 }
875             }
876         }
877
878         return Either.left(true);
879     }
880
881     private Map<String, List<ArtifactDefinition>> getComponentInstanceSpecificArtifacts(Map<String, ArtifactDefinition> componentArtifacts,
882                                                                                         Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> componentTypeArtifacts,
883                                                                                         ArtifactGroupTypeEnum artifactGroupTypeEnum) {
884         Map<String, List<ArtifactDefinition>> parentArtifacts = componentTypeArtifacts
885             .get(artifactGroupTypeEnum);    //the artfiacts of the component itself and not the instance
886
887         Map<String, List<ArtifactDefinition>> artifactsByTypeOfComponentInstance = new HashMap<>();
888         if (componentArtifacts != null) {
889             for (ArtifactDefinition artifact : componentArtifacts.values()) {
890                 List<ArtifactDefinition> parentArtifactsByType = null;
891                 if (parentArtifacts != null) {
892                     parentArtifactsByType = parentArtifacts.get(artifact.getArtifactType());
893                 }
894                 //the artifact is of instance
895                 if (parentArtifactsByType == null || !parentArtifactsByType.contains(artifact)) {
896                     List<ArtifactDefinition> typeArtifacts = artifactsByTypeOfComponentInstance.get(artifact.getArtifactType());
897                     if (typeArtifacts == null) {
898                         typeArtifacts = new ArrayList<>();
899                         artifactsByTypeOfComponentInstance.put(artifact.getArtifactType(), typeArtifacts);
900                     }
901                     typeArtifacts.add(artifact);
902                 }
903             }
904         }
905
906         return artifactsByTypeOfComponentInstance;
907     }
908
909     private ComponentTypeArtifacts collectComponentTypeArtifacts(Component fetchedComponent) {
910         ArtifactsInfo componentArtifacts = collectComponentArtifacts(fetchedComponent);
911         ComponentTypeArtifacts componentArtifactsInfo = new ComponentTypeArtifacts();
912         if (componentArtifacts.isNotEmpty()) {
913             componentArtifactsInfo.setComponentArtifacts(componentArtifacts);
914         }
915         return componentArtifactsInfo;
916     }
917
918     private ArtifactsInfo collectComponentArtifacts(Component component) {
919         Map<String, ArtifactDefinition> informationalArtifacts = component.getArtifacts();
920         Map<String, List<ArtifactDefinition>> informationalArtifactsByType = collectGroupArtifacts(informationalArtifacts);
921         Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
922         Map<String, List<ArtifactDefinition>> deploymentArtifactsByType = collectGroupArtifacts(deploymentArtifacts);
923         ArtifactsInfo artifactsInfo = new ArtifactsInfo();
924         if (!informationalArtifactsByType.isEmpty()) {
925             artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.INFORMATIONAL, informationalArtifactsByType);
926         }
927         if (!deploymentArtifactsByType.isEmpty()) {
928             artifactsInfo.addArtifactsToGroup(ArtifactGroupTypeEnum.DEPLOYMENT, deploymentArtifactsByType);
929         }
930
931         return artifactsInfo;
932     }
933
934     private Map<String, List<ArtifactDefinition>> collectGroupArtifacts(
935         final Map<String, ArtifactDefinition> componentArtifacts) {
936         final Map<String, List<ArtifactDefinition>> artifactsByType = new HashMap<>();
937         for (final ArtifactDefinition artifact : componentArtifacts.values()) {
938             if (artifact.getArtifactUUID() != null) {
939                 artifactsByType.putIfAbsent(artifact.getArtifactType(), new ArrayList<>());
940                 final List<ArtifactDefinition> typeArtifacts = artifactsByType.get(artifact.getArtifactType());
941                 typeArtifacts.add(artifact);
942             }
943         }
944         return artifactsByType;
945     }
946
947     private Either<ZipOutputStream, ResponseFormat> writeAllFilesToCsar(Component mainComponent, CsarDefinition csarDefinition,
948                                                                         ZipOutputStream zipstream, boolean isInCertificationRequest)
949         throws IOException {
950         ComponentArtifacts componentArtifacts = csarDefinition.getComponentArtifacts();
951         Either<ZipOutputStream, ResponseFormat> writeComponentArtifactsToSpecifiedPath = writeComponentArtifactsToSpecifiedPath(mainComponent,
952             componentArtifacts, zipstream, ARTIFACTS_PATH, isInCertificationRequest);
953         if (writeComponentArtifactsToSpecifiedPath.isRight()) {
954             return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
955         }
956         ComponentTypeArtifacts mainTypeAndCIArtifacts = componentArtifacts.getMainTypeAndCIArtifacts();
957         writeComponentArtifactsToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent, mainTypeAndCIArtifacts.getComponentArtifacts(),
958             zipstream, ARTIFACTS_PATH, isInCertificationRequest);
959         if (writeComponentArtifactsToSpecifiedPath.isRight()) {
960             return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
961         }
962         Map<String, ArtifactsInfo> componentInstancesArtifacts = mainTypeAndCIArtifacts.getComponentInstancesArtifacts();
963         String currentPath = ARTIFACTS_PATH + RESOURCES_PATH;
964         for (String keyAssetName : componentInstancesArtifacts.keySet()) {
965             ArtifactsInfo artifactsInfo = componentInstancesArtifacts.get(keyAssetName);
966             String pathWithAssetName = currentPath + keyAssetName + PATH_DELIMITER;
967             writeComponentArtifactsToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent, artifactsInfo, zipstream, pathWithAssetName,
968                 isInCertificationRequest);
969             if (writeComponentArtifactsToSpecifiedPath.isRight()) {
970                 return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
971             }
972         }
973         writeComponentArtifactsToSpecifiedPath = writeOperationsArtifactsToCsar(mainComponent, zipstream);
974         if (writeComponentArtifactsToSpecifiedPath.isRight()) {
975             return Either.right(writeComponentArtifactsToSpecifiedPath.right().value());
976         }
977         return Either.left(zipstream);
978     }
979
980     private Either<ZipOutputStream, ResponseFormat> writeOperationsArtifactsToCsar(Component component, ZipOutputStream zipstream) {
981         if (checkComponentBeforeOperation(component)) {
982             return Either.left(zipstream);
983         }
984         for (Map.Entry<String, InterfaceDefinition> interfaceEntry : ((Resource) component).getInterfaces().entrySet()) {
985             for (OperationDataDefinition operation : interfaceEntry.getValue().getOperations().values()) {
986                 try {
987                     if (checkComponentBeforeWrite(component, interfaceEntry, operation)) {
988                         continue;
989                     }
990                     final String artifactUUID = operation.getImplementation().getArtifactUUID();
991                     if (artifactUUID == null) {
992                         continue;
993                     }
994                     final Either<byte[], ActionStatus> artifactFromCassandra = getFromCassandra(artifactUUID);
995                     final String artifactName = operation.getImplementation().getArtifactName();
996                     if (artifactFromCassandra.isRight()) {
997                         LOGGER.error(ARTIFACT_NAME_UNIQUE_ID, artifactName, artifactUUID);
998                         LOGGER.error("Failed to get {} payload from DB reason: {}", artifactName, artifactFromCassandra.right().value());
999                         return Either.right(componentsUtils.getResponseFormat(
1000                             ARTIFACT_PAYLOAD_NOT_FOUND_DURING_CSAR_CREATION, "Resource", component.getUniqueId(), artifactName, artifactUUID));
1001                     }
1002                     zipstream.putNextEntry(new ZipEntry(OperationArtifactUtil.createOperationArtifactPath(component, null, operation, true)));
1003                     zipstream.write(artifactFromCassandra.left().value());
1004                 } catch (IOException e) {
1005                     LOGGER.error("Component Name {},  Interface Name {}, Operation Name {}", component.getNormalizedName(), interfaceEntry.getKey(),
1006                         operation.getName());
1007                     LOGGER.error("Error while writing the operation's artifacts to the CSAR", e);
1008                     return Either.right(componentsUtils.getResponseFormat(ERROR_DURING_CSAR_CREATION, "Resource", component.getUniqueId()));
1009                 }
1010             }
1011         }
1012         return Either.left(zipstream);
1013     }
1014
1015     private boolean checkComponentBeforeWrite(Component component, Map.Entry<String, InterfaceDefinition> interfaceEntry,
1016                                               OperationDataDefinition operation) {
1017         final ArtifactDataDefinition implementation = operation.getImplementation();
1018         if (implementation == null) {
1019             LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - no Operation Implementation found", component.getNormalizedName(),
1020                 interfaceEntry.getValue().getUniqueId(), operation.getName());
1021             return true;
1022         }
1023         final String artifactName = implementation.getArtifactName();
1024         if (artifactName == null) {
1025             LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - no artifact found", component.getNormalizedName(),
1026                 interfaceEntry.getValue().getUniqueId(), operation.getName());
1027             return true;
1028         }
1029         if (OperationArtifactUtil.artifactNameIsALiteralValue(artifactName)) {
1030             LOGGER.debug("Component Name {}, Interface Id {}, Operation Name {} - artifact name is a literal value rather than an SDC artifact",
1031                 component.getNormalizedName(), interfaceEntry.getValue().getUniqueId(), operation.getName());
1032             return true;
1033         }
1034         return false;
1035     }
1036
1037     private boolean checkComponentBeforeOperation(Component component) {
1038         if (component instanceof Service) {
1039             return true;
1040         }
1041         if (Objects.isNull(((Resource) component).getInterfaces())) {
1042             LOGGER.debug("Component Name {}- no interfaces found", component.getNormalizedName());
1043             return true;
1044         }
1045         return false;
1046     }
1047
1048     private Either<ZipOutputStream, ResponseFormat> writeArtifactsInfoToSpecifiedPath(final Component mainComponent,
1049                                                                                       final ArtifactsInfo currArtifactsInfo,
1050                                                                                       final ZipOutputStream zip, final String path,
1051                                                                                       final boolean isInCertificationRequest) throws IOException {
1052         final Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactsInfo = currArtifactsInfo.getArtifactsInfo();
1053         for (final ArtifactGroupTypeEnum artifactGroupTypeEnum : artifactsInfo.keySet()) {
1054             final String groupTypeFolder = path + WordUtils.capitalizeFully(artifactGroupTypeEnum.getType()) + PATH_DELIMITER;
1055             final Map<String, List<ArtifactDefinition>> artifactTypesMap = artifactsInfo.get(artifactGroupTypeEnum);
1056             for (final String artifactType : artifactTypesMap.keySet()) {
1057                 final List<ArtifactDefinition> artifactDefinitionList = artifactTypesMap.get(artifactType);
1058                 String artifactTypeFolder = groupTypeFolder + artifactType + PATH_DELIMITER;
1059                 if (ArtifactTypeEnum.WORKFLOW.getType().equals(artifactType) && path.contains(ARTIFACTS_PATH + RESOURCES_PATH)) {
1060                     // Ignore this packaging as BPMN artifacts needs to be packaged in different manner
1061                     continue;
1062                 }
1063                 if (ArtifactTypeEnum.WORKFLOW.getType().equals(artifactType)) {
1064                     artifactTypeFolder += OperationArtifactUtil.BPMN_ARTIFACT_PATH + File.separator;
1065                 } else if (ArtifactTypeEnum.ONBOARDED_PACKAGE.getType().equals(artifactType)) {
1066                     // renaming legacy folder ONBOARDED_PACKAGE to the new folder ETSI_PACKAGE
1067                     artifactTypeFolder = artifactTypeFolder
1068                         .replace(ArtifactTypeEnum.ONBOARDED_PACKAGE.getType(), ArtifactTypeEnum.ETSI_PACKAGE.getType());
1069                 }
1070                 // TODO: We should not do this but in order to keep this refactoring small enough,
1071
1072                 // we'll leave this as is for now
1073                 List<ArtifactDefinition> collect = filterArtifactDefinitionToZip(mainComponent, artifactDefinitionList, isInCertificationRequest)
1074                     .collect(Collectors.toList());
1075                 for (ArtifactDefinition ad : collect) {
1076                     zip.putNextEntry(new ZipEntry(artifactTypeFolder + ad.getArtifactName()));
1077                     zip.write(ad.getPayloadData());
1078                 }
1079             }
1080         }
1081         return Either.left(zip);
1082     }
1083
1084     private Stream<ArtifactDefinition> filterArtifactDefinitionToZip(Component mainComponent, List<ArtifactDefinition> artifactDefinitionList,
1085                                                                      boolean isInCertificationRequest) {
1086         return artifactDefinitionList.stream().filter(shouldBeInZip(isInCertificationRequest, mainComponent)).map(this::fetchPayLoadData)
1087             .filter(Either::isLeft).map(e -> e.left().value());
1088     }
1089
1090     private Predicate<ArtifactDefinition> shouldBeInZip(boolean isInCertificationRequest, Component component) {
1091         return artifactDefinition -> !(!isInCertificationRequest && component.isService() && artifactDefinition.isHeatEnvType() || artifactDefinition
1092             .hasNoMandatoryEsId());
1093     }
1094
1095     private Either<ArtifactDefinition, ActionStatus> fetchPayLoadData(ArtifactDefinition ad) {
1096         byte[] payloadData = ad.getPayloadData();
1097         if (payloadData == null) {
1098             return getFromCassandra(ad.getEsId()).left().map(pd -> {
1099                 ad.setPayload(pd);
1100                 return ad;
1101             }).right().map(as -> {
1102                 LOGGER.debug(ARTIFACT_NAME_UNIQUE_ID, ad.getArtifactName(), ad.getUniqueId());
1103                 LOGGER.debug("Failed to get {} payload from DB reason: {}", ad.getArtifactName(), as);
1104                 return as;
1105             });
1106         } else {
1107             return Either.left(ad);
1108         }
1109     }
1110
1111     private Either<ZipOutputStream, ResponseFormat> writeComponentArtifactsToSpecifiedPath(Component mainComponent,
1112                                                                                            ComponentArtifacts componentArtifacts,
1113                                                                                            ZipOutputStream zipstream, String currentPath,
1114                                                                                            boolean isInCertificationRequest) throws IOException {
1115         Map<String, ComponentTypeArtifacts> componentTypeArtifacts = componentArtifacts.getComponentTypeArtifacts();
1116         //Keys are defined:
1117
1118         //<Inner Asset TOSCA name (e.g. VFC name)> folder name: <Inner Asset TOSCA name (e.g. VFC name)>_v<version>.
1119
1120         //E.g. "org.openecomp.resource.vf.vipr_atm_v1.0"
1121         Set<String> componentTypeArtifactsKeys = componentTypeArtifacts.keySet();
1122         for (String keyAssetName : componentTypeArtifactsKeys) {
1123             ComponentTypeArtifacts componentInstanceArtifacts = componentTypeArtifacts.get(keyAssetName);
1124             ArtifactsInfo componentArtifacts2 = componentInstanceArtifacts.getComponentArtifacts();
1125             String pathWithAssetName = currentPath + keyAssetName + PATH_DELIMITER;
1126             Either<ZipOutputStream, ResponseFormat> writeArtifactsInfoToSpecifiedPath = writeArtifactsInfoToSpecifiedPath(mainComponent,
1127                 componentArtifacts2, zipstream, pathWithAssetName, isInCertificationRequest);
1128             if (writeArtifactsInfoToSpecifiedPath.isRight()) {
1129                 return writeArtifactsInfoToSpecifiedPath;
1130             }
1131         }
1132         return Either.left(zipstream);
1133     }
1134
1135     private Either<ToscaRepresentation, ResponseFormat> generateToscaRepresentation(Component component, boolean isSkipImports) {
1136         return toscaExportUtils.exportComponent(component, isSkipImports).right().map(toscaError -> {
1137             LOGGER.debug("exportComponent failed {}", toscaError);
1138             return componentsUtils.getResponseFormat(componentsUtils.convertFromToscaError(toscaError));
1139         });
1140     }
1141
1142     private String createToscaBlock0(String metaFileVersion, String csarVersion, String createdBy, String entryDef, boolean isAsdPackage,
1143                                      String definitionsPath) {
1144         final String block0template = "TOSCA-Meta-File-Version: %s\nCSAR-Version: %s\nCreated-By: %s\nEntry-Definitions: "
1145             + definitionsPath + "%s\n%s\nName: csar.meta\nContent-Type: text/plain\n";
1146         return String.format(block0template, metaFileVersion, csarVersion, createdBy, entryDef, isAsdPackage ? "entry_definition_type: asd" : "");
1147     }
1148
1149     private class CsarDefinition {
1150
1151         private ComponentArtifacts componentArtifacts;
1152
1153         // add list of tosca artifacts and meta describes CSAR zip root
1154         public CsarDefinition(ComponentArtifacts componentArtifacts) {
1155             this.componentArtifacts = componentArtifacts;
1156         }
1157
1158         public ComponentArtifacts getComponentArtifacts() {
1159             return componentArtifacts;
1160         }
1161     }
1162
1163     private class ComponentArtifacts {
1164
1165         //artifacts of the component and CI's artifacts contained in it's composition (represents Informational, Deployment & Resource folders of main component)
1166         private ComponentTypeArtifacts mainTypeAndCIArtifacts;
1167         //artifacts of all component types mapped by their tosca name
1168         private Map<String, ComponentTypeArtifacts> componentTypeArtifacts;
1169
1170         public ComponentArtifacts() {
1171             mainTypeAndCIArtifacts = new ComponentTypeArtifacts();
1172             componentTypeArtifacts = new HashMap<>();
1173         }
1174
1175         public ComponentTypeArtifacts getMainTypeAndCIArtifacts() {
1176             return mainTypeAndCIArtifacts;
1177         }
1178
1179         public void setMainTypeAndCIArtifacts(ComponentTypeArtifacts componentInstanceArtifacts) {
1180             this.mainTypeAndCIArtifacts = componentInstanceArtifacts;
1181         }
1182
1183         public Map<String, ComponentTypeArtifacts> getComponentTypeArtifacts() {
1184             return componentTypeArtifacts;
1185         }
1186     }
1187
1188     /**
1189      * The artifacts of the component and of all its composed instances
1190      */
1191     private class ComponentTypeArtifacts {
1192
1193         private ArtifactsInfo componentArtifacts;    //component artifacts (describes the Informational Deployment folders)
1194
1195         private Map<String, ArtifactsInfo> componentInstancesArtifacts;        //artifacts of the composed instances mapped by the resourceInstance normalized name (describes the Resources folder)
1196
1197         public ComponentTypeArtifacts() {
1198             componentArtifacts = new ArtifactsInfo();
1199             componentInstancesArtifacts = new HashMap<>();
1200         }
1201
1202         public ArtifactsInfo getComponentArtifacts() {
1203             return componentArtifacts;
1204         }
1205
1206         public void setComponentArtifacts(ArtifactsInfo artifactsInfo) {
1207             this.componentArtifacts = artifactsInfo;
1208         }
1209
1210         public Map<String, ArtifactsInfo> getComponentInstancesArtifacts() {
1211             return componentInstancesArtifacts;
1212         }
1213
1214         public void addComponentInstancesArtifacts(String normalizedName, ArtifactsInfo artifactsInfo) {
1215             componentInstancesArtifacts.put(normalizedName, artifactsInfo);
1216         }
1217     }
1218
1219     /**
1220      * The artifacts Definition saved by their structure
1221      */
1222     private class ArtifactsInfo {
1223         //Key is the type of artifacts(Informational/Deployment)
1224
1225         //Value is a map between an artifact type and a list of all artifacts of this type
1226         private Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> artifactsInfoField;
1227
1228         public ArtifactsInfo() {
1229             this.artifactsInfoField = new EnumMap<>(ArtifactGroupTypeEnum.class);
1230         }
1231
1232         public Map<ArtifactGroupTypeEnum, Map<String, List<ArtifactDefinition>>> getArtifactsInfo() {
1233             return artifactsInfoField;
1234         }
1235
1236         public void addArtifactsToGroup(ArtifactGroupTypeEnum artifactGroup, Map<String, List<ArtifactDefinition>> artifactsDefinition) {
1237             if (artifactsInfoField.get(artifactGroup) == null) {
1238                 artifactsInfoField.put(artifactGroup, artifactsDefinition);
1239             } else {
1240                 Map<String, List<ArtifactDefinition>> artifactTypeEnumListMap = artifactsInfoField.get(artifactGroup);
1241                 artifactTypeEnumListMap.putAll(artifactsDefinition);
1242                 artifactsInfoField.put(artifactGroup, artifactTypeEnumListMap);
1243             }
1244         }
1245
1246         public boolean isEmpty() {
1247             return artifactsInfoField.isEmpty();
1248         }
1249
1250         public boolean isNotEmpty() {
1251             return !isEmpty();
1252         }
1253     }
1254
1255     public static class ToscaErrorException extends Exception {
1256
1257         ToscaErrorException(ToscaError error) {
1258             super("Error while exporting component's interface (toscaError:" + error + ")");
1259         }
1260     }
1261
1262 }