blob: 3fe01cd917752ff5c31962a4fce9c75f1e37847b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* global define, module, require, exports */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery',
'd3',
'nf.ErrorHandler',
'nf.Common',
'nf.Dialog',
'nf.Storage',
'nf.Client',
'nf.Settings',
'nf.UniversalCapture',
'nf.CustomUi',
'nf.CanvasUtils',
'nf.Processor'],
function ($, d3, nfErrorHandler, nfCommon, nfDialog, nfStorage, nfClient, nfSettings, nfUniversalCapture, nfCustomUi, nfCanvasUtils, nfProcessor) {
return (nf.ControllerService = factory($, d3, nfErrorHandler, nfCommon, nfDialog, nfStorage, nfClient, nfSettings, nfUniversalCapture, nfCustomUi, nfCanvasUtils, nfProcessor));
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.ControllerService =
factory(require('jquery'),
require('d3'),
require('nf.ErrorHandler'),
require('nf.Common'),
require('nf.Dialog'),
require('nf.Storage'),
require('nf.Client'),
require('nf.Settings'),
require('nf.UniversalCapture'),
require('nf.CustomUi'),
require('nf.CanvasUtils'),
require('nf.Processor')));
} else {
nf.ControllerService = factory(root.$,
root.d3,
root.nf.ErrorHandler,
root.nf.Common,
root.nf.Dialog,
root.nf.Storage,
root.nf.Client,
root.nf.Settings,
root.nf.UniversalCapture,
root.nf.CustomUi,
root.nf.CanvasUtils,
root.nf.Processor);
}
}(this, function ($, d3, nfErrorHandler, nfCommon, nfDialog, nfStorage, nfClient, nfSettings, nfUniversalCapture, nfCustomUi, nfCanvasUtils, nfProcessor) {
'use strict';
var nfControllerServices, nfReportingTask;
var config = {
edit: 'edit',
readOnly: 'read-only',
serviceOnly: 'SERVICE_ONLY',
serviceAndReferencingComponents: 'SERVICE_AND_REFERENCING_COMPONENTS',
urls: {
api: '../nifi-api'
}
};
/**
* Determines whether the user has made any changes to the controller service configuration
* that needs to be saved.
*/
var isSaveRequired = function () {
var entity = $('#controller-service-configuration').data('controllerServiceDetails');
// determine if any controller service settings have changed
if ($('#controller-service-name').val() !== entity.component['name']) {
return true;
}
if ($('#controller-service-comments').val() !== entity.component['comments']) {
return true;
}
// defer to the properties
return $('#controller-service-properties').propertytable('isSaveRequired');
};
/**
* Marshals the data that will be used to update the contr oller service's configuration.
*/
var marshalDetails = function () {
// properties
var properties = $('#controller-service-properties').propertytable('marshalProperties');
// create the controller service dto
var controllerServiceDto = {};
controllerServiceDto['id'] = $('#controller-service-id').text();
controllerServiceDto['name'] = $('#controller-service-name').val();
controllerServiceDto['comments'] = $('#controller-service-comments').val();
// set the properties
if ($.isEmptyObject(properties) === false) {
controllerServiceDto['properties'] = properties;
}
// create the controller service entity
var controllerServiceEntity = {};
controllerServiceEntity['disconnectedNodeAcknowledged'] = nfStorage.isDisconnectionAcknowledged();
controllerServiceEntity['component'] = controllerServiceDto;
// return the marshaled details
return controllerServiceEntity;
};
/**
* Validates the specified details.
*
* @argument {object} details The details to validate
*/
var validateDetails = function (details) {
return true;
};
/**
* Reloads the specified controller service if we have read permissions. It's referencing and referenced
* components are NOT reloaded.
*
* @param {jQuery} serviceTable
* @param {string} id
*/
var reloadControllerService = function (serviceTable, id) {
// get the table and update the row accordingly
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(id);
// this may happen if controller service A references another controller
// service B that has been removed. attempting to enable/disable/remove A
// will attempt to reload B which is no longer a known service. also ensure
// we have permissions to reload the service
if (nfCommon.isUndefined(controllerServiceEntity) || controllerServiceEntity.permissions.canRead === false) {
return $.Deferred(function (deferred) {
deferred.reject();
}).promise();
}
return $.ajax({
type: 'GET',
url: controllerServiceEntity.uri,
dataType: 'json'
}).done(function (response) {
renderControllerService(serviceTable, response);
}).fail(nfErrorHandler.handleAjaxError);
};
/**
* Renders the specified controller service.
*
* @param {object} serviceTable
* @param {object} controllerServiceEntity
*/
var renderControllerService = function (serviceTable, controllerServiceEntity) {
// get the table and update the row accordingly
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var currentControllerServiceEntity = controllerServiceData.getItemById(controllerServiceEntity.id);
controllerServiceData.updateItem(controllerServiceEntity.id, $.extend({
type: 'ControllerService',
bulletins: currentControllerServiceEntity.bulletins
}, controllerServiceEntity));
};
/**
* Reloads the specified controller services and all of its referencing
* and referenced components.
*
* @param {jQuery} serviceTable
* @param {type} controllerService
*/
var reloadControllerServiceAndReferencingComponents = function (serviceTable, controllerService) {
reloadControllerService(serviceTable, controllerService.id).done(function (response) {
reloadControllerServiceReferences(serviceTable, response.component);
});
};
/**
* Reloads components that reference this controller service as well as
* other services that this controller service references.
*
* @param {jQuery} serviceTable
* @param {object} controllerService
*/
var reloadControllerServiceReferences = function (serviceTable, controllerService) {
// reload all dependent processors if they are currently visible
$.each(controllerService.referencingComponents, function (_, referencingComponentEntity) {
// ensure we can read the referencing component prior to reloading
if (referencingComponentEntity.permissions.canRead === false) {
return;
}
var reference = referencingComponentEntity.component;
if (reference.referenceType === 'Processor') {
// reload the processor on the canvas if appropriate
if (nfCanvasUtils.getGroupId() === reference.groupId) {
nfProcessor.reload(reference.id);
}
// update the current active thread count
$('div.' + reference.id + '-active-threads').text(reference.activeThreadCount);
// update the current state of this processor
var referencingComponentState = $('div.' + reference.id + '-state');
if (referencingComponentState.length) {
updateReferencingSchedulableComponentState(referencingComponentState, reference);
}
} else if (reference.referenceType === 'ReportingTask') {
// reload the referencing reporting tasks
nfReportingTask.reload(reference.id);
// update the current active thread count
$('div.' + reference.id + '-active-threads').text(reference.activeThreadCount);
// update the current state of this reporting task
var referencingComponentState = $('div.' + reference.id + '-state');
if (referencingComponentState.length) {
updateReferencingSchedulableComponentState(referencingComponentState, reference);
}
} else {
// reload the referencing services
reloadControllerService(serviceTable, reference.id);
// update the current state of this service
var referencingComponentState = $('div.' + reference.id + '-state');
if (referencingComponentState.length) {
updateReferencingServiceState(referencingComponentState, reference);
}
// consider it's referencing components if appropriate
if (reference.referenceCycle === false) {
reloadControllerServiceReferences(serviceTable, reference);
}
}
});
// see if this controller service references another controller service
// in order to update the referenced service referencing components
nfControllerService.reloadReferencedServices(serviceTable, controllerService);
};
/**
* Adds a border to the controller service referencing components if necessary.
*
* @argument {jQuery} referenceContainer
*/
var updateReferencingComponentsBorder = function (referenceContainer) {
// determine if it is too big
var tooBig = referenceContainer.get(0).scrollHeight > Math.round(referenceContainer.innerHeight()) ||
referenceContainer.get(0).scrollWidth > Math.round(referenceContainer.innerWidth());
// draw the border if necessary
if (referenceContainer.is(':visible') && tooBig) {
referenceContainer.css('border-width', '1px');
} else {
referenceContainer.css('border-width', '0px');
}
};
/**
* Updates the specified bulletinIcon with the specified bulletins if necessary.
*
* @param {array} bulletins
* @param {jQuery} bulletinIcon
*/
var updateBulletins = function (bulletins, bulletinIcon) {
var currentBulletins = bulletinIcon.data('bulletins');
// update the bulletins if necessary
if (nfCommon.doBulletinsDiffer(currentBulletins, bulletins)) {
bulletinIcon.data('bulletins', bulletins);
// format the new bulletins
var formattedBulletins = nfCommon.getFormattedBulletins(bulletins);
// if there are bulletins update them
if (bulletins.length > 0) {
var list = nfCommon.formatUnorderedList(formattedBulletins);
// update existing tooltip or initialize a new one if appropriate
if (bulletinIcon.data('qtip')) {
bulletinIcon.qtip('option', 'content.text', list);
} else {
bulletinIcon.addClass('has-bulletins').show().qtip($.extend({},
nfCanvasUtils.config.systemTooltipConfig,
{
content: list
}));
}
} else if (bulletinIcon.data('qtip')) {
bulletinIcon.removeClass('has-bulletins').removeData('bulletins').hide().qtip('api').destroy(true);
}
}
};
/**
* Updates the referencingComponentState using the specified referencingComponent.
*
* @param {jQuery} referencingComponentState
* @param {object} referencingComponent
*/
var updateReferencingSchedulableComponentState = function (referencingComponentState, referencingComponent) {
referencingComponentState.removeClass('disabled stopped running invalid').addClass(function () {
var icon = $(this);
var state = referencingComponent.state.toLowerCase();
if (state === 'stopped' && !nfCommon.isEmpty(referencingComponent.validationErrors)) {
state = 'invalid';
// add tooltip for the warnings
var list = nfCommon.formatUnorderedList(referencingComponent.validationErrors);
if (icon.data('qtip')) {
icon.qtip('option', 'content.text', list);
} else {
icon.qtip($.extend({},
nfCanvasUtils.config.systemTooltipConfig,
{
content: list
}));
}
} else if (icon.data('qtip')) {
icon.qtip('api').destroy(true);
}
return state;
});
};
/**
* Updates the referencingServiceState using the specified referencingService.
*
* @param {jQuery} referencingServiceState
* @param {object} referencingService
*/
var updateReferencingServiceState = function (referencingServiceState, referencingService) {
referencingServiceState.removeClass('disabled enabled invalid').addClass(function () {
var icon = $(this);
var state = referencingService.state === 'ENABLED' ? 'enabled' : 'disabled';
if (state === 'disabled' && !nfCommon.isEmpty(referencingService.validationErrors)) {
state = 'invalid';
// add tooltip for the warnings
var list = nfCommon.formatUnorderedList(referencingService.validationErrors);
if (icon.data('qtip')) {
icon.qtip('option', 'content.text', list);
} else {
icon.qtip($.extend({},
nfCanvasUtils.config.systemTooltipConfig,
{
content: list
}));
}
} else if (icon.data('qtip')) {
icon.qtip('api').destroy(true);
}
return state;
});
};
/**
* Updates the bulletins for all referencing components.
*
* @param {array} bulletins
*/
var updateReferencingComponentBulletins = function (bulletins) {
var bulletinsBySource = d3.nest()
.key(function (d) {
return d.sourceId;
})
.map(bulletins, d3.map);
bulletinsBySource.each(function (sourceBulletins, sourceId) {
$('div.' + sourceId + '-bulletins').each(function () {
updateBulletins(sourceBulletins, $(this));
});
});
};
/**
* Adds the specified reference for this controller service.
*
* @param {jQuery} serviceTable
* @param {jQuery} referenceContainer
* @param {object} controllerServiceEntity
*/
var createReferencingComponents = function (serviceTable, referenceContainer, controllerServiceEntity) {
if (controllerServiceEntity.permissions.canRead === false) {
referenceContainer.append('<div class="unset">Unauthorized</div>');
return;
}
var referencingComponents = controllerServiceEntity.component.referencingComponents;
if (nfCommon.isEmpty(referencingComponents)) {
referenceContainer.append('<div class="unset">No referencing components.</div>');
return;
}
// toggles the visibility of a container
var toggle = function (twist, container) {
if (twist.hasClass('expanded')) {
twist.removeClass('expanded').addClass('collapsed');
container.hide();
} else {
twist.removeClass('collapsed').addClass('expanded');
container.show();
}
};
var processors = $('<ul class="referencing-component-listing clear"></ul>');
var services = $('<ul class="referencing-component-listing clear"></ul>');
var tasks = $('<ul class="referencing-component-listing clear"></ul>');
var unauthorized = $('<ul class="referencing-component-listing clear"></ul>');
$.each(referencingComponents, function (_, referencingComponentEntity) {
// check the access policy for this referencing component
if (referencingComponentEntity.permissions.canRead === false) {
var unauthorizedReferencingComponent = $('<div class="unset"></div>').text(referencingComponentEntity.id);
unauthorized.append(unauthorizedReferencingComponent);
} else {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'Processor') {
var processorLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
// show the component
nfCanvasUtils.showComponent(referencingComponent.groupId, referencingComponent.id);
// close the dialog and shell
referenceContainer.closest('.dialog').modal('hide');
$('#shell-close-button').click();
});
// state
var processorState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
updateReferencingSchedulableComponentState(processorState, referencingComponent);
// bulletin
var processorBulletins = $('<div class="referencing-component-bulletins"></div>').addClass(referencingComponent.id + '-bulletins');
if (!nfCommon.isEmpty(referencingComponentEntity.bulletins)) {
updateBulletins(referencingComponentEntity.bulletins, processorBulletins);
}
// type
var processorType = $('<span class="referencing-component-type"></span>').text(referencingComponent.type);
// active thread count
var processorActiveThreadCount = $('<span class="referencing-component-active-thread-count"></span>').addClass(referencingComponent.id + '-active-threads');
if (nfCommon.isDefinedAndNotNull(referencingComponent.activeThreadCount) && referencingComponent.activeThreadCount > 0) {
processorActiveThreadCount.text('(' + referencingComponent.activeThreadCount + ')');
}
// processor
var processorItem = $('<li></li>').append(processorState).append(processorBulletins).append(processorLink).append(processorType).append(processorActiveThreadCount);
processors.append(processorItem);
} else if (referencingComponent.referenceType === 'ControllerService') {
var serviceLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// select the selected row
var row = controllerServiceData.getRowById(referencingComponent.id);
controllerServiceGrid.setSelectedRows([row]);
controllerServiceGrid.scrollRowIntoView(row);
// close the dialog and shell
referenceContainer.closest('.dialog').modal('hide');
});
// container for this service's references
var referencingServiceReferencesContainer = $('<div class="referencing-component-references hidden"></div>');
var serviceTwist = $('<div class="service expansion-button collapsed pointer"></div>').on('click', function () {
if (serviceTwist.hasClass('collapsed')) {
// create the markup for the references
if (referencingComponent.referenceCycle === true) {
referencingServiceReferencesContainer.append('<div class="unset">Reference cycle detected.</div>');
} else {
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// get the controller service and expand its referencing services
getControllerService(referencingComponent.id, controllerServiceData).done(function (controllerServiceEntity) {
createReferencingComponents(serviceTable, referencingServiceReferencesContainer, controllerServiceEntity);
});
}
} else {
referencingServiceReferencesContainer.empty();
}
// toggle visibility
toggle(serviceTwist, referencingServiceReferencesContainer);
// update borders as necessary
updateReferencingComponentsBorder(referenceContainer);
});
// state
var serviceState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
updateReferencingServiceState(serviceState, referencingComponent);
// bulletin
var serviceBulletins = $('<div class="referencing-component-bulletins"></div>').addClass(referencingComponent.id + '-bulletins');
if (!nfCommon.isEmpty(referencingComponentEntity.bulletins)) {
updateBulletins(referencingComponentEntity.bulletins, serviceBulletins);
}
// type
var serviceType = $('<span class="referencing-component-type"></span>').text(referencingComponent.type);
// service
var serviceItem = $('<li></li>').append(serviceTwist).append(serviceState).append(serviceBulletins).append(serviceLink).append(serviceType).append(referencingServiceReferencesContainer);
services.append(serviceItem);
} else if (referencingComponent.referenceType === 'ReportingTask') {
var reportingTaskLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
var reportingTaskData = reportingTaskGrid.getData();
// select the selected row
var row = reportingTaskData.getRowById(referencingComponent.id);
reportingTaskGrid.setSelectedRows([row]);
reportingTaskGrid.scrollRowIntoView(row);
// select the reporting task tab
$('#settings-tabs').find('li:nth-child(3)').click();
// close the dialog and shell
referenceContainer.closest('.dialog').modal('hide');
});
// state
var reportingTaskState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
updateReferencingSchedulableComponentState(reportingTaskState, referencingComponent);
// bulletins
var reportingTaskBulletins = $('<div class="referencing-component-bulletins"></div>').addClass(referencingComponent.id + '-bulletins');
if (!nfCommon.isEmpty(referencingComponentEntity.bulletins)) {
updateBulletins(referencingComponentEntity.bulletins, reportingTaskBulletins);
}
// type
var reportingTaskType = $('<span class="referencing-component-type"></span>').text(nfCommon.substringAfterLast(referencingComponent.type, '.'));
// active thread count
var reportingTaskActiveThreadCount = $('<span class="referencing-component-active-thread-count"></span>').addClass(referencingComponent.id + '-active-threads');
if (nfCommon.isDefinedAndNotNull(referencingComponent.activeThreadCount) && referencingComponent.activeThreadCount > 0) {
reportingTaskActiveThreadCount.text('(' + referencingComponent.activeThreadCount + ')');
}
// reporting task
var reportingTaskItem = $('<li></li>').append(reportingTaskState).append(reportingTaskBulletins).append(reportingTaskLink).append(reportingTaskType).append(reportingTaskActiveThreadCount);
tasks.append(reportingTaskItem);
}
}
});
// create the collapsable listing for each type
var createReferenceBlock = function (titleText, list) {
if (list.is(':empty')) {
list.remove();
return;
}
var twist = $('<div class="expansion-button expanded"></div>');
var title = $('<span class="referencing-component-title"></span>').text(titleText);
var count = $('<span class="referencing-component-count"></span>').text('(' + list.children().length + ')');
// create the reference block
$('<div class="referencing-component-block pointer unselectable"></div>').on('click', function () {
// toggle this block
toggle(twist, list);
// update the border if necessary
updateReferencingComponentsBorder(referenceContainer);
}).append(twist).append(title).append(count).appendTo(referenceContainer);
// add the listing
list.appendTo(referenceContainer);
};
// create blocks for each type of component
createReferenceBlock('Processors', processors);
createReferenceBlock('Reporting Tasks', tasks);
createReferenceBlock('Controller Services', services);
createReferenceBlock('Unauthorized', unauthorized);
// now that the dom elements are in place, we can show the bulletin icons.
$('div.referencing-component-bulletins.has-bulletins').show();
};
/**
* Sets whether the specified controller service is enabled.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} enabled
* @param {function} pollCondition
*/
var setEnabled = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
// build the request entity
var updateControllerServiceEntity = {
'revision': nfClient.getRevision(controllerServiceEntity),
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
'state': enabled ? 'ENABLED' : 'DISABLED'
};
var updated = $.ajax({
type: 'PUT',
url: controllerServiceEntity.uri + '/run-status',
data: JSON.stringify(updateControllerServiceEntity),
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
renderControllerService(serviceTable, response);
}).fail(nfErrorHandler.handleAjaxError);
// wait until the polling of each service finished
return $.Deferred(function (deferred) {
updated.done(function () {
if (controllerServiceEntity.permissions.canRead === false) {
// Can not use GET request to wait for completion. Just resolve blindly. The PUT request has been finished anyway.
deferred.resolve();
return;
}
var serviceUpdated = pollService(controllerServiceEntity, function (latestControllerServiceEntity, bulletins) {
if ($.isArray(bulletins)) {
if (enabled) {
updateBulletins(bulletins, $('#enable-controller-service-bulletins'));
} else {
updateBulletins(bulletins, $('#disable-controller-service-bulletins'));
}
}
// the condition is met once the service is (ENABLING or ENABLED)/DISABLED
var runStatus = latestControllerServiceEntity.status.runStatus;
if (enabled) {
return runStatus === 'ENABLING' || runStatus === 'ENABLED';
} else {
return runStatus === 'DISABLED';
}
}, function (service) {
return nfCanvasUtils.queryBulletins([controllerServiceEntity.id]);
}, pollCondition);
// once the service has updated, resolve and render the updated service
serviceUpdated.done(function () {
deferred.resolve();
}).fail(function () {
deferred.reject();
});
}).fail(function () {
deferred.reject();
});
}).promise();
};
/**
* Gets the id's of all controller services referencing the specified controller service.
*
* @param {object} controllerService
*/
var getReferencingControllerServiceIds = function (controllerService) {
var ids = d3.set();
ids.add(controllerService.id);
var checkReferencingServices = function (referencingComponents) {
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'ControllerService') {
// add the id
ids.add(referencingComponent.id);
// consider it's referencing components if appropriate
if (referencingComponent.referenceCycle === false) {
checkReferencingServices(referencingComponent.referencingComponents);
}
}
});
};
// check the referencing services
checkReferencingServices(controllerService.referencingComponents);
return ids;
};
/**
* Gathers all referencing component revisions.
*
* @param referencingComponents
* @param referencingComponentRevisions
* @param serviceOnly - true includes only services, false includes only schedulable components
*/
var getReferencingComponentRevisions = function (referencingComponents, referencingComponentRevisions, serviceOnly) {
// include the revision of each referencing component
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (serviceOnly) {
if (referencingComponent.referenceType === 'ControllerService') {
referencingComponentRevisions[referencingComponentEntity.id] = nfClient.getRevision(referencingComponentEntity);
}
} else {
if (referencingComponent.referenceType !== 'ControllerService') {
referencingComponentRevisions[referencingComponentEntity.id] = nfClient.getRevision(referencingComponentEntity);
}
}
// recurse
getReferencingComponentRevisions(referencingComponent.referencingComponents, referencingComponentRevisions, serviceOnly);
});
};
/**
* Updates the scheduled state of the processors/reporting tasks referencing
* the specified controller service.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} running
* @param {function} pollCondition
*/
var updateReferencingSchedulableComponents = function (serviceTable, controllerServiceEntity, running, pollCondition) {
var referencingRevisions = {};
getReferencingComponentRevisions(controllerServiceEntity.component.referencingComponents, referencingRevisions, false);
var referenceEntity = {
'id': controllerServiceEntity.id,
'state': running ? 'RUNNING' : 'STOPPED',
'referencingComponentRevisions': referencingRevisions,
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
};
// issue the request to update the referencing components
var updated = $.ajax({
type: 'PUT',
url: controllerServiceEntity.uri + '/references',
data: JSON.stringify(referenceEntity),
dataType: 'json',
contentType: 'application/json'
}).fail(nfErrorHandler.handleAjaxError);
// Note: updated revisions will be retrieved after updateReferencingSchedulableComponents is invoked
// wait until the polling of each service finished
return $.Deferred(function (deferred) {
updated.done(function (response) {
// update the controller service
controllerServiceEntity.component.referencingComponents = response.controllerServiceReferencingComponents;
// if we're just starting schedulable components we're done when the update is finished
if (running) {
deferred.resolve();
} else {
// identify all referencing services
var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service
var polling = [];
services.each(function (controllerServiceId) {
getControllerService(controllerServiceId, controllerServiceData).done(function(controllerServiceEntity) {
polling.push(stopReferencingSchedulableComponents(controllerServiceEntity, pollCondition));
});
});
// wait until polling has finished
$.when.apply(window, polling).done(function () {
deferred.resolve();
}).fail(function () {
deferred.reject();
});
}
}).fail(function () {
deferred.reject();
});
}).promise();
};
/**
* Gets the controller service with the specified id. If the service is present in the specified
* controllerServiceData it will be used. Otherwise it will query for it.
*
* @param controllerServiceId service id
* @param controllerServiceData service table data
* @returns {deferred} the controller service entity
*/
var getControllerService = function (controllerServiceId, controllerServiceData) {
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
if (nfCommon.isDefinedAndNotNull(controllerServiceEntity)) {
return $.Deferred(function (deferred) {
deferred.resolve(controllerServiceEntity);
});
} else {
return $.ajax({
type: 'GET',
url: '../nifi-api/controller-services/' + encodeURIComponent(controllerServiceId),
dataType: 'json'
}).fail(nfErrorHandler.handleAjaxError);
}
};
/**
* Polls the specified services referencing components to see if the
* specified condition is satisfied.
*
* @param {object} controllerServiceEntity
* @param {function} completeCondition
* @param {function} bulletinDeferred
* @param {function} pollCondition
*/
var pollService = function (controllerServiceEntity, completeCondition, bulletinDeferred, pollCondition) {
var controllerService = controllerServiceEntity.component;
// we want to keep polling until the condition is met
return $.Deferred(function (deferred) {
var current = 2;
var getTimeout = function () {
var val = current;
// update the current timeout for the next time
current = Math.min(current * 2, 4);
return val * 1000;
};
// polls for the current status of the referencing components
var poll = function () {
var bulletins = bulletinDeferred(controllerService);
var service = $.ajax({
type: 'GET',
url: controllerServiceEntity.uri,
dataType: 'json'
});
$.when(bulletins, service).done(function (bulletinResponse, serviceResult) {
var latestControllerServiceEntity = serviceResult[0];
conditionMet(latestControllerServiceEntity, bulletinResponse.bulletinBoard.bulletins);
}).fail(function (xhr, status, error) {
deferred.reject();
nfErrorHandler.handleAjaxError(xhr, status, error);
});
};
// tests to if the condition has been met
var conditionMet = function (latestControllerServiceEntity, bulletins) {
if (completeCondition(latestControllerServiceEntity, bulletins)) {
deferred.resolve();
} else {
if (typeof pollCondition === 'function' && pollCondition()) {
setTimeout(poll, getTimeout());
} else {
deferred.reject();
}
}
};
// poll for the status of the referencing components
bulletinDeferred(controllerService).done(function (response) {
conditionMet(controllerServiceEntity, response.bulletinBoard.bulletins);
}).fail(function (xhr, status, error) {
deferred.reject();
nfErrorHandler.handleAjaxError(xhr, status, error);
});
}).promise();
};
/**
* Continues to poll the specified controller service until all referencing schedulable
* components are stopped (not scheduled and 0 active threads).
*
* @param {object} controllerServiceEntity
* @param {function} pollCondition
*/
var stopReferencingSchedulableComponents = function (controllerServiceEntity, pollCondition) {
// continue to poll the service until all schedulable components have stopped
return pollService(controllerServiceEntity, function (latestControllerServiceEntity, bulletins) {
var referencingComponents = latestControllerServiceEntity.component.referencingComponents;
var stillRunning = false;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask') {
if (referencingComponent.state === 'RUNNING' || referencingComponent.activeThreadCount > 0) {
stillRunning = true;
}
// update the current active thread count
$('div.' + referencingComponent.id + '-active-threads').text(referencingComponent.activeThreadCount);
// update the current state of this component
var referencingComponentState = $('div.' + referencingComponent.id + '-state');
updateReferencingSchedulableComponentState(referencingComponentState, referencingComponent);
}
});
// query for the bulletins
updateReferencingComponentBulletins(bulletins);
// condition is met once all referencing are not running
return stillRunning === false;
}, function (service) {
var referencingSchedulableComponents = [];
var referencingComponents = service.referencingComponents;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask') {
referencingSchedulableComponents.push(referencingComponent.id);
}
});
return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
/**
* Continues to poll until all referencing services are enabled.
*
* @param {object} controllerServiceEntity
* @param {function} pollCondition
*/
var enableReferencingServices = function (controllerServiceEntity, pollCondition) {
// continue to poll the service until all referencing services are enabled
return pollService(controllerServiceEntity, function (latestControllerServiceEntity, bulletins) {
var referencingComponents = latestControllerServiceEntity.component.referencingComponents;
var notEnabled = false;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'ControllerService') {
if (referencingComponent.state !== 'ENABLING' && referencingComponent.state !== 'ENABLED') {
notEnabled = true;
}
// update the state of the referencing service
var referencingServiceState = $('div.' + referencingComponent.id + '-state');
updateReferencingServiceState(referencingServiceState, referencingComponent);
}
});
// query for the bulletins
updateReferencingComponentBulletins(bulletins);
// condition is met once all referencing are not disabled
return notEnabled === false;
}, function (service) {
var referencingSchedulableComponents = [];
var referencingComponents = service.referencingComponents;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'ControllerService') {
referencingSchedulableComponents.push(referencingComponent.id);
}
});
return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
/**
* Continues to poll until all referencing services are disabled.
*
* @param {object} controllerServiceEntity
* @param {function} pollCondition
*/
var disableReferencingServices = function (controllerServiceEntity, pollCondition) {
// continue to poll the service until all referencing services are disabled
return pollService(controllerServiceEntity, function (latestControllerServiceEntity, bulletins) {
var referencingComponents = latestControllerServiceEntity.component.referencingComponents;
var notDisabled = false;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'ControllerService') {
if (referencingComponent.state !== 'DISABLED') {
notDisabled = true;
}
// update the state of the referencing service
var referencingServiceState = $('div.' + referencingComponent.id + '-state');
updateReferencingServiceState(referencingServiceState, referencingComponent);
}
});
// query for the bulletins
updateReferencingComponentBulletins(bulletins);
// condition is met once all referencing are not enabled
return notDisabled === false;
}, function (service) {
var referencingSchedulableComponents = [];
var referencingComponents = service.referencingComponents;
$.each(referencingComponents, function (_, referencingComponentEntity) {
var referencingComponent = referencingComponentEntity.component;
if (referencingComponent.referenceType === 'ControllerService') {
referencingSchedulableComponents.push(referencingComponent.id);
}
});
return nfCanvasUtils.queryBulletins(referencingSchedulableComponents);
}, pollCondition);
};
/**
* Updates the referencing services with the specified state.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} enabled
* @param {function} pollCondition
*/
var updateReferencingServices = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
var referencingRevisions = {};
getReferencingComponentRevisions(controllerServiceEntity.component.referencingComponents, referencingRevisions, true);
// build the reference entity
var referenceEntity = {
'id': controllerServiceEntity.id,
'state': enabled ? 'ENABLED' : 'DISABLED',
'referencingComponentRevisions': referencingRevisions,
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
};
// issue the request to update the referencing components
var updated = $.ajax({
type: 'PUT',
url: controllerServiceEntity.uri + '/references',
data: JSON.stringify(referenceEntity),
dataType: 'json',
contentType: 'application/json'
}).fail(nfErrorHandler.handleAjaxError);
// Note: updated revisions will be retrieved after updateReferencingServices is invoked
// wait unil the polling of each service finished
return $.Deferred(function (deferred) {
updated.done(function (response) {
// update the controller service
controllerServiceEntity.component.referencingComponents = response.controllerServiceReferencingComponents;
// identify all referencing services
var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service
var polling = [];
services.each(function (controllerServiceId) {
getControllerService(controllerServiceId, controllerServiceData).done(function(controllerServiceEntity) {
if (enabled) {
polling.push(enableReferencingServices(controllerServiceEntity, pollCondition));
} else {
polling.push(disableReferencingServices(controllerServiceEntity, pollCondition));
}
});
});
$.when.apply(window, polling).done(function () {
deferred.resolve();
}).fail(function () {
deferred.reject();
});
}).fail(function () {
deferred.reject();
});
}).promise();
};
/**
* Shows the dialog for disabling a controller service.
*
* @argument {object} controllerService The controller service to disable
*/
var showDisableControllerServiceDialog = function (serviceTable, controllerServiceEntity) {
// populate the disable controller service dialog
$('#disable-controller-service-id').text(controllerServiceEntity.id);
$('#disable-controller-service-name').text(nfCommon.getComponentName(controllerServiceEntity));
// load the controller referencing components list
var referencingComponentsContainer = $('#disable-controller-service-referencing-components');
createReferencingComponents(serviceTable, referencingComponentsContainer, controllerServiceEntity);
// build the button model
var buttons = [{
buttonText: 'Disable',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
disableHandler(serviceTable);
}
}
}, {
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: closeModal
}
}];
// show the dialog
$('#disable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins
nfCanvasUtils.queryBulletins([controllerServiceEntity.id]).done(function (response) {
updateBulletins(response.bulletinBoard.bulletins, $('#disable-controller-service-bulletins'));
});
// update the border if necessary
updateReferencingComponentsBorder(referencingComponentsContainer);
};
/**
* Shows the dialog for enabling a controller service.
*
* @param {object} serviceTable
* @param {object} controllerService
*/
var showEnableControllerServiceDialog = function (serviceTable, controllerServiceEntity) {
// populate the disable controller service dialog
$('#enable-controller-service-id').text(controllerServiceEntity.id);
$('#enable-controller-service-name').text(nfCommon.getComponentName(controllerServiceEntity));
// load the controller referencing components list
var referencingComponentsContainer = $('#enable-controller-service-referencing-components');
createReferencingComponents(serviceTable, referencingComponentsContainer, controllerServiceEntity);
// build the button model
var buttons = [{
buttonText: 'Enable',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
enableHandler(serviceTable);
}
}
}, {
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: closeModal
}
}];
// show the dialog
$('#enable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins
nfCanvasUtils.queryBulletins([controllerServiceEntity.id]).done(function (response) {
updateBulletins(response.bulletinBoard.bulletins, $('#enable-controller-service-bulletins'));
});
// update the border if necessary
updateReferencingComponentsBorder(referencingComponentsContainer);
};
/**
* Used to handle closing a modal dialog
*/
var closeModal = function () {
$(this).modal('hide');
};
/**
* Handles the disable action of the disable controller service dialog.
*
* @param {jQuery} serviceTable
*/
var disableHandler = function (serviceTable) {
var disableDialog = $('#disable-controller-service-dialog');
var canceled = false;
// only provide a cancel option
disableDialog.modal('setButtonModel', [{
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
canceled = true;
disableDialog.modal('setButtonModel', []);
$('#disable-controller-service-dialog div.controller-service-canceling').show();
}
}
}]);
// show the progress
$('#disable-controller-service-scope-container').hide();
$('#disable-controller-service-progress-container').show();
// get the controller service
var controllerServiceId = $('#disable-controller-service-id').text();
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component;
// whether or not to continue polling
var continuePolling = function () {
return canceled === false;
};
// sets the close button on the dialog
var setCloseButton = function () {
$('#disable-controller-service-dialog div.controller-service-canceling').hide();
disableDialog.modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: closeModal
}
}]);
};
// ensure we have access to all referencing components before attempting the sequence
if (hasUnauthorizedReferencingComponent(controllerServiceEntity)) {
setCloseButton();
nfDialog.showOkDialog({
headerText: 'Controller Service',
dialogContent: controllerServiceEntity.permissions.canRead === false
// Unknown references.
? 'Unable to disable due to a lack of read permission to see referencing components.'
// Unauthorized references.
: 'Unable to disable due to unauthorized referencing components.'
});
return;
}
$('#disable-progress-label').text('Steps to disable ' + nfCommon.getComponentName(controllerServiceEntity));
var disableReferencingSchedulable = $('#disable-referencing-schedulable').addClass('ajax-loading');
$.Deferred(function (deferred) {
// stop all referencing schedulable components
var stopped = updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, false, continuePolling);
// once everything has stopped
stopped.done(function () {
disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
var disableReferencingServices = $('#disable-referencing-services').addClass('ajax-loading');
// disable all referencing services
var disabled = updateReferencingServices(serviceTable, controllerServiceEntity, false, continuePolling);
// everything is disabled
disabled.done(function () {
disableReferencingServices.removeClass('ajax-loading').addClass('ajax-complete');
var disableControllerService = $('#disable-controller-service').addClass('ajax-loading');
// disable this service
setEnabled(serviceTable, controllerServiceEntity, false, continuePolling).done(function () {
deferred.resolve();
disableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () {
deferred.reject();
disableControllerService.removeClass('ajax-loading').addClass('ajax-error');
});
}).fail(function () {
deferred.reject();
disableReferencingServices.removeClass('ajax-loading').addClass('ajax-error');
});
}).fail(function () {
deferred.reject();
disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
});
}).always(function () {
if (controllerServiceEntity.permissions.canRead === true) {
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
}
setCloseButton();
// inform the user if the action was canceled
if (canceled === true && $('#nf-ok-dialog').not(':visible')) {
nfDialog.showOkDialog({
headerText: 'Controller Service',
dialogContent: 'The request to disable has been canceled. Parts of this request may have already completed. Please verify the state of this service and all referencing components.'
});
}
});
};
/**
* Determines if any of the specified referencing components are not authorized to enable/disable.
*
* @param {object} ControllerServiceEntity having referencingComponents referencing components
* @returns {boolean}
*/
var hasUnauthorizedReferencingComponent = function (controllerServiceEntity) {
if (controllerServiceEntity.permissions.canRead === false
|| (controllerServiceEntity.permissions.canWrite === false && controllerServiceEntity.operatePermissions.canWrite === false)) {
return true;
}
var hasUnauthorized = false;
var referencingComponents = controllerServiceEntity.component.referencingComponents;
$.each(referencingComponents, function (_, referencingComponentEntity) {
if (hasUnauthorizedReferencingComponent(referencingComponentEntity)) {
hasUnauthorized = true;
return false;
}
});
return hasUnauthorized;
};
/**
* Handles the enable action of the enable controller service dialog.
*
* @param {jQuery} serviceTable
*/
var enableHandler = function (serviceTable) {
var enableDialog = $('#enable-controller-service-dialog');
var canceled = false;
// get the controller service
var controllerServiceId = $('#enable-controller-service-id').text();
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component;
// determine if we want to also activate referencing components
var scope = $('#enable-controller-service-scope').combo('getSelectedOption').value;
// update visibility
if (scope === config.serviceOnly) {
$('#enable-controller-service-progress li.referencing-component').hide();
}
// only provide a cancel option
enableDialog.modal('setButtonModel', [{
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
canceled = true;
enableDialog.modal('setButtonModel', []);
$('#enable-controller-service-dialog div.controller-service-canceling').show();
}
}
}]);
// show the progress
$('#enable-controller-service-scope-container').hide();
$('#enable-controller-service-progress-container').show();
// whether or not to continue polling
var continuePolling = function () {
return canceled === false;
};
// sets the button to close
var setCloseButton = function () {
$('#enable-controller-service-dialog div.controller-service-canceling').hide();
enableDialog.modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: closeModal
}
}]);
};
// ensure appropriate access
if (scope === config.serviceAndReferencingComponents && hasUnauthorizedReferencingComponent(controllerServiceEntity)) {
setCloseButton();
nfDialog.showOkDialog({
headerText: 'Controller Service',
dialogContent: controllerServiceEntity.permissions.canRead === false
// Unknown references.
? 'Unable to enable due to a lack of read permission to see referencing components.'
// Unauthorized references.
: 'Unable to enable due to unauthorized referencing components.'
});
return;
}
$('#enable-progress-label').text('Steps to enable ' + nfCommon.getComponentName(controllerServiceEntity));
var enableControllerService = $('#enable-controller-service').addClass('ajax-loading');
$.Deferred(function (deferred) {
// enable this controller service
var enable = setEnabled(serviceTable, controllerServiceEntity, true, continuePolling);
if (scope === config.serviceAndReferencingComponents) {
// once the service is enabled, activate all referencing components
enable.done(function () {
enableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
var enableReferencingServices = $('#enable-referencing-services').addClass('ajax-loading');
// enable the referencing services
var servicesEnabled = updateReferencingServices(serviceTable, controllerServiceEntity, true, continuePolling);
// once all the referencing services are enbled
servicesEnabled.done(function () {
enableReferencingServices.removeClass('ajax-loading').addClass('ajax-complete');
var enableReferencingSchedulable = $('#enable-referencing-schedulable').addClass('ajax-loading');
// start all referencing schedulable components
updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, true, continuePolling).done(function () {
deferred.resolve();
enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () {
deferred.reject();
enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
});
}).fail(function () {
deferred.reject();
enableReferencingServices.removeClass('ajax-loading').addClass('ajax-error');
});
}).fail(function () {
deferred.reject();
enableControllerService.removeClass('ajax-loading').addClass('ajax-error');
});
} else {
enable.done(function () {
deferred.resolve();
enableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () {
deferred.reject();
enableControllerService.removeClass('ajax-loading').addClass('ajax-error');
});
}
}).always(function () {
if (controllerServiceEntity.permissions.canRead === true) {
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
}
setCloseButton();
// inform the user if the action was canceled
if (canceled === true && $('#nf-ok-dialog').not(':visible')) {
nfDialog.showOkDialog({
headerText: 'Controller Service',
dialogContent: 'The request to enable has been canceled. Parts of this request may have already completed. Please verify the state of this service and all referencing components.'
});
}
});
};
/**
* Gets a property descriptor for the controller service currently being configured.
*
* @param {type} propertyName
*/
var getControllerServicePropertyDescriptor = function (propertyName) {
var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails');
return $.ajax({
type: 'GET',
url: controllerServiceEntity.uri + '/descriptors',
data: {
propertyName: propertyName
},
dataType: 'json'
}).fail(nfErrorHandler.handleAjaxError);
};
/**
* Gets the parameters for the specified property descriptor and group.
*
* @param propertyDescriptor The property descriptor in question
* @param groupId The group in question
* @returns {deferred}
*/
var getParameters = function (propertyDescriptor, groupId) {
return $.Deferred(function (deferred) {
if (nfCommon.isDefinedAndNotNull(groupId)) {
var parameterContext;
// attempt to identify the parameter context id, conditional based on whether
// the user is configuring the current process group
if (groupId === nfCanvasUtils.getGroupId()) {
parameterContext = nfCanvasUtils.getParameterContext();
} else {
var parentProcessGroup = nfCanvasUtils.getComponentByType('ProcessGroup').get(groupId);
parameterContext = parentProcessGroup.parameterContext;
}
if (nfCommon.isDefinedAndNotNull(parameterContext)) {
$.ajax({
type: 'GET',
url: '../nifi-api/parameter-contexts/' + encodeURIComponent(parameterContext.id),
dataType: 'json'
}).done(function (response) {
var sensitive = nfCommon.isSensitiveProperty(propertyDescriptor);
deferred.resolve(response.component.parameters.map(function (parameterEntity) {
return parameterEntity.parameter;
}).filter(function (parameter) {
return parameter.sensitive === sensitive;
}));
}).fail(function () {
deferred.resolve([]);
});
} else {
deferred.resolve([]);
}
} else {
deferred.resolve([]);
}
}).promise();
};
/**
* Goes to a service configuration from the property table.
*
* @param {jQuery} serviceTable
*/
var goToServiceFromProperty = function (serviceTable) {
return $.Deferred(function (deferred) {
// close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow');
// determine if changes have been made
if (isSaveRequired()) {
// see if those changes should be saved
nfDialog.showYesNoDialog({
headerText: 'Save',
dialogContent: 'Save changes before going to this Controller Service?',
noHandler: function () {
deferred.resolve();
},
yesHandler: function () {
var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails');
saveControllerService(serviceTable, controllerServiceEntity).done(function () {
deferred.resolve();
}).fail(function () {
deferred.reject();
});
}
});
} else {
deferred.resolve();
}
}).promise();
};
var getParameterContext = function (groupId) {
if (_.isNil(groupId)) {
return null;
}
var parameterContext;
// attempt to identify the parameter context, conditional based on whether
// the user is configuring the current process group
if (groupId === nfCanvasUtils.getGroupId()) {
parameterContext = nfCanvasUtils.getParameterContext();
} else {
var parentProcessGroup = nfCanvasUtils.getComponentByType('ProcessGroup').get(groupId);
parameterContext = parentProcessGroup.parameterContext;
}
return parameterContext;
};
var saveControllerService = function (serviceTable, controllerServiceEntity) {
// marshal the settings and properties and update the controller service
var updatedControllerService = marshalDetails();
// ensure details are valid as far as we can tell
if (validateDetails(updatedControllerService)) {
updatedControllerService['revision'] = nfClient.getRevision(controllerServiceEntity);
var previouslyReferencedServiceIds = [];
$.each(identifyReferencedServiceDescriptors(controllerServiceEntity.component), function (_, descriptor) {
var modifyingService = !nfCommon.isUndefined(updatedControllerService.component.properties) && !nfCommon.isUndefined(updatedControllerService.component.properties[descriptor.name]);
var isCurrentlyConfigured = nfCommon.isDefinedAndNotNull(controllerServiceEntity.component.properties[descriptor.name]);
// if we are attempting to update a controller service reference
if (modifyingService && isCurrentlyConfigured) {
// record the current value if set
previouslyReferencedServiceIds.push(controllerServiceEntity.component.properties[descriptor.name]);
}
});
// update the selected component
return $.ajax({
type: 'PUT',
data: JSON.stringify(updatedControllerService),
url: controllerServiceEntity.uri,
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
// reload the controller service
renderControllerService(serviceTable, response);
// reload all previously referenced controller services
$.each(previouslyReferencedServiceIds, function (_, oldServiceReferenceId) {
reloadControllerService(serviceTable, oldServiceReferenceId);
});
}).fail(nfErrorHandler.handleConfigurationUpdateAjaxError);
} else {
return $.Deferred(function (deferred) {
deferred.reject();
}).promise();
}
};
/**
* Identifies the descriptors that identify controller services.
*
* @param {object} component
*/
var identifyReferencedServiceDescriptors = function (component) {
var referencedServiceDescriptors = [];
$.each(component.descriptors, function (_, descriptor) {
if (nfCommon.isDefinedAndNotNull(descriptor.identifiesControllerService)) {
referencedServiceDescriptors.push(descriptor);
}
});
return referencedServiceDescriptors;
};
/**
* Identifies descriptors that reference controller services.
*
* @param {object} component
*/
var getReferencedServices = function (component) {
var referencedServices = [];
$.each(identifyReferencedServiceDescriptors(component), function (_, descriptor) {
var referencedServiceId = component.properties[descriptor.name];
// ensure the property is configured
if (nfCommon.isDefinedAndNotNull(referencedServiceId) && $.trim(referencedServiceId).length > 0) {
referencedServices.push(referencedServiceId);
}
});
return referencedServices;
};
/**
* Track the current table
*/
var currentTable;
var nfControllerService = {
/**
* Initializes the controller service configuration dialog.
*/
init: function (nfControllerServicesRef, nfReportingTaskRef) {
nfControllerServices = nfControllerServicesRef;
nfReportingTask = nfReportingTaskRef;
// initialize the configuration dialog tabs
$('#controller-service-configuration-tabs').tabbs({
tabStyle: 'tab',
selectedTabStyle: 'selected-tab',
scrollableTabContentStyle: 'scrollable',
tabs: [{
name: 'Settings',
tabContentId: 'controller-service-standard-settings-tab-content'
}, {
name: 'Properties',
tabContentId: 'controller-service-properties-tab-content'
}, {
name: 'Comments',
tabContentId: 'controller-service-comments-tab-content'
}],
select: function () {
// remove all property detail dialogs
nfUniversalCapture.removeAllPropertyDetailDialogs();
// update the property table size in case this is the first time its rendered
if ($(this).text() === 'Properties') {
$('#controller-service-properties').propertytable('resetTableSize');
}
// close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow');
// show the border around the processor relationships if necessary
var referenceContainer = $('#controller-service-referencing-components');
updateReferencingComponentsBorder(referenceContainer);
}
});
// initialize the conroller service configuration dialog
$('#controller-service-configuration').modal({
headerText: 'Configure Controller Service',
scrollableContentStyle: 'scrollable',
handler: {
close: function () {
// empty the referencing components list
var referencingComponents = $('#controller-service-referencing-components');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins');
referencingComponents.css('border-width', '0').empty();
// cancel any active edits
$('#controller-service-properties').propertytable('cancelEdit');
// clear the tables
$('#controller-service-properties').propertytable('clear');
// clear the comments
nfCommon.clearField('read-only-controller-service-comments');
// clear the compatible apis
$('#controller-service-compatible-apis').empty();
// removed the cached controller service details
$('#controller-service-configuration').removeData('controllerServiceDetails');
},
open: function () {
nfCommon.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0));
}
}
});
// initialize the disable service dialog
$('#disable-controller-service-dialog').modal({
headerText: 'Disable Controller Service',
scrollableContentStyle: 'scrollable',
handler: {
close: function () {
var disableDialog = $(this);
// reset visibility
$('#disable-controller-service-scope-container').show();
$('#disable-controller-service-progress-container').hide();
// clear the dialog
$('#disable-controller-service-id').text('');
$('#disable-controller-service-name').text('');
// bulletins
$('#disable-controller-service-bulletins').removeClass('has-bulletins').removeData('bulletins').hide();
nfCommon.cleanUpTooltips($('#disable-controller-service-service-container'), '#disable-controller-service-bulletins');
// reset progress
$('div.disable-referencing-components').removeClass('ajax-loading ajax-complete ajax-error');
// referencing components
var referencingComponents = $('#disable-controller-service-referencing-components');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins');
referencingComponents.css('border-width', '0').empty();
}
}
});
// initialize the enable scope combo
$('#enable-controller-service-scope').combo({
options: [{
text: 'Service only',
value: config.serviceOnly,
description: 'Enable only this controller service'
}, {
text: 'Service and referencing components',
value: config.serviceAndReferencingComponents,
description: 'Enable this controller service and enable/start all referencing components'
}]
});
// initialize the enable service dialog
$('#enable-controller-service-dialog').modal({
headerText: 'Enable Controller Service',
scrollableContentStyle: 'scrollable',
handler: {
close: function () {
var enableDialog = $(this);
// reset visibility
$('#enable-controller-service-scope-container').show();
$('#enable-controller-service-progress-container').hide();
$('#enable-controller-service-progress li.referencing-component').show();
// clear the dialog
$('#enable-controller-service-id').text('');
$('#enable-controller-service-name').text('');
// bulletins
$('#enable-controller-service-bulletins').removeClass('has-bulletins').removeData('bulletins').hide();
nfCommon.cleanUpTooltips($('#enable-controller-service-service-container'), '#enable-controller-service-bulletins');
// reset progress
$('div.enable-referencing-components').removeClass('ajax-loading ajax-complete ajax-error');
// referencing components
var referencingComponents = $('#enable-controller-service-referencing-components');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
nfCommon.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins');
referencingComponents.css('border-width', '0').empty();
}
}
});
},
/**
* Shows the configuration dialog for the specified controller service.
*
* @argument {jQuery} serviceTable The controller service table
* @argument {object} controllerServiceEntity The controller service
*/
showConfiguration: function (serviceTable, controllerServiceEntity) {
if (nfCommon.isUndefined(currentTable)) {
currentTable = serviceTable;
}
var controllerServiceDialog = $('#controller-service-configuration');
controllerServiceDialog.find('.dialog-header .dialog-header-text').text('Configure Controller Service');
if (controllerServiceDialog.data('mode') !== config.edit || currentTable !== serviceTable) {
// update the visibility
$('#controller-service-configuration .controller-service-read-only').hide();
$('#controller-service-configuration .controller-service-editable').show();
// conditionally get the parameter deferred function
var getParameterDeferred = null;
if (nfCommon.isDefinedAndNotNull(controllerServiceEntity.parentGroupId)) {
getParameterDeferred = getParameters;
}
// initialize the property table
$('#controller-service-properties').propertytable('destroy').propertytable({
readOnly: false,
supportsGoTo: true,
dialogContainer: '#new-controller-service-property-container',
descriptorDeferred: getControllerServicePropertyDescriptor,
parameterDeferred: getParameterDeferred,
controllerServiceCreatedDeferred: function (response) {
var controllerServicesUri;
// calculate the correct uri
var createdControllerService = response.component;
if (nfCommon.isDefinedAndNotNull(createdControllerService.parentGroupId)) {
controllerServicesUri = config.urls.api + '/flow/process-groups/' + encodeURIComponent(createdControllerService.parentGroupId) + '/controller-services';
} else {
controllerServicesUri = config.urls.api + '/flow/controller/controller-services';
}
// load the controller services accordingly
return nfControllerServices.loadControllerServices(controllerServicesUri, serviceTable);
},
goToServiceDeferred: function () {
return goToServiceFromProperty(serviceTable);
},
getParameterContext: function (groupId) {
return getParameterContext(groupId);
}
});
// update the mode
controllerServiceDialog.data('mode', config.edit);
}
//track the current table
currentTable = serviceTable;
// reload the service in case the property descriptors have changed
var reloadService = $.ajax({
type: 'GET',
url: controllerServiceEntity.uri,
dataType: 'json'
});
// get the controller service history
var loadHistory = $.ajax({
type: 'GET',
url: '../nifi-api/flow/history/components/' + encodeURIComponent(controllerServiceEntity.id),
dataType: 'json'
});
// once everything is loaded, show the dialog
$.when(reloadService, loadHistory).done(function (serviceResponse, historyResponse) {
// get the updated controller service
controllerServiceEntity = serviceResponse[0];
var controllerService = controllerServiceEntity.component;
// get the controller service history
var controllerServiceHistory = historyResponse[0].componentHistory;
// record the controller service details
controllerServiceDialog.data('controllerServiceDetails', controllerServiceEntity);
// populate the controller service settings
nfCommon.populateField('controller-service-id', controllerService['id']);
nfCommon.populateField('controller-service-type', nfCommon.formatType(controllerService));
$('#controller-service-configuration').modal('setSubtitle', nfCommon.formatType(controllerService));
nfCommon.populateField('controller-service-bundle', nfCommon.formatBundle(controllerService['bundle']));
$('#controller-service-name').val(controllerService['name']);
$('#controller-service-comments').val(controllerService['comments']);
// set the implemented apis
if (!nfCommon.isEmpty(controllerService['controllerServiceApis'])) {
var formattedControllerServiceApis = nfCommon.getFormattedServiceApis(controllerService['controllerServiceApis']);
var serviceTips = nfCommon.formatUnorderedList(formattedControllerServiceApis);
$('#controller-service-compatible-apis').append(serviceTips);
} else {
$('#controller-service-compatible-apis').append('<span class="unset">None</span>');
}
// get the reference container
var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list
createReferencingComponents(serviceTable, referenceContainer, controllerServiceEntity);
var buttons = [{
buttonText: 'Apply',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
// close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow');
// save the controller service
saveControllerService(serviceTable, controllerServiceEntity).done(function (response) {
reloadControllerServiceReferences(serviceTable, response.component);
// close the details panel
controllerServiceDialog.modal('hide');
});
}
}
}, {
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
controllerServiceDialog.modal('hide');
}
}
}];
// determine if we should show the advanced button
if (nfCommon.isDefinedAndNotNull(controllerService.customUiUrl) && controllerService.customUiUrl !== '') {
buttons.push({
buttonText: 'Advanced',
clazz: 'fa fa-cog button-icon',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
var openCustomUi = function () {
// reset state and close the dialog manually to avoid hiding the faded background
controllerServiceDialog.modal('hide');
// close the settings dialog since the custom ui is also opened in the shell
$('#shell-close-button').click();
// show the custom ui
nfCustomUi.showCustomUi(controllerServiceEntity, controllerService.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the controller service
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
// show the settings
nfSettings.showSettings();
});
};
// close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow');
// determine if changes have been made
if (isSaveRequired()) {
// see if those changes should be saved
nfDialog.showYesNoDialog({
headerText: 'Save',
dialogContent: 'Save changes before opening the advanced configuration?',
noHandler: openCustomUi,
yesHandler: function () {
saveControllerService(serviceTable, controllerServiceEntity).done(function () {
// open the custom ui
openCustomUi();
});
}
});
} else {
// if there were no changes, simply open the custom ui
openCustomUi();
}
}
}
});
}
// set the button model
controllerServiceDialog.modal('setButtonModel', buttons);
// load the property table
$('#controller-service-properties')
.propertytable('setGroupId', controllerService.parentGroupId)
.propertytable('loadProperties', controllerService.properties, controllerService.descriptors, controllerServiceHistory.propertyHistory);
// show the details
controllerServiceDialog.modal('show');
// show the border if necessary
updateReferencingComponentsBorder(referenceContainer);
$('#controller-service-properties').propertytable('resetTableSize');
}).fail(nfErrorHandler.handleAjaxError);
},
/**
* Shows the controller service details in a read only dialog.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
showDetails: function (serviceTable, controllerServiceEntity) {
var controllerServiceDialog = $('#controller-service-configuration');
controllerServiceDialog.find('.dialog-header .dialog-header-text').text('Controller Service Details');
if (controllerServiceDialog.data('mode') !== config.readOnly) {
// update the visibility
$('#controller-service-configuration .controller-service-read-only').show();
$('#controller-service-configuration .controller-service-editable').hide();
// initialize the property table
$('#controller-service-properties').propertytable('destroy').propertytable({
supportsGoTo: true,
readOnly: true,
getParameterContext: function (groupId) {
return getParameterContext(groupId);
}
});
// update the mode
controllerServiceDialog.data('mode', config.readOnly);
}
// reload the service in case the property descriptors have changed
var reloadService = $.ajax({
type: 'GET',
url: controllerServiceEntity.uri,
dataType: 'json'
});
// get the controller service history
var loadHistory = $.ajax({
type: 'GET',
url: '../nifi-api/flow/history/components/' + encodeURIComponent(controllerServiceEntity.id),
dataType: 'json'
});
// once everything is loaded, show the dialog
$.when(reloadService, loadHistory).done(function (serviceResponse, historyResponse) {
// get the updated controller service
controllerServiceEntity = serviceResponse[0];
var controllerService = controllerServiceEntity.component;
// get the controller service history
var controllerServiceHistory = historyResponse[0].componentHistory;
// record the controller service details
controllerServiceDialog.data('controllerServiceDetails', controllerServiceEntity);
// populate the controller service settings
nfCommon.populateField('controller-service-id', controllerService['id']);
nfCommon.populateField('controller-service-type', nfCommon.formatType(controllerService));
nfCommon.populateField('controller-service-bundle', nfCommon.formatBundle(controllerService['bundle']));
nfCommon.populateField('read-only-controller-service-name', controllerService['name']);
nfCommon.populateField('read-only-controller-service-comments', controllerService['comments']);
// set the implemented apis
if (!nfCommon.isEmpty(controllerService['controllerServiceApis'])) {
var formattedControllerServiceApis = nfCommon.getFormattedServiceApis(controllerService['controllerServiceApis']);
var serviceTips = nfCommon.formatUnorderedList(formattedControllerServiceApis);
$('#controller-service-compatible-apis').append(serviceTips);
} else {
$('#controller-service-compatible-apis').append('<span class="unset">None</span>');
}
// get the reference container
var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list
createReferencingComponents(serviceTable, referenceContainer, controllerServiceEntity);
var buttons = [{
buttonText: 'Ok',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
// hide the dialog
controllerServiceDialog.modal('hide');
}
}
}];
// determine if we should show the advanced button
if (nfCommon.isDefinedAndNotNull(nfCustomUi) && nfCommon.isDefinedAndNotNull(controllerService.customUiUrl) && controllerService.customUiUrl !== '') {
buttons.push({
buttonText: 'Advanced',
clazz: 'fa fa-cog button-icon',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
// reset state and close the dialog manually to avoid hiding the faded background
controllerServiceDialog.modal('hide');
// close the settings dialog since the custom ui is also opened in the shell
$('#shell-close-button').click();
// show the custom ui
nfCustomUi.showCustomUi(controllerServiceEntity, controllerService.customUiUrl, false).done(function () {
nfSettings.showSettings();
});
}
}
});
}
// show the dialog
controllerServiceDialog.modal('setButtonModel', buttons);
// load the property table
$('#controller-service-properties')
.propertytable('setGroupId', controllerService.parentGroupId)
.propertytable('loadProperties', controllerService.properties, controllerService.descriptors, controllerServiceHistory.propertyHistory);
// show the details
controllerServiceDialog.modal('show');
// show the border if necessary
updateReferencingComponentsBorder(referenceContainer);
$('#controller-service-properties').propertytable('resetTableSize');
});
},
/**
* Enables the specified controller service.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
enable: function (serviceTable, controllerServiceEntity) {
showEnableControllerServiceDialog(serviceTable, controllerServiceEntity);
},
/**
* Disables the specified controller service.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
disable: function (serviceTable, controllerServiceEntity) {
showDisableControllerServiceDialog(serviceTable, controllerServiceEntity);
},
/**
* Reloads the services that the specified comonent references. This is
* necessary because the specified component state is reflected in the
* referenced service referencing components.
*
* @param {jQuery} serviceTable
* @param {object} component
*/
reloadReferencedServices: function (serviceTable, component) {
$.each(getReferencedServices(component), function (_, referencedServiceId) {
reloadControllerService(serviceTable, referencedServiceId);
});
},
/**
* Prompts the user before attempting to delete the specified controller service.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
promptToDeleteController: function (serviceTable, controllerServiceEntity) {
// prompt for deletion
nfDialog.showYesNoDialog({
headerText: 'Delete Controller Service',
dialogContent: 'Delete controller service \'' + nfCommon.escapeHtml(controllerServiceEntity.component.name) + '\'?',
yesHandler: function () {
nfControllerService.remove(serviceTable, controllerServiceEntity);
}
});
},
/**
* Deletes the specified controller service.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
remove: function (serviceTable, controllerServiceEntity) {
// prompt for removal?
var revision = nfClient.getRevision(controllerServiceEntity);
$.ajax({
type: 'DELETE',
url: controllerServiceEntity.uri + '?' + $.param({
'version': revision.version,
'clientId': revision.clientId,
'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
}),
dataType: 'json'
}).done(function (response) {
// remove the service
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
controllerServiceData.deleteItem(controllerServiceEntity.id);
// reload the as necessary
if (controllerServiceEntity.permissions.canRead) {
reloadControllerServiceReferences(serviceTable, controllerServiceEntity.component);
}
}).fail(nfErrorHandler.handleAjaxError);
}
};
return nfControllerService;
}));