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;
}
}