package com.thinkbiganalytics.feedmgr.util; /*- * #%L * thinkbig-feed-manager-controller * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.thinkbiganalytics.feedmgr.rest.ImportComponent; import com.thinkbiganalytics.feedmgr.rest.ImportType; import com.thinkbiganalytics.feedmgr.rest.model.FeedDataTransformation; import com.thinkbiganalytics.feedmgr.rest.model.FeedMetadata; import com.thinkbiganalytics.feedmgr.rest.model.ImportComponentOption; import com.thinkbiganalytics.feedmgr.rest.model.ImportOptions; import com.thinkbiganalytics.feedmgr.rest.model.ImportProperty; import com.thinkbiganalytics.feedmgr.rest.model.RegisteredTemplate; import com.thinkbiganalytics.feedmgr.service.feed.ExportImportFeedService; import com.thinkbiganalytics.feedmgr.service.template.ExportImportTemplateService; import com.thinkbiganalytics.nifi.rest.model.NifiError; import com.thinkbiganalytics.nifi.rest.model.NifiProcessGroup; import com.thinkbiganalytics.nifi.rest.model.NifiProperty; import com.thinkbiganalytics.support.FeedNameUtil; import org.apache.commons.lang3.StringUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.annotation.Nonnull; public class ImportUtil { public static Set<ImportComponentOption> inspectZipComponents(byte[] content, ImportType importType) throws IOException { InputStream inputStream = new ByteArrayInputStream(content); return inspectZipComponents(inputStream, importType); } public static Set<ImportComponentOption> inspectZipComponents(InputStream inputStream, ImportType importType) throws IOException { Set<ImportComponentOption> options = new HashSet<>(); ZipInputStream zis = new ZipInputStream(inputStream); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (entry.getName().startsWith(ExportImportTemplateService.NIFI_TEMPLATE_XML_FILE)) { options.add(new ImportComponentOption(ImportComponent.NIFI_TEMPLATE, importType.equals(ImportType.TEMPLATE) ? true : false)); } else if (entry.getName().startsWith(ExportImportTemplateService.TEMPLATE_JSON_FILE)) { options.add(new ImportComponentOption(ImportComponent.TEMPLATE_DATA, importType.equals(ImportType.TEMPLATE) ? true : false)); } else if (entry.getName().startsWith(ExportImportTemplateService.NIFI_CONNECTING_REUSABLE_TEMPLATE_XML_FILE)) { options.add(new ImportComponentOption(ImportComponent.REUSABLE_TEMPLATE, false)); } else if (importType.equals(ImportType.FEED) && entry.getName().startsWith(ExportImportFeedService.FEED_JSON_FILE)) { options.add(new ImportComponentOption(ImportComponent.FEED_DATA, true)); options.add(new ImportComponentOption(ImportComponent.USER_DATASOURCES, true)); } } zis.closeEntry(); zis.close(); return options; } public static void addToImportOptionsSensitiveProperties(ImportOptions importOptions, List<NifiProperty> sensitiveProperties, ImportComponent component) { ImportComponentOption option = importOptions.findImportComponentOption(component); if (option.getProperties().isEmpty()) { option.setProperties(sensitiveProperties.stream().map(p -> new ImportProperty(p.getProcessorName(), p.getProcessorId(), p.getKey(), "", p.getProcessorType())).collect( Collectors.toList())); } else { //only add in those that are unique Map<String, ImportProperty> propertyMap = option.getProperties().stream().collect(Collectors.toMap(p -> p.getProcessorNameTypeKey(), p -> p)); sensitiveProperties.stream().filter(nifiProperty -> !propertyMap.containsKey(nifiProperty.getProcessorNameTypeKey())).forEach(p -> { option.getProperties().add(new ImportProperty(p.getProcessorName(), p.getProcessorId(), p.getKey(), "", p.getProcessorType())); }); } } public static boolean applyImportPropertiesToTemplate(RegisteredTemplate template, ExportImportTemplateService.ImportTemplate importTemplate, ImportComponent component) { ImportComponentOption option = importTemplate.getImportOptions().findImportComponentOption(component); if (!option.getProperties().isEmpty() && option.getProperties().stream().anyMatch(importProperty -> StringUtils.isBlank(importProperty.getPropertyValue()))) { importTemplate.setSuccess(false); importTemplate.setTemplateResults(new NifiProcessGroup()); String msg = "Unable to import Template. Additional properties to be supplied before importing."; importTemplate.getTemplateResults().addError(NifiError.SEVERITY.WARN, msg, ""); option.getErrorMessages().add(msg); return false; } else { template.getSensitiveProperties().forEach(nifiProperty -> { ImportProperty userSuppliedValue = option.getProperties().stream().filter(importFeedProperty -> nifiProperty.getProcessorId().equalsIgnoreCase(importFeedProperty.getProcessorId()) && nifiProperty.getKey() .equalsIgnoreCase(importFeedProperty.getPropertyKey())).findFirst().orElse(null); //deal with nulls? if(userSuppliedValue == null) { //attempt to find it via the name userSuppliedValue = option.getProperties().stream().filter(importFeedProperty -> nifiProperty.getProcessorName().equalsIgnoreCase(importFeedProperty.getProcessorName()) && nifiProperty.getKey() .equalsIgnoreCase(importFeedProperty.getPropertyKey())).findFirst().orElse(null); } if(userSuppliedValue != null) { nifiProperty.setValue(userSuppliedValue.getPropertyValue()); } }); return true; } } public static boolean applyImportPropertiesToFeed(FeedMetadata metadata, ExportImportFeedService.ImportFeed importFeed, ImportComponent component) { ImportComponentOption option = importFeed.getImportOptions().findImportComponentOption(component); if (!option.getProperties().isEmpty() && option.getProperties().stream().anyMatch(importProperty -> StringUtils.isBlank(importProperty.getPropertyValue()))) { importFeed.setSuccess(false); if (importFeed.getTemplate() == null) { ExportImportTemplateService.ImportTemplate importTemplate = new ExportImportTemplateService.ImportTemplate(importFeed.getFileName()); importFeed.setTemplate(importTemplate); } String feedCategory = importFeed.getImportOptions().getCategorySystemName() != null ? importFeed.getImportOptions().getCategorySystemName() : metadata.getSystemCategoryName(); String msg = "The feed " + FeedNameUtil.fullName(feedCategory, metadata.getSystemFeedName()) + " needs additional properties to be supplied before importing."; importFeed.addErrorMessage(metadata, msg); option.getErrorMessages().add(msg); return false; } else { metadata.getSensitiveProperties().forEach(nifiProperty -> { ImportProperty userSuppliedValue = importFeed.getImportOptions().getProperties(ImportComponent.FEED_DATA).stream().filter(importFeedProperty -> { return nifiProperty.getProcessorId().equalsIgnoreCase(importFeedProperty.getProcessorId()) && nifiProperty.getKey().equalsIgnoreCase(importFeedProperty.getPropertyKey()); }).findFirst().orElse(null); //deal with nulls? if (userSuppliedValue != null) { nifiProperty.setValue(userSuppliedValue.getPropertyValue()); } }); return true; } } /** * Replaces the specified data source id with a new data source id. * * @param metadata the feed metadata * @param oldDatasourceId the id of the data source to be replaced * @param newDatasourceId the id of the new data source */ @SuppressWarnings("unchecked") public static void replaceDatasource(@Nonnull final FeedMetadata metadata, @Nonnull final String oldDatasourceId, @Nonnull final String newDatasourceId) { // Update data transformation final FeedDataTransformation transformation = metadata.getDataTransformation(); if (transformation != null) { // Update chart view model Optional.of(transformation.getChartViewModel()) .map(model -> (List<Map<String, Object>>) model.get("nodes")) .ifPresent( nodes -> nodes.forEach( node -> { final String nodeDatasourceId = (String) node.get("datasourceId"); if (nodeDatasourceId != null && oldDatasourceId.equals(nodeDatasourceId)) { node.put("datasourceId", newDatasourceId); } } ) ); // Update data source id list transformation.getDatasourceIds().replaceAll(id -> oldDatasourceId.equals(id) ? newDatasourceId : id); // Update transform script final String updatedDataTransformScript = transformation.getDataTransformScript().replace(oldDatasourceId, newDatasourceId); transformation.setDataTransformScript(updatedDataTransformScript); } // Update processor properties metadata.getProperties().forEach(property -> { final String value = property.getValue(); if (value != null && !value.isEmpty()) { property.setValue(value.replace(oldDatasourceId, newDatasourceId)); } }); } public static byte[] streamToByteArray(InputStream inputStream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int n; while ((n = inputStream.read(buf)) >= 0) { baos.write(buf, 0, n); } byte[] content = baos.toByteArray(); return content; } }