7c11fb65ab5a7133eda7cf13f1f214eb5edd410f
[sdc.git] /
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  * Modifications copyright (c) 2021 Nokia
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.openecomp.sdc.vendorsoftwareproduct.services.impl.filedatastructuremodule;
19
20 import static org.openecomp.core.validation.errors.ErrorMessagesFormatBuilder.getErrorWithParameters;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.ByteBuffer;
27 import java.nio.charset.StandardCharsets;
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36 import java.util.zip.ZipEntry;
37 import java.util.zip.ZipInputStream;
38 import java.util.zip.ZipOutputStream;
39 import org.apache.commons.collections4.CollectionUtils;
40 import org.openecomp.core.utilities.file.FileContentHandler;
41 import org.openecomp.core.utilities.json.JsonUtil;
42 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
43 import org.openecomp.sdc.common.errors.CoreException;
44 import org.openecomp.sdc.common.errors.ErrorCategory;
45 import org.openecomp.sdc.common.errors.ErrorCode;
46 import org.openecomp.sdc.common.errors.Messages;
47 import org.openecomp.sdc.common.utils.SdcCommon;
48 import org.openecomp.sdc.common.zip.ZipUtils;
49 import org.openecomp.sdc.common.zip.exception.ZipSlipException;
50 import org.openecomp.sdc.datatypes.error.ErrorLevel;
51 import org.openecomp.sdc.datatypes.error.ErrorMessage;
52 import org.openecomp.sdc.heat.datatypes.manifest.FileData;
53 import org.openecomp.sdc.heat.datatypes.manifest.ManifestContent;
54 import org.openecomp.sdc.heat.datatypes.structure.Artifact;
55 import org.openecomp.sdc.heat.datatypes.structure.HeatStructureTree;
56 import org.openecomp.sdc.heat.datatypes.structure.ValidationStructureList;
57 import org.openecomp.sdc.vendorsoftwareproduct.dao.OrchestrationTemplateCandidateDao;
58 import org.openecomp.sdc.vendorsoftwareproduct.dao.OrchestrationTemplateCandidateDaoFactory;
59 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.OrchestrationTemplateCandidateData;
60 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspDetails;
61 import org.openecomp.sdc.vendorsoftwareproduct.errors.utils.ErrorsUtil;
62 import org.openecomp.sdc.vendorsoftwareproduct.services.HeatFileAnalyzer;
63 import org.openecomp.sdc.vendorsoftwareproduct.services.filedatastructuremodule.CandidateService;
64 import org.openecomp.sdc.vendorsoftwareproduct.services.filedatastructuremodule.ManifestCreator;
65 import org.openecomp.sdc.vendorsoftwareproduct.services.utils.CandidateServiceValidator;
66 import org.openecomp.sdc.vendorsoftwareproduct.types.CandidateDataEntityTo;
67 import org.openecomp.sdc.vendorsoftwareproduct.types.candidateheat.AnalyzedZipHeatFiles;
68 import org.openecomp.sdc.vendorsoftwareproduct.types.candidateheat.FilesDataStructure;
69 import org.openecomp.sdc.vendorsoftwareproduct.types.candidateheat.Module;
70 import org.openecomp.sdc.versioning.dao.types.Version;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 public class CandidateServiceImpl implements CandidateService {
75   private static final Logger logger = LoggerFactory.getLogger(CandidateServiceImpl.class);
76   private CandidateServiceValidator candidateServiceValidator = new CandidateServiceValidator();
77   private ManifestCreator manifestCreator;
78   private OrchestrationTemplateCandidateDao orchestrationTemplateCandidateDao;
79
80   public CandidateServiceImpl(ManifestCreator manifestCreator,
81                               OrchestrationTemplateCandidateDao orchestrationTemplateCandidateDao) {
82     this.manifestCreator = manifestCreator;
83     this.orchestrationTemplateCandidateDao = orchestrationTemplateCandidateDao;
84   }
85
86   public CandidateServiceImpl() {
87   }
88
89   @Override
90   public Optional<ErrorMessage> validateNonEmptyFileToUpload(InputStream fileToUpload,
91                                                              String fileSuffix) {
92     String errorMessage =
93         getErrorWithParameters(Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(),
94             fileSuffix);
95
96     if (Objects.isNull(fileToUpload)) {
97       return Optional.of(new ErrorMessage(ErrorLevel.ERROR,
98           errorMessage));
99     } else {
100       try {
101         int available = fileToUpload.available();
102         if (available == 0) {
103           return Optional.of(new ErrorMessage(ErrorLevel.ERROR,
104               errorMessage));
105         }
106       } catch (IOException e) {
107         logger.debug(e.getMessage(), e);
108         return Optional.of(new ErrorMessage(ErrorLevel.ERROR,
109             errorMessage));
110       }
111     }
112     return Optional.empty();
113   }
114
115   @Override
116   public Optional<ErrorMessage> validateRawZipData(String fileSuffix,
117                                                    byte[] uploadedFileData) {
118     if (Objects.isNull(uploadedFileData)) {
119       return Optional.of(new ErrorMessage(ErrorLevel.ERROR,
120           getErrorWithParameters(Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(),
121               fileSuffix)));
122     }
123     return Optional.empty();
124   }
125
126   private String heatStructureTreeToFileDataStructure(HeatStructureTree tree,
127                                                       FileContentHandler zipContentMap,
128                                                       Map<String, List<ErrorMessage>> uploadErrors,
129                                                       AnalyzedZipHeatFiles analyzedZipHeatFiles) {
130     FilesDataStructure structure = new FilesDataStructure();
131     Set<String> usedEnvFiles = new HashSet<>();
132     addHeatsToFileDataStructure(tree, usedEnvFiles, structure, uploadErrors,
133         analyzedZipHeatFiles);
134     handleOtherResources(tree, usedEnvFiles, structure);
135     FilesDataStructure fileDataStructureFromManifest =
136         createFileDataStructureFromManifest(zipContentMap.getFileContentAsStream(SdcCommon.MANIFEST_NAME));
137     List<String> structureArtifacts = structure.getArtifacts();
138     structureArtifacts.addAll(fileDataStructureFromManifest.getArtifacts().stream().filter
139         (artifact -> isNotStrctureArtifact(structureArtifacts, artifact))
140         .collect(Collectors.toList()));
141     handleArtifactsFromTree(tree, structure);
142
143     return JsonUtil.object2Json(structure);
144   }
145
146   private boolean isNotStrctureArtifact(List<String> structureArtifacts, String artifact) {
147     return !structureArtifacts.contains(artifact);
148   }
149
150   @Override
151   public OrchestrationTemplateCandidateData createCandidateDataEntity(
152       CandidateDataEntityTo candidateDataEntityTo, InputStream zipFileManifest,
153       AnalyzedZipHeatFiles analyzedZipHeatFiles) {
154     FileContentHandler zipContentMap = candidateDataEntityTo.getContentMap();
155     FilesDataStructure filesDataStructure;
156     String dataStructureJson;
157
158     if (zipFileManifest != null) {
159       // create data structure from manifest
160       filesDataStructure = createFileDataStructureFromManifest(zipFileManifest);
161       Set<String> zipFileList = zipContentMap.getFileList();
162       balanceManifestFilesWithZipFiles(filesDataStructure,
163           zipContentMap, analyzedZipHeatFiles);
164       Set<String> filesDataStructureFiles = getFlatFileNames(filesDataStructure);
165       filesDataStructure.getUnassigned().addAll(zipFileList.stream()
166           .filter(fileName -> (!filesDataStructureFiles.contains(fileName)
167               && !filesDataStructure.getNested().contains(fileName)
168               && !fileName.equals(SdcCommon.MANIFEST_NAME)))
169           .collect(Collectors.toList()));
170       dataStructureJson = JsonUtil.object2Json(filesDataStructure);
171     } else {
172       // create data structure from based on naming convention
173       dataStructureJson =
174           heatStructureTreeToFileDataStructure(candidateDataEntityTo.getTree(), zipContentMap,
175               candidateDataEntityTo.getErrors(), analyzedZipHeatFiles);
176     }
177
178     OrchestrationTemplateCandidateData candidateData = new OrchestrationTemplateCandidateData();
179     candidateData.setContentData(ByteBuffer.wrap(candidateDataEntityTo.getUploadedFileData()));
180     candidateData.setFilesDataStructure(dataStructureJson);
181     return candidateData;
182   }
183
184   private void balanceManifestFilesWithZipFiles(
185       FilesDataStructure filesDataStructure,
186       FileContentHandler fileContentHandler, AnalyzedZipHeatFiles analyzedZipHeatFiles) {
187     Set<String> zipFileList = fileContentHandler.getFileList();
188     filesDataStructure.getNested().addAll(analyzedZipHeatFiles.getNestedFiles());
189     List<Module> modules = filesDataStructure.getModules();
190     if (CollectionUtils.isEmpty(modules)) {
191       return;
192     }
193
194     for (int i = 0; i < modules.size(); i++) {
195       Module module = modules.get(i);
196       if (!isFileExistInZipContains(zipFileList, module.getYaml())) {
197         addFileToUnassigned(filesDataStructure, zipFileList, module.getEnv());
198         addFileToUnassigned(filesDataStructure, zipFileList, module.getVol());
199         addFileToUnassigned(filesDataStructure, zipFileList, module.getVolEnv());
200         modules.remove(i--);
201       } else if (Objects.nonNull(module.getVol()) && !zipFileList.contains(module.getVol())) {
202         module.setVol(null);
203         CollectionUtils
204             .addIgnoreNull(filesDataStructure.getUnassigned(), module.getVolEnv());
205       } else {
206         if (filesDataStructure.getNested().contains(module.getYaml())) {
207           moveModuleFileToNested(filesDataStructure, i--, module);
208         }
209       }
210     }
211   }
212
213   private void addFileToUnassigned(FilesDataStructure filesDataStructure, Set<String> zipFileList,
214                                    String fileName) {
215     if (isFileExistInZipContains(zipFileList, fileName)) {
216       filesDataStructure.getUnassigned().add(fileName);
217     }
218   }
219
220   private boolean isFileExistInZipContains(Set<String> zipFileList, String fileName) {
221     return Objects.nonNull(fileName) && zipFileList.contains(fileName);
222   }
223
224   private void moveModuleFileToNested(FilesDataStructure filesDataStructure, int i,
225                                       Module module) {
226     if (!filesDataStructure.getNested().contains(module.getYaml())) {
227       filesDataStructure.getNested().add(module.getYaml());
228     }
229     if (Objects.nonNull(module.getEnv())) {
230       filesDataStructure.getNested().add(module.getEnv());
231     }
232     if (Objects.nonNull(module.getVol())) {
233       filesDataStructure.getNested().add(module.getVol());
234     }
235     if (Objects.nonNull(module.getVolEnv())) {
236       filesDataStructure.getNested().add(module.getVolEnv());
237     }
238     filesDataStructure.getModules().remove(i);
239   }
240
241   private Set<String> getFlatFileNames(FilesDataStructure filesDataStructure) {
242     Set<String> fileNames = new HashSet<>();
243     if (!CollectionUtils.isEmpty(filesDataStructure.getModules())) {
244       for (Module module : filesDataStructure.getModules()) {
245         CollectionUtils.addIgnoreNull(fileNames, module.getEnv());
246         CollectionUtils.addIgnoreNull(fileNames, module.getVol());
247         CollectionUtils.addIgnoreNull(fileNames, module.getVolEnv());
248         CollectionUtils.addIgnoreNull(fileNames, module.getYaml());
249       }
250     }
251     fileNames.addAll(filesDataStructure.getArtifacts());
252     fileNames.addAll(filesDataStructure.getNested());
253     fileNames.addAll(filesDataStructure.getUnassigned());
254
255     return fileNames;
256   }
257
258   private FilesDataStructure createFileDataStructureFromManifest(InputStream isManifestContent) {
259     ManifestContent manifestContent =
260         JsonUtil.json2Object(isManifestContent, ManifestContent.class);
261     FilesDataStructure structure = new FilesDataStructure();
262     for (FileData fileData : manifestContent.getData()) {
263       if (Objects.nonNull(fileData.getType()) &&
264           fileData.getType().equals(FileData.Type.HEAT)) {
265         Module module = new Module();
266         module.setType(FileData.Type.HEAT);
267         module.setYaml(fileData.getFile());
268         module.setIsBase(fileData.getBase());
269         addHeatDependenciesToModule(module, fileData.getData());
270         structure.getModules().add(module);
271       }else if (Objects.nonNull(fileData.getType()) &&
272               fileData.getType().equals(FileData.Type.HELM)) {
273         Module module = new Module();
274         module.setType(FileData.Type.HELM);
275         module.setYaml(fileData.getFile());
276         module.setIsBase(fileData.getBase());
277         structure.getModules().add(module);
278       }
279       else if (HeatFileAnalyzer.isYamlOrEnvFile(fileData.getFile()) &&
280           !FileData.Type.isArtifact(fileData.getType())) {
281         structure.getUnassigned().add(fileData.getFile());
282       } else {
283         structure.getArtifacts().add(fileData.getFile());
284       }
285     }
286     return structure;
287   }
288
289   private void addHeatDependenciesToModule(Module module, List<FileData> data) {
290     if (CollectionUtils.isEmpty(data)) {
291       return;
292     }
293
294     for (FileData fileData : data) {
295       if (fileData.getType().equals(FileData.Type.HEAT_ENV)) {
296         module.setEnv(fileData.getFile());
297       } else if (fileData.getType().equals(FileData.Type.HEAT_VOL)) { // must be volume
298         module.setVol(fileData.getFile());
299         if (!CollectionUtils.isEmpty(fileData.getData())) {
300           FileData volEnv = fileData.getData().get(0);
301           if (volEnv.getType().equals(FileData.Type.HEAT_ENV)) {
302             module.setVolEnv(volEnv.getFile());
303           } else {
304             throw new CoreException((new ErrorCode.ErrorCodeBuilder())
305                 .withMessage(Messages.ILLEGAL_MANIFEST.getErrorMessage())
306                 .withId(Messages.ILLEGAL_MANIFEST.getErrorMessage())
307                 .withCategory(ErrorCategory.APPLICATION).build());
308           }
309         }
310       } else {
311         throw new CoreException((new ErrorCode.ErrorCodeBuilder())
312             .withMessage(Messages.FILE_TYPE_NOT_LEGAL.getErrorMessage())
313             .withId(Messages.FILE_TYPE_NOT_LEGAL.getErrorMessage())
314             .withCategory(ErrorCategory.APPLICATION).build());
315       }
316     }
317   }
318
319   @Override
320   public void updateCandidateUploadData(final String vspId, final Version version,
321                                         final OrchestrationTemplateCandidateData uploadData) {
322     orchestrationTemplateCandidateDao.update(vspId, version, uploadData);
323   }
324
325   @Override
326   public Optional<FilesDataStructure> getOrchestrationTemplateCandidateFileDataStructure(
327       String vspId, Version version) {
328     Optional<String> jsonFileDataStructure =
329         orchestrationTemplateCandidateDao.getStructure(vspId, version);
330
331     if (jsonFileDataStructure.isPresent() && JsonUtil.isValidJson(jsonFileDataStructure.get())) {
332       return Optional
333           .of(JsonUtil.json2Object(jsonFileDataStructure.get(), FilesDataStructure.class));
334     } else {
335       return Optional.empty();
336     }
337   }
338
339   @Override
340   public void updateOrchestrationTemplateCandidateFileDataStructure(String vspId, Version version,
341                                                                     FilesDataStructure fileDataStructure) {
342     OrchestrationTemplateCandidateDaoFactory.getInstance().createInterface()
343         .updateStructure(vspId, version, fileDataStructure);
344   }
345
346   @Override
347   public Optional<OrchestrationTemplateCandidateData> getOrchestrationTemplateCandidate(String vspId,
348                                                                                         Version version) {
349     return orchestrationTemplateCandidateDao.get(vspId, version);
350   }
351
352   @Override
353   public Optional<OrchestrationTemplateCandidateData> getOrchestrationTemplateCandidateInfo(
354       String vspId,
355       Version version) {
356     return orchestrationTemplateCandidateDao.getInfo(vspId, version);
357   }
358
359   @Override
360   public String createManifest(VspDetails vspDetails, FilesDataStructure structure) {
361     return JsonUtil.object2Json(manifestCreator.createManifest(vspDetails, structure)
362         .orElseThrow(() -> new CoreException(new ErrorCode.ErrorCodeBuilder()
363             .withMessage(Messages.CREATE_MANIFEST_FROM_ZIP.getErrorMessage()).build())));
364   }
365
366   @Override
367   public String createManifestFromExisting(VspDetails vspDetails, FilesDataStructure structure, ManifestContent existingManifest) {
368     return JsonUtil.object2Json(manifestCreator.createManifestFromExisting(vspDetails, structure, existingManifest)
369             .orElseThrow(() -> new CoreException(new ErrorCode.ErrorCodeBuilder()
370                     .withMessage(Messages.CREATE_MANIFEST_FROM_ZIP.getErrorMessage()).build())));
371   }
372
373   @Override
374   public Optional<ManifestContent> createManifest(VspDetails vspDetails,
375                                                   FileContentHandler fileContentHandler,
376                                                   AnalyzedZipHeatFiles analyzedZipHeatFiles) {
377     return manifestCreator.createManifest(vspDetails, fileContentHandler, analyzedZipHeatFiles);
378   }
379
380   @Override
381   public Optional<ByteArrayInputStream> fetchZipFileByteArrayInputStream(String vspId,
382                                                                          OrchestrationTemplateCandidateData candidateDataEntity,
383                                                                          String manifest,
384                                                                          OnboardingTypesEnum type,
385                                                                          Map<String, List<ErrorMessage>> uploadErrors) {
386     byte[] file;
387     ByteArrayInputStream byteArrayInputStream = null;
388     try {
389       file = replaceManifestInZip(candidateDataEntity.getContentData(), manifest, type);
390       byteArrayInputStream = new ByteArrayInputStream(
391           Objects.isNull(file) ? candidateDataEntity.getContentData().array()
392               : file);
393     } catch (IOException e) {
394       ErrorMessage errorMessage =
395           new ErrorMessage(ErrorLevel.ERROR,
396               Messages.CANDIDATE_PROCESS_FAILED.getErrorMessage());
397       logger.error(errorMessage.getMessage(), e);
398       ErrorsUtil
399           .addStructureErrorToErrorMap(SdcCommon.UPLOAD_FILE, errorMessage, uploadErrors);
400     }
401     return Optional.ofNullable(byteArrayInputStream);
402   }
403
404   @Override
405   public byte[] replaceManifestInZip(ByteBuffer contentData, String manifest,
406                                      OnboardingTypesEnum type)
407       throws IOException {
408     ByteArrayOutputStream baos = new ByteArrayOutputStream();
409
410     try (final ZipOutputStream zos = new ZipOutputStream(baos);
411          ZipInputStream zipStream = new ZipInputStream(
412              new ByteArrayInputStream(contentData.array()))) {
413       ZipEntry zipEntry;
414       boolean manifestWritten = false;
415       while ((zipEntry = zipStream.getNextEntry()) != null) {
416         if (!zipEntry.getName().equalsIgnoreCase(SdcCommon.MANIFEST_NAME)) {
417           ZipEntry loc_ze = new ZipEntry(zipEntry.getName());
418           zos.putNextEntry(loc_ze);
419           byte[] buf = new byte[1024];
420           int len;
421           while ((len = zipStream.read(buf)) > 0) {
422             zos.write(buf, 0, (len < buf.length) ? len : buf.length);
423           }
424         } else {
425           manifestWritten = true;
426           writeManifest(manifest, type, zos);
427         }
428         zos.closeEntry();
429       }
430       if (!manifestWritten) {
431         writeManifest(manifest, type, zos);
432         zos.closeEntry();
433       }
434     }
435     return baos.toByteArray();
436   }
437
438   @Override
439   public byte[] getZipData(ByteBuffer contentData)
440       throws IOException {
441     ByteArrayOutputStream baos = new ByteArrayOutputStream();
442
443     try (final ZipOutputStream zos = new ZipOutputStream(baos);
444          ZipInputStream zipStream = new ZipInputStream(
445              new ByteArrayInputStream(contentData.array()))) {
446       ZipEntry zipEntry;
447       while ((zipEntry = zipStream.getNextEntry()) != null) {
448         try {
449           ZipUtils.checkForZipSlipInRead(zipEntry);
450         } catch (ZipSlipException e) {
451           throw new IOException(e);
452         }
453         ZipEntry locZipEntry = new ZipEntry(zipEntry.getName());
454         zos.putNextEntry(locZipEntry);
455         byte[] buf = new byte[1024];
456         int len;
457         while ((len = zipStream.read(buf)) > 0) {
458           zos.write(buf, 0, (len < buf.length) ? len : buf.length);
459         }
460         zos.closeEntry();
461       }
462     }
463     return baos.toByteArray();
464   }
465
466   @Override
467   public Optional<List<ErrorMessage>> validateFileDataStructure(
468       FilesDataStructure filesDataStructure) {
469     return candidateServiceValidator.validateFileDataStructure(filesDataStructure);
470   }
471
472   @Override
473   public void deleteOrchestrationTemplateCandidate(String vspId, Version versionId) {
474     orchestrationTemplateCandidateDao.delete(vspId, versionId);
475   }
476
477   @Override
478   public void updateValidationData(String vspId, Version version, ValidationStructureList
479       validationData) {
480     orchestrationTemplateCandidateDao.updateValidationData(vspId, version, validationData);
481   }
482
483   private void writeManifest(String manifest,
484                              OnboardingTypesEnum type,
485                              ZipOutputStream zos) throws IOException {
486
487     if (isManifestNeedsToGetWritten(type)) {
488       return;
489     }
490
491     zos.putNextEntry(new ZipEntry(SdcCommon.MANIFEST_NAME));
492     try (InputStream manifestStream = new ByteArrayInputStream(
493         manifest.getBytes(StandardCharsets.UTF_8))) {
494       byte[] buf = new byte[1024];
495       int len;
496       while ((len = (manifestStream.read(buf))) > 0) {
497         zos.write(buf, 0, (len < buf.length) ? len : buf.length);
498       }
499     }
500   }
501
502   private boolean isManifestNeedsToGetWritten(OnboardingTypesEnum type) {
503     return type.equals(OnboardingTypesEnum.CSAR);
504   }
505
506   private void handleArtifactsFromTree(HeatStructureTree tree, FilesDataStructure structure) {
507
508     if (Objects.isNull(tree) || Objects.isNull(tree.getArtifacts())) {
509       return;
510     }
511
512     if (CollectionUtils.isNotEmpty(tree.getArtifacts())) {
513       structure.getArtifacts().addAll(
514           tree.getArtifacts()
515               .stream()
516               .map(Artifact::getFileName)
517               .filter(fileName -> !structure.getArtifacts().contains(fileName))
518               .collect(Collectors.toList()));
519     }
520   }
521
522   private void handleOtherResources(HeatStructureTree tree, Set<String> usedEnvFiles,
523                                     FilesDataStructure structure) {
524     Set<HeatStructureTree> others = tree.getOther();
525     if (Objects.isNull(others)) {
526       return;
527     }
528
529     List<String> artifacts = new ArrayList<>();
530     List<String> unassigned = new ArrayList<>();
531     for (HeatStructureTree other : others) {
532       if (HeatFileAnalyzer.isYamlOrEnvFile(other.getFileName())) {
533         if (isEnvFileUsedByHeatFile(usedEnvFiles, other)) {
534           continue;
535         }
536         unassigned.add(other.getFileName());
537       } else {
538         artifacts.add(other.getFileName());
539       }
540       handleArtifactsFromTree(other, structure);
541     }
542     structure.getArtifacts().addAll(artifacts);
543     structure.getUnassigned().addAll(unassigned);
544   }
545
546   private boolean isEnvFileUsedByHeatFile(Set<String> usedEnvFiles, HeatStructureTree other) {
547     return HeatFileAnalyzer.isEnvFile(other.getFileName()) &&
548         usedEnvFiles.contains(other.getFileName());
549   }
550
551   private void addHeatsToFileDataStructure(HeatStructureTree tree, Set<String> usedEnvFiles,
552                                            FilesDataStructure structure,
553                                            Map<String, List<ErrorMessage>> uploadErrors,
554                                            AnalyzedZipHeatFiles analyzedZipHeatFiles) {
555     List<Module> modules = new ArrayList<>();
556     Set<HeatStructureTree> heatsSet = tree.getHeat();
557     if (Objects.isNull(heatsSet)) {
558       return;
559     }
560     for (HeatStructureTree heat : heatsSet) {
561       if (isFileBaseFile(heat.getFileName())) {
562         handleSingleHeat(structure, modules, heat, uploadErrors);
563       } else if (isFileModuleFile(heat.getFileName(),
564           analyzedZipHeatFiles.getModuleFiles())) {
565         handleSingleHeat(structure, modules, heat, uploadErrors);
566       } else {
567         structure.getUnassigned().add(heat.getFileName());
568         addNestedToFileDataStructure(heat, structure);
569       }
570       if (!Objects.isNull(heat.getEnv())) {
571         usedEnvFiles.add(heat.getEnv() == null ? null : heat.getEnv().getFileName());
572       }
573     }
574     structure.setModules(modules);
575
576   }
577
578   private boolean isFileModuleFile(String fileName, Set<String> modulesFileNames) {
579     return modulesFileNames.contains(fileName);
580   }
581
582   private boolean isFileBaseFile(String fileName) {
583     return manifestCreator.isFileBaseFile(fileName);
584   }
585
586   private void handleSingleHeat(FilesDataStructure structure, List<Module> modules,
587                                 HeatStructureTree heat,
588                                 Map<String, List<ErrorMessage>> uploadErrors) {
589     Module module = new Module();
590     module.setYaml(heat.getFileName());
591     module.setIsBase(heat.getBase());
592     addNestedToFileDataStructure(heat, structure);
593     Set<HeatStructureTree> volumeSet = heat.getVolume();
594     int inx = 0;
595     if (Objects.nonNull(volumeSet)) {
596       handleVolumes(module, volumeSet, structure, inx, uploadErrors);
597     }
598     handleEnv(module, heat, false, structure);
599     modules.add(module);
600   }
601
602   private void handleVolumes(Module module, Set<HeatStructureTree> volumeSet,
603                              FilesDataStructure structure, int inx,
604                              Map<String, List<ErrorMessage>> uploadErrors) {
605     for (HeatStructureTree volume : volumeSet) {
606       Objects.requireNonNull(volume, "volume cannot be null!");
607       if (inx++ > 0) {
608         ErrorsUtil.addStructureErrorToErrorMap(SdcCommon.UPLOAD_FILE,
609             new ErrorMessage(ErrorLevel.WARNING,
610                 Messages.MORE_THEN_ONE_VOL_FOR_HEAT.getErrorMessage()), uploadErrors);
611         break;
612       }
613       handleArtifactsFromTree(volume, structure);
614       module.setVol(volume.getFileName());
615       handleEnv(module, volume, true, structure);
616       addNestedToFileDataStructure(volume, structure);
617     }
618   }
619
620   private void handleEnv(Module module, HeatStructureTree tree, boolean isVolEnv,
621                          FilesDataStructure structure) {
622     if (Objects.nonNull(tree.getEnv())) {
623       if (isVolEnv) {
624         module.setVolEnv(tree.getEnv().getFileName());
625       } else {
626         module.setEnv(tree.getEnv().getFileName());
627       }
628       handleArtifactsFromTree(tree.getEnv(), structure);
629     }
630   }
631
632   private void addNestedToFileDataStructure(HeatStructureTree heat,
633                                             FilesDataStructure structure) {
634     Set<HeatStructureTree> nestedSet = heat.getNested();
635     if (Objects.isNull(nestedSet)) {
636       return;
637     }
638     for (HeatStructureTree nested : nestedSet) {
639       if (structure.getNested().contains(nested.getFileName())) {
640         continue;
641       }
642       structure.getNested().add(nested.getFileName());
643       if (CollectionUtils.isNotEmpty(nested.getArtifacts())) {
644         handleArtifactsFromTree(nested, structure);
645       }
646       addNestedToFileDataStructure(nested, structure);
647     }
648   }
649 }