mirror of https://github.com/apache/nifi.git
NIFI-6346:
- Updating nfel to support referencing parameters.
This commit is contained in:
parent
7228496801
commit
d93ae47afc
|
@ -38,29 +38,66 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the specified arguments for the EL function tooltip.
|
* Formats the specified function definition.
|
||||||
*
|
*
|
||||||
* @param {type} args
|
* @param details
|
||||||
* @returns {String}
|
* @returns {jQuery|HTMLElement}
|
||||||
*/
|
*/
|
||||||
var formatArguments = function(args) {
|
var formatDetails = function (details) {
|
||||||
if ($.isEmptyObject(args)) {
|
var detailsContainer = $('<div></div>');
|
||||||
return '<span class="unset">None</span>';
|
|
||||||
} else {
|
// add the detail name
|
||||||
var formatted = '<div class="clear"></div><ul class="el-arguments">';
|
$('<div class="el-name el-section"></div>').text(details.name).appendTo(detailsContainer);
|
||||||
$.each(args, function(key, value) {
|
|
||||||
formatted += (
|
// add the detail description
|
||||||
'<li>' +
|
$('<div class="el-section"></div>').text(details.description).appendTo(detailsContainer);
|
||||||
'<span class="el-argument-name">' + key + '</span> - ' +
|
|
||||||
value +
|
// add the function arguments
|
||||||
'</li>'
|
if (typeof details.args !== 'undefined') {
|
||||||
);
|
var argumentsContainer = $('<div class="el-section"></div>').appendTo(detailsContainer);
|
||||||
});
|
$('<div class="el-header">Arguments</div>').appendTo(argumentsContainer);
|
||||||
formatted += '</ul>';
|
|
||||||
return formatted;
|
if ($.isEmptyObject(details.args)) {
|
||||||
|
$('<span class="unset">None</span>').appendTo(argumentsContainer);
|
||||||
|
} else {
|
||||||
|
$('<div class="clear"></div>').appendTo(argumentsContainer);
|
||||||
|
|
||||||
|
// add the argument
|
||||||
|
var argumentContainer = $('<ul class="el-arguments"></ul>').appendTo(argumentsContainer);
|
||||||
|
$.each(details.args, function (key, value) {
|
||||||
|
var argName = $('<span class="el-argument-name"></span>').text(key);
|
||||||
|
var argDescription = $('<span></span>').text(value);
|
||||||
|
$('<li></li>').append(argName).append(' - ').append(argDescription).appendTo(argumentContainer);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add the function subject
|
||||||
|
if (typeof details.subject !== 'undefined') {
|
||||||
|
var subjectContainer = $('<div class="el-section"></div>').appendTo(detailsContainer);
|
||||||
|
$('<div class="el-header">Subject</div>').appendTo(subjectContainer);
|
||||||
|
$('<p></p>').text(details.subject).appendTo(subjectContainer);
|
||||||
|
$('<div class="clear"></div>').appendTo(subjectContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the function return type
|
||||||
|
if (typeof details.returnType !== 'undefined') {
|
||||||
|
var returnTypeContainer = $('<div class="el-section"></div>').appendTo(detailsContainer);
|
||||||
|
$('<div class="el-header">Returns</div>').appendTo(returnTypeContainer);
|
||||||
|
$('<p></p>').text(details.returnType).appendTo(returnTypeContainer);
|
||||||
|
$('<div class="clear"></div>').appendTo(returnTypeContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return detailsContainer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
|
||||||
|
|
||||||
|
var parameters = [];
|
||||||
|
var parameterRegex = new RegExp('^$');
|
||||||
|
|
||||||
|
var parameterDetails = {};
|
||||||
|
|
||||||
var subjectlessFunctions = [];
|
var subjectlessFunctions = [];
|
||||||
var functions = [];
|
var functions = [];
|
||||||
|
|
||||||
|
@ -88,7 +125,7 @@
|
||||||
// Determine if this function supports running subjectless
|
// Determine if this function supports running subjectless
|
||||||
if (subjectless.length) {
|
if (subjectless.length) {
|
||||||
subjectlessFunctions.push(name);
|
subjectlessFunctions.push(name);
|
||||||
subject = '<span class="unset">None</span>';
|
subject = 'None';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if this function supports running with a subject
|
// Determine if this function supports running with a subject
|
||||||
|
@ -105,26 +142,14 @@
|
||||||
args[argName.text()] = argDescription.text();
|
args[argName.text()] = argDescription.text();
|
||||||
});
|
});
|
||||||
|
|
||||||
// format the function tooltip
|
// record the function details
|
||||||
functionDetails[name] =
|
functionDetails[name] = {
|
||||||
'<div>' +
|
name: name,
|
||||||
'<div class="el-name el-section">' + name + '</div>' +
|
description: description,
|
||||||
'<div class="el-section">' + description + '</div>' +
|
args: args,
|
||||||
'<div class="el-section">' +
|
subject: subject,
|
||||||
'<div class="el-header">Arguments</div>' +
|
returnType: returnType
|
||||||
formatArguments(args) +
|
};
|
||||||
'</div>' +
|
|
||||||
'<div class="el-section">' +
|
|
||||||
'<div class="el-header">Subject</div>' +
|
|
||||||
'<p>' + subject + '</p>' +
|
|
||||||
'<div class="clear"></div>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="el-section">' +
|
|
||||||
'<div class="el-header">Returns</div>' +
|
|
||||||
'<p>' + returnType + '</p>' +
|
|
||||||
'<div class="clear"></div>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>';
|
|
||||||
});
|
});
|
||||||
}).always(function() {
|
}).always(function() {
|
||||||
// build the regex for all functions discovered
|
// build the regex for all functions discovered
|
||||||
|
@ -139,37 +164,40 @@
|
||||||
var EXPRESSION = 'expression';
|
var EXPRESSION = 'expression';
|
||||||
var ARGUMENTS = 'arguments';
|
var ARGUMENTS = 'arguments';
|
||||||
var ARGUMENT = 'argument';
|
var ARGUMENT = 'argument';
|
||||||
|
var PARAMETER = 'parameter';
|
||||||
var INVALID = 'invalid';
|
var INVALID = 'invalid';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles dollars identifies on the stream.
|
* Handles dollars identifies on the stream.
|
||||||
*
|
*
|
||||||
* @param {object} stream The character stream
|
* @param {string} startChar The start character
|
||||||
* @param {object} states The states
|
* @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) {
|
var handleStart = function (startChar, context, stream, states) {
|
||||||
// determine the number of sequential dollars
|
// determine the number of sequential start chars
|
||||||
var dollarCount = 0;
|
var startCharCount = 0;
|
||||||
stream.eatWhile(function (ch) {
|
stream.eatWhile(function (ch) {
|
||||||
if (ch === '$') {
|
if (ch === startChar) {
|
||||||
dollarCount++;
|
startCharCount++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// if there is an even number of consecutive dollars this expression is escaped
|
// if there is an even number of consecutive start chars this expression is escaped
|
||||||
if (dollarCount % 2 === 0) {
|
if (startCharCount % 2 === 0) {
|
||||||
// do not style an escaped expression
|
// do not style an escaped expression
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there was an odd number of consecutive dollars and there was more than 1
|
// if there was an odd number of consecutive start chars and there was more than 1
|
||||||
if (dollarCount > 1) {
|
if (startCharCount > 1) {
|
||||||
// back up one char so we can process the start sequence next iteration
|
// back up one char so we can process the start sequence next iteration
|
||||||
stream.backUp(1);
|
stream.backUp(1);
|
||||||
|
|
||||||
// do not style the preceeding dollars
|
// do not style the preceding start chars
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,17 +208,16 @@
|
||||||
|
|
||||||
// new expression start
|
// new expression start
|
||||||
states.push({
|
states.push({
|
||||||
context: EXPRESSION
|
context: context
|
||||||
});
|
});
|
||||||
|
|
||||||
// consume any addition whitespace
|
// consume any addition whitespace
|
||||||
stream.eatSpace();
|
stream.eatSpace();
|
||||||
|
|
||||||
return 'bracket';
|
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 renderer = function(element, self, data) {
|
||||||
var item = $('<div></div>').text(data.text);
|
var item = $('<div></div>').text(data.text);
|
||||||
var li = $(element).qtip({
|
var li = $(element).qtip({
|
||||||
content: functionDetails[data.text],
|
content: formatDetails(data.details),
|
||||||
style: {
|
style: {
|
||||||
classes: 'nifi-tooltip nf-tooltip',
|
classes: 'nifi-tooltip nf-tooltip',
|
||||||
tip: false,
|
tip: false,
|
||||||
|
@ -346,6 +373,23 @@
|
||||||
|
|
||||||
return {
|
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.
|
* 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 we've hit some comments... will consume the remainder of the line
|
||||||
if (current === '#') {
|
if (current === '#') {
|
||||||
stream.skipToEnd();
|
// consume the pound
|
||||||
return 'comment';
|
stream.next();
|
||||||
|
|
||||||
|
var afterPound = stream.peek();
|
||||||
|
if (afterPound !== '{') {
|
||||||
|
stream.skipToEnd();
|
||||||
|
return 'comment';
|
||||||
|
} else {
|
||||||
|
// unconsume the pound
|
||||||
|
stream.backUp(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current state
|
// get the current state
|
||||||
|
@ -477,7 +530,7 @@
|
||||||
// nested expression
|
// nested expression
|
||||||
// -----------------
|
// -----------------
|
||||||
|
|
||||||
var expressionDollarResult = handleDollar(stream, states);
|
var expressionDollarResult = handleStart('$', EXPRESSION, stream, states);
|
||||||
|
|
||||||
// if we've found an embedded expression we need to...
|
// if we've found an embedded expression we need to...
|
||||||
if (expressionDollarResult !== null) {
|
if (expressionDollarResult !== null) {
|
||||||
|
@ -486,6 +539,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return expressionDollarResult;
|
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 === '}') {
|
} else if (current === '}') {
|
||||||
// -----------------
|
// -----------------
|
||||||
// end of expression
|
// end of expression
|
||||||
|
@ -737,7 +805,7 @@
|
||||||
// -----------------
|
// -----------------
|
||||||
|
|
||||||
// handle the nested expression
|
// 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 we've found an embedded expression we need to...
|
||||||
if (argumentDollarResult !== null) {
|
if (argumentDollarResult !== null) {
|
||||||
|
@ -746,6 +814,82 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return argumentDollarResult;
|
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 {
|
} else {
|
||||||
// ----------
|
// ----------
|
||||||
// unexpected
|
// unexpected
|
||||||
|
@ -762,7 +906,12 @@
|
||||||
|
|
||||||
// signifies the potential start of an expression
|
// signifies the potential start of an expression
|
||||||
if (current === '$') {
|
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
|
// signifies the end of an expression
|
||||||
|
@ -810,9 +959,15 @@
|
||||||
return context === EXPRESSION || context === SUBJECT_OR_FUNCTION;
|
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
|
// only support suggestion in certain cases
|
||||||
var context = state.context;
|
var context = state.context;
|
||||||
if (!isSubjectlessFunction(context) && !isFunction(context)) {
|
if (!isSubjectlessFunction(context) && !isFunction(context) && !isParameterReference(context)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -823,19 +978,29 @@
|
||||||
var trimmed = $.trim(value);
|
var trimmed = $.trim(value);
|
||||||
|
|
||||||
// identify potential patterns and increment the start location appropriately
|
// identify potential patterns and increment the start location appropriately
|
||||||
if (trimmed === '${' || trimmed === ':') {
|
if (trimmed === '${' || trimmed === ':' || trimmed === '#{') {
|
||||||
includeAll = true;
|
includeAll = true;
|
||||||
token.start += value.length;
|
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 = [];
|
var found = [];
|
||||||
|
|
||||||
$.each(functions, function (i, funct) {
|
$.each(options, function (i, opt) {
|
||||||
if ($.inArray(funct, found) === -1) {
|
if ($.inArray(opt, found) === -1) {
|
||||||
if (includeAll || funct.toLowerCase().indexOf(value) === 0) {
|
if (includeAll || opt.toLowerCase().indexOf(value) === 0) {
|
||||||
found.push({
|
found.push({
|
||||||
text: funct,
|
text: opt,
|
||||||
|
details: useFunctionDetails ? functionDetails[opt] : parameterDetails[opt],
|
||||||
render: renderer
|
render: renderer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -846,7 +1011,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// get the suggestions for the current context
|
// get the suggestions for the current context
|
||||||
var completionList = getCompletions(isSubjectlessFunction(context) ? subjectlessFunctions : functions);
|
var completionList = getCompletions(options);
|
||||||
completionList = completionList.sort(function (a, b) {
|
completionList = completionList.sort(function (a, b) {
|
||||||
var aLower = a.text.toLowerCase();
|
var aLower = a.text.toLowerCase();
|
||||||
var bLower = b.text.toLowerCase();
|
var bLower = b.text.toLowerCase();
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
return parameterContainer;
|
return parameterContainer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
|
||||||
|
|
||||||
var parameters = [];
|
var parameters = [];
|
||||||
var parameterRegex = new RegExp('^$');
|
var parameterRegex = new RegExp('^$');
|
||||||
|
|
||||||
|
@ -311,10 +313,8 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// within a function
|
// within a parameter reference
|
||||||
if (state.context === PARAMETER) {
|
if (state.context === PARAMETER) {
|
||||||
var parameterKeyRegex = /^[a-zA-Z0-9-_. ]+/;
|
|
||||||
|
|
||||||
// attempt to extract a parameter name
|
// attempt to extract a parameter name
|
||||||
var parameterName = stream.match(parameterKeyRegex, false);
|
var parameterName = stream.match(parameterKeyRegex, false);
|
||||||
|
|
||||||
|
|
|
@ -1257,6 +1257,34 @@
|
||||||
columns: {
|
columns: {
|
||||||
value: {
|
value: {
|
||||||
editor: getNfEditor(function (propertyDescriptor) {
|
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;
|
return nf.nfel;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue