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';
|
||||
|
||||
/**
|
||||
* 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 '<span class="unset">None</span>';
|
||||
} else {
|
||||
var formatted = '<div class="clear"></div><ul class="el-arguments">';
|
||||
$.each(args, function(key, value) {
|
||||
formatted += (
|
||||
'<li>' +
|
||||
'<span class="el-argument-name">' + key + '</span> - ' +
|
||||
value +
|
||||
'</li>'
|
||||
);
|
||||
});
|
||||
formatted += '</ul>';
|
||||
return formatted;
|
||||
var formatDetails = function (details) {
|
||||
var detailsContainer = $('<div></div>');
|
||||
|
||||
// add the detail name
|
||||
$('<div class="el-name el-section"></div>').text(details.name).appendTo(detailsContainer);
|
||||
|
||||
// add the detail description
|
||||
$('<div class="el-section"></div>').text(details.description).appendTo(detailsContainer);
|
||||
|
||||
// add the function arguments
|
||||
if (typeof details.args !== 'undefined') {
|
||||
var argumentsContainer = $('<div class="el-section"></div>').appendTo(detailsContainer);
|
||||
$('<div class="el-header">Arguments</div>').appendTo(argumentsContainer);
|
||||
|
||||
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 functions = [];
|
||||
|
||||
|
@ -88,7 +125,7 @@
|
|||
// Determine if this function supports running subjectless
|
||||
if (subjectless.length) {
|
||||
subjectlessFunctions.push(name);
|
||||
subject = '<span class="unset">None</span>';
|
||||
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] =
|
||||
'<div>' +
|
||||
'<div class="el-name el-section">' + name + '</div>' +
|
||||
'<div class="el-section">' + description + '</div>' +
|
||||
'<div class="el-section">' +
|
||||
'<div class="el-header">Arguments</div>' +
|
||||
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>';
|
||||
// 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 = $('<div></div>').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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue