diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
index 0cf813cb87..7bcb7e3618 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfel.js
@@ -38,29 +38,66 @@
'use strict';
/**
- * Formats the specified arguments for the EL function tooltip.
+ * Formats the specified function definition.
*
- * @param {type} args
- * @returns {String}
+ * @param details
+ * @returns {jQuery|HTMLElement}
*/
- var formatArguments = function(args) {
- if ($.isEmptyObject(args)) {
- return 'None';
- } else {
- var formatted = '
';
- $.each(args, function(key, value) {
- formatted += (
- '- ' +
- '' + key + ' - ' +
- value +
- '
'
- );
- });
- formatted += '
';
- return formatted;
+ var formatDetails = function (details) {
+ var detailsContainer = $('');
+
+ // add the detail name
+ $('').text(details.name).appendTo(detailsContainer);
+
+ // add the detail description
+ $('').text(details.description).appendTo(detailsContainer);
+
+ // add the function arguments
+ if (typeof details.args !== 'undefined') {
+ var argumentsContainer = $('').appendTo(detailsContainer);
+ $('').appendTo(argumentsContainer);
+
+ if ($.isEmptyObject(details.args)) {
+ $('None').appendTo(argumentsContainer);
+ } else {
+ $('').appendTo(argumentsContainer);
+
+ // add the argument
+ var argumentContainer = $('').appendTo(argumentsContainer);
+ $.each(details.args, function (key, value) {
+ var argName = $('').text(key);
+ var argDescription = $('').text(value);
+ $('').append(argName).append(' - ').append(argDescription).appendTo(argumentContainer);
+ });
+ }
}
+
+ // add the function subject
+ if (typeof details.subject !== 'undefined') {
+ var subjectContainer = $('').appendTo(detailsContainer);
+ $('').appendTo(subjectContainer);
+ $('').text(details.subject).appendTo(subjectContainer);
+ $('').appendTo(subjectContainer);
+ }
+
+ // add the function return type
+ if (typeof details.returnType !== 'undefined') {
+ var returnTypeContainer = $('').appendTo(detailsContainer);
+ $('').appendTo(returnTypeContainer);
+ $('').text(details.returnType).appendTo(returnTypeContainer);
+ $('').appendTo(returnTypeContainer);
+ }
+
+ return detailsContainer;
};
+ var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
+
+ var parameters = [];
+ var parameterRegex = new RegExp('^$');
+
+ var parameterDetails = {};
+
var subjectlessFunctions = [];
var functions = [];
@@ -88,7 +125,7 @@
// Determine if this function supports running subjectless
if (subjectless.length) {
subjectlessFunctions.push(name);
- subject = 'None';
+ subject = 'None';
}
// Determine if this function supports running with a subject
@@ -105,26 +142,14 @@
args[argName.text()] = argDescription.text();
});
- // format the function tooltip
- functionDetails[name] =
- '' +
- '
' + name + '
' +
- '
' + description + '
' +
- '
' +
- '' +
- formatArguments(args) +
- '
' +
- '
' +
- '' +
- '
' + subject + '
' +
- '
' +
- '
' +
- '
' +
- '' +
- '
' + returnType + '
' +
- '
' +
- '
' +
- '
';
+ // record the function details
+ functionDetails[name] = {
+ name: name,
+ description: description,
+ args: args,
+ subject: subject,
+ returnType: returnType
+ };
});
}).always(function() {
// build the regex for all functions discovered
@@ -139,37 +164,40 @@
var EXPRESSION = 'expression';
var ARGUMENTS = 'arguments';
var ARGUMENT = 'argument';
+ var PARAMETER = 'parameter';
var INVALID = 'invalid';
/**
* Handles dollars identifies on the stream.
*
- * @param {object} stream The character stream
- * @param {object} states The states
+ * @param {string} startChar The start character
+ * @param {string} context The context to transition to if we match on the specified start character
+ * @param {object} stream The character stream
+ * @param {object} states The states
*/
- var handleDollar = function (stream, states) {
- // determine the number of sequential dollars
- var dollarCount = 0;
+ var handleStart = function (startChar, context, stream, states) {
+ // determine the number of sequential start chars
+ var startCharCount = 0;
stream.eatWhile(function (ch) {
- if (ch === '$') {
- dollarCount++;
+ if (ch === startChar) {
+ startCharCount++;
return true;
}
return false;
});
- // if there is an even number of consecutive dollars this expression is escaped
- if (dollarCount % 2 === 0) {
+ // if there is an even number of consecutive start chars this expression is escaped
+ if (startCharCount % 2 === 0) {
// do not style an escaped expression
return null;
}
- // if there was an odd number of consecutive dollars and there was more than 1
- if (dollarCount > 1) {
+ // if there was an odd number of consecutive start chars and there was more than 1
+ if (startCharCount > 1) {
// back up one char so we can process the start sequence next iteration
stream.backUp(1);
- // do not style the preceeding dollars
+ // do not style the preceding start chars
return null;
}
@@ -180,17 +208,16 @@
// new expression start
states.push({
- context: EXPRESSION
+ context: context
});
// consume any addition whitespace
stream.eatSpace();
return 'bracket';
- } else {
- // not a valid start sequence
- return null;
}
+ // not a valid start sequence
+ return null;
};
/**
@@ -317,7 +344,7 @@
var renderer = function(element, self, data) {
var item = $('').text(data.text);
var li = $(element).qtip({
- content: functionDetails[data.text],
+ content: formatDetails(data.details),
style: {
classes: 'nifi-tooltip nf-tooltip',
tip: false,
@@ -346,6 +373,23 @@
return {
+ /**
+ * Sets the available parameters.
+ *
+ * @param parameterListing
+ */
+ setParameters: function (parameterListing) {
+ parameters = [];
+ parameterDetails = {};
+
+ parameterListing.forEach(function (parameter) {
+ parameters.push(parameter.name);
+ parameterDetails[parameter.name] = parameter;
+ });
+
+ parameterRegex = new RegExp('^((' + parameters.join(')|(') + '))$');
+ },
+
/**
* Returns an object that provides syntax highlighting for NiFi expression language.
*/
@@ -410,8 +454,17 @@
// if we've hit some comments... will consume the remainder of the line
if (current === '#') {
- stream.skipToEnd();
- return 'comment';
+ // consume the pound
+ stream.next();
+
+ var afterPound = stream.peek();
+ if (afterPound !== '{') {
+ stream.skipToEnd();
+ return 'comment';
+ } else {
+ // unconsume the pound
+ stream.backUp(1);
+ }
}
// get the current state
@@ -477,7 +530,7 @@
// nested expression
// -----------------
- var expressionDollarResult = handleDollar(stream, states);
+ var expressionDollarResult = handleStart('$', EXPRESSION, stream, states);
// if we've found an embedded expression we need to...
if (expressionDollarResult !== null) {
@@ -486,6 +539,21 @@
}
return expressionDollarResult;
+ } else if (current === '#') {
+ // --------------------------
+ // nested parameter reference
+ // --------------------------
+
+ // handle the nested parameter reference
+ var parameterReferenceResult = handleStart('#', PARAMETER, stream, states);
+
+ // if we've found an embedded parameter reference we need to...
+ if (parameterReferenceResult !== null) {
+ // transition back to subject when this parameter reference completes
+ state.context = SUBJECT;
+ }
+
+ return parameterReferenceResult;
} else if (current === '}') {
// -----------------
// end of expression
@@ -737,7 +805,7 @@
// -----------------
// handle the nested expression
- var argumentDollarResult = handleDollar(stream, states);
+ var argumentDollarResult = handleStart('$', EXPRESSION, stream, states);
// if we've found an embedded expression we need to...
if (argumentDollarResult !== null) {
@@ -746,6 +814,82 @@
}
return argumentDollarResult;
+ } else if (current === '#') {
+ // --------------------------
+ // nested parameter reference
+ // --------------------------
+
+ // handle the nested parameter reference
+ var parameterReferenceResult = handleStart('#', PARAMETER, stream, states);
+
+ // if we've found an embedded parameter reference we need to...
+ if (parameterReferenceResult !== null) {
+ // transition back to arguments when this parameter reference completes
+ state.context = ARGUMENTS;
+ }
+
+ return parameterReferenceResult;
+ } else {
+ // ----------
+ // unexpected
+ // ----------
+
+ // consume and move along
+ stream.skipToEnd();
+ state.context = INVALID;
+
+ // unexpected...
+ return null;
+ }
+ }
+
+ // within a parameter reference
+ if (state.context === PARAMETER) {
+ // attempt to extract a parameter name
+ var parameterName = stream.match(parameterKeyRegex, false);
+
+ // if the result returned a match
+ if (parameterName !== null && parameterName.length === 1) {
+ // consume the entire token to ensure the whole function
+ // name is matched. this is an issue with functions like
+ // substring and substringAfter since 'substringA' would
+ // match the former and when we really want to autocomplete
+ // against the latter.
+ stream.match(parameterKeyRegex);
+
+ // see if this matches a known function and is followed by (
+ if (parameterRegex.test(parameterName)) {
+ // ------------------
+ // resolved parameter
+ // ------------------
+
+ // style for function
+ return 'builtin';
+ } else {
+ // --------------------
+ // unresolved parameter
+ // --------------------
+
+ // style for function
+ return 'string';
+ }
+ }
+
+ if (current === '}') {
+ // -----------------
+ // end of expression
+ // -----------------
+
+ // consume the close
+ stream.next();
+
+ // signifies the end of an parameter reference
+ if (typeof states.pop() === 'undefined') {
+ return null;
+ } else {
+ // style as expression
+ return 'bracket';
+ }
} else {
// ----------
// unexpected
@@ -762,7 +906,12 @@
// signifies the potential start of an expression
if (current === '$') {
- return handleDollar(stream, states);
+ return handleStart('$', EXPRESSION, stream, states);
+ }
+
+ // signifies the potential start of a parameter reference
+ if (current === '#') {
+ return handleStart('#', PARAMETER, stream, states);
}
// signifies the end of an expression
@@ -810,9 +959,15 @@
return context === EXPRESSION || context === SUBJECT_OR_FUNCTION;
};
+ // whether or not the current context is within a parameter reference
+ var isParameterReference = function (context) {
+ // attempting to match a function name or already successfully matched a function name
+ return context === PARAMETER;
+ };
+
// only support suggestion in certain cases
var context = state.context;
- if (!isSubjectlessFunction(context) && !isFunction(context)) {
+ if (!isSubjectlessFunction(context) && !isFunction(context) && !isParameterReference(context)) {
return null;
}
@@ -823,19 +978,29 @@
var trimmed = $.trim(value);
// identify potential patterns and increment the start location appropriately
- if (trimmed === '${' || trimmed === ':') {
+ if (trimmed === '${' || trimmed === ':' || trimmed === '#{') {
includeAll = true;
token.start += value.length;
}
- var getCompletions = function(functions) {
+ var options = functions;
+ var useFunctionDetails = true;
+ if (isSubjectlessFunction(context)) {
+ options = subjectlessFunctions;
+ } else if (isParameterReference(context)) {
+ options = parameters;
+ useFunctionDetails = false;
+ }
+
+ var getCompletions = function(options) {
var found = [];
- $.each(functions, function (i, funct) {
- if ($.inArray(funct, found) === -1) {
- if (includeAll || funct.toLowerCase().indexOf(value) === 0) {
+ $.each(options, function (i, opt) {
+ if ($.inArray(opt, found) === -1) {
+ if (includeAll || opt.toLowerCase().indexOf(value) === 0) {
found.push({
- text: funct,
+ text: opt,
+ details: useFunctionDetails ? functionDetails[opt] : parameterDetails[opt],
render: renderer
});
}
@@ -846,7 +1011,7 @@
};
// get the suggestions for the current context
- var completionList = getCompletions(isSubjectlessFunction(context) ? subjectlessFunctions : functions);
+ var completionList = getCompletions(options);
completionList = completionList.sort(function (a, b) {
var aLower = a.text.toLowerCase();
var bLower = b.text.toLowerCase();
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfpr.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfpr.js
index 23e5af4721..ef9c8d795a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfpr.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/languages/nfpr.js
@@ -57,6 +57,8 @@
return parameterContainer;
};
+ var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
+
var parameters = [];
var parameterRegex = new RegExp('^$');
@@ -311,10 +313,8 @@
return null;
}
- // within a function
+ // within a parameter reference
if (state.context === PARAMETER) {
- var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
-
// attempt to extract a parameter name
var parameterName = stream.match(parameterKeyRegex, false);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index ce5dea58d1..b8a79d9f08 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -1257,6 +1257,34 @@
columns: {
value: {
editor: getNfEditor(function (propertyDescriptor) {
+ // set the available parameters
+ // TODO - obtain actual parameters and filter accordingly to sensitivity
+ nf.nfel.setParameters([
+ {
+ name: 'param 1',
+ sensitive: false,
+ description: 'this is the description for param 1',
+ value: 'value 1'
+ },
+ {
+ name: 'param 2',
+ sensitive: true,
+ description: 'this is the description for param 2',
+ value: 'value 2'
+ },
+ {
+ name: 'param 3',
+ sensitive: false,
+ value: 'value 3'
+ },
+ {
+ name: 'param 4',
+ sensitive: false,
+ description: 'this is the description for param 4',
+ value: 'value 4'
+ }
+ ]);
+
return nf.nfel;
})
}