package com.thinkbiganalytics.nifi.feedmgr;
/*-
* #%L
* thinkbig-nifi-rest-client-api
* %%
* 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.nifi.rest.model.NiFiPropertyDescriptorTransform;
import com.thinkbiganalytics.nifi.rest.model.NifiProperty;
import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.web.api.dto.ControllerServiceDTO;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Auto Inject Property Values stored in the application.properties file
* 3 use cases are supported
* 1) store properties in the file starting with the prefix defined in the "PropertyExpressionResolver class" default = config.
* 2) store properties in the file starting with "nifi.<PROCESSORTYPE>.<PROPERTY_KEY> where PROCESSORTYPE and PROPERTY_KEY are all lowercase and the spaces are substituted with underscore
* 3) Global property replacement. properties starting with "nifi.all_processors.<PROPERTY_KEY> will globally replace the value when the template is instantiated
*/
public class ConfigurationPropertyReplacer {
public static final String NIF_EL_PROPERTY_REPLACEMENT_PREFIX = "$nifi{";
/**
* returns a new str in lowercase replacing spaces with _
* @param str a string to parse
* @return a new str in lowercase replacing spaces with _
*/
private static String toPropertyName(String str) {
return str.toLowerCase().trim().replaceAll(" +", "_");
}
/**
* return the application.properties property key for the specific property using the proocessor type as the key reference
*
* @param property the nifi property
* @return the property key prepended with the "nifi.<processor type>."
*/
public static String getProcessorPropertyConfigName(NifiProperty property) {
String processorTypeName = "nifi." + toPropertyName(StringUtils.substringAfterLast(property.getProcessorType(), ".") + "." + property.getKey());
return processorTypeName;
}
/**
* return the application.properties property key for the specific property using the proocessor type as the key reference
*
* @param property the nifi property
* @return the property key prepended with the "nifi.<processor type>[<processor name>]."
*/
public static String getProcessorNamePropertyConfigName(NifiProperty property) {
String processorTypeName = "nifi." + toPropertyName(StringUtils.substringAfterLast(property.getProcessorType(), ".") + "["+property.getProcessorName()+"]." + property.getKey());
return processorTypeName;
}
/**
* Return the application.properties property key for 'all_processors' matching the supplied NiFi property.
*
* @param property the nifi property to inspect
* @return the property key prepended with the "nifi.all_processors."
*/
public static String getGlobalAllProcessorsPropertyConfigName(NifiProperty property) {
String processorTypeName = "nifi.all_processors." + toPropertyName(property.getKey());
return processorTypeName;
}
/**
* Replace the $nifi{} with ${}
* @param value the property value
* @return the replaced value
*/
public static String fixNiFiExpressionPropertyValue(String value){
if(StringUtils.isNotBlank(value)) {
return StringUtils.replace(value, ConfigurationPropertyReplacer.NIF_EL_PROPERTY_REPLACEMENT_PREFIX, "${");
}
return value;
}
/**
* This will replace the Map of Properties in the DTO but not persist back to Nifi. You need to call the rest client to persist the change
*
* @param controllerServiceDTO the controller service
* @param properties the properties to set
* @param propertyDescriptorTransform transformer
* @return {@code true} if the properties were updated, {@code false} if not
*/
public static boolean replaceControllerServiceProperties(ControllerServiceDTO controllerServiceDTO, Map<String, String> properties, NiFiPropertyDescriptorTransform propertyDescriptorTransform) {
Set<String> changedProperties = new HashSet<>();
if (controllerServiceDTO != null) {
//check both Nifis Internal Key name as well as the Displayname to match the properties
CaseInsensitiveMap propertyMap = new CaseInsensitiveMap(properties);
Map<String, String> controllerServiceProperties = controllerServiceDTO.getProperties();
controllerServiceProperties.entrySet().stream().filter(
entry -> (propertyMap.containsKey(entry.getKey()) || (controllerServiceDTO.getDescriptors().get(entry.getKey()) != null && propertyMap
.containsKey(controllerServiceDTO.getDescriptors().get(entry.getKey()).getDisplayName().toLowerCase())))).
forEach(entry -> {
boolean isSensitive = propertyDescriptorTransform.isSensitive(controllerServiceDTO.getDescriptors().get(entry.getKey()));
String value = (String) propertyMap.get(entry.getKey());
if (StringUtils.isBlank(value)) {
value = (String) propertyMap.get(controllerServiceDTO.getDescriptors().get(entry.getKey()).getDisplayName().toLowerCase());
}
if (!isSensitive || (isSensitive && StringUtils.isNotBlank(value))) {
entry.setValue(value);
changedProperties.add(entry.getKey());
}
});
}
return !changedProperties.isEmpty();
}
private static String resolveValue(NifiProperty property, Map<String,Object> configProperties){
Object value = null;
if(configProperties != null) {
//see if the processorType is configured
String processorTypeWithProcessorNameProperty = getProcessorNamePropertyConfigName(property);
value = configProperties.get(processorTypeWithProcessorNameProperty);
if (value == null || StringUtils.isBlank(value.toString())) {
String processorTypeProperty = getProcessorPropertyConfigName(property);
value = configProperties.get(processorTypeProperty);
if (value == null || StringUtils.isBlank(value.toString())) {
String globalPropertyType = getGlobalAllProcessorsPropertyConfigName(property);
value = configProperties.get(globalPropertyType);
}
}
if(value != null){
String updatedPropertyValue = fixNiFiExpressionPropertyValue(value.toString());
return updatedPropertyValue;
}
}
return null;
}
/**
* @param property the NifiProperty to replace
* @param configProperties a Map of properties which will be looked to to match against the property key
*/
public static boolean resolveStaticConfigurationProperty(NifiProperty property, Map<String, Object> configProperties) {
String value = property.getValue();
StringBuffer sb = null;
if (configProperties != null && !configProperties.isEmpty()) {
if (StringUtils.isNotBlank(value)) {
Pattern variablePattern = Pattern.compile("\\$\\{(.*?)\\}");
Matcher matchVariablePattern = variablePattern.matcher(value);
while (matchVariablePattern.find()) {
if (sb == null) {
sb = new StringBuffer();
}
String group = matchVariablePattern.group();
int groupCount = matchVariablePattern.groupCount();
if (groupCount == 1) {
String variable = matchVariablePattern.group(1);
//lookup the variable
//first look at configuration properties
Object resolvedValue = configProperties.get(variable);
if (resolvedValue != null) {
matchVariablePattern.appendReplacement(sb, resolvedValue.toString());
}
}
}
if (sb != null) {
matchVariablePattern.appendTail(sb);
}
}
}
if (sb == null) {
String updatedValue = resolveValue(property,configProperties);
if(StringUtils.isNotBlank(updatedValue)) {
sb = new StringBuffer();
sb.append(updatedValue);
}
}
if (sb != null) {
property.setValue(StringUtils.trim(sb.toString()));
}
return sb != null;
}
}