http://git-wip-us.apache.org/repos/asf/nifi/blob/dc934cbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js index 7da8593..2826e3a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js @@ -15,1446 +15,1508 @@ * limitations under the License. */ -/* global nf, top, Slick */ - -nf.ng.ProvenanceTable = function (provenanceLineageCtrl) { +/* global nf, top, define, module, require, exports */ + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery', + 'Slick', + 'nf.Common', + 'nf.Dialog', + 'nf.ErrorHandler', + 'nf.Storage', + 'nf.ng.Bridge'], + function ($, Slick, common, dialog, errorHandler, storage, angularBridge) { + return (nf.ng.ProvenanceTable = factory($, Slick, common, dialog, errorHandler, storage, angularBridge)); + }); + } else if (typeof exports === 'object' && typeof module === 'object') { + module.exports = (nf.ng.ProvenanceTable = + factory(require('jquery'), + require('Slick'), + require('nf.Common'), + require('nf.Dialog'), + require('nf.ErrorHandler'), + require('nf.Storage'), + require('nf.ng.Bridge'))); + } else { + nf.ng.ProvenanceTable = factory(root.$, + root.Slick, + root.nf.Common, + root.nf.Dialog, + root.nf.ErrorHandler, + root.nf.Storage, + root.nf.ng.Bridge); + } +}(this, function ($, Slick, common, dialog, errorHandler, storage, angularBridge) { 'use strict'; - /** - * Configuration object used to hold a number of configuration items. - */ - var config = { - maxResults: 1000, - defaultStartTime: '00:00:00', - defaultEndTime: '23:59:59', - styles: { - hidden: 'hidden' - }, - urls: { - searchOptions: '../nifi-api/provenance/search-options', - replays: '../nifi-api/provenance-events/replays', - provenance: '../nifi-api/provenance', - provenanceEvents: '../nifi-api/provenance-events/', - clusterSearch: '../nifi-api/flow/cluster/search-results', - d3Script: 'js/d3/d3.min.js', - lineageScript: 'js/nf/provenance/nf-provenance-lineage.js', - uiExtensionToken: '../nifi-api/access/ui-extension-token', - downloadToken: '../nifi-api/access/download-token' - } - }; + var nfProvenanceTable = function (provenanceLineageCtrl) { + 'use strict'; - /** - * The last search performed - */ - var cachedQuery = {}; - - /** - * Downloads the content for the provenance event that is currently loaded in the specified direction. - * - * @param {string} direction - */ - var downloadContent = function (direction) { - var eventId = $('#provenance-event-id').text(); - - // build the url - var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); - - // perform the request once we've received a token - nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) { - var parameters = {}; - - // conditionally include the ui extension token - if (!nf.Common.isBlank(downloadToken)) { - parameters['access_token'] = downloadToken; + /** + * Configuration object used to hold a number of configuration items. + */ + var config = { + maxResults: 1000, + defaultStartTime: '00:00:00', + defaultEndTime: '23:59:59', + styles: { + hidden: 'hidden' + }, + urls: { + searchOptions: '../nifi-api/provenance/search-options', + replays: '../nifi-api/provenance-events/replays', + provenance: '../nifi-api/provenance', + provenanceEvents: '../nifi-api/provenance-events/', + clusterSearch: '../nifi-api/flow/cluster/search-results', + d3Script: 'js/d3/d3.min.js', + lineageScript: 'js/nf/provenance/nf-provenance-lineage.js', + uiExtensionToken: '../nifi-api/access/ui-extension-token', + downloadToken: '../nifi-api/access/download-token' } + }; - // conditionally include the cluster node id - var clusterNodeId = $('#provenance-event-cluster-node-id').text(); - if (!nf.Common.isBlank(clusterNodeId)) { - parameters['clusterNodeId'] = clusterNodeId; - } + /** + * The last search performed + */ + var cachedQuery = {}; - // open the url - if ($.isEmptyObject(parameters)) { - window.open(dataUri); - } else { - window.open(dataUri + '?' + $.param(parameters)); - } - }).fail(function () { - nf.Dialog.showOkDialog({ - headerText: 'Provenance', - dialogContent: 'Unable to generate access token for downloading content.' - }); - }); - }; + /** + * Downloads the content for the provenance event that is currently loaded in the specified direction. + * + * @param {string} direction + */ + var downloadContent = function (direction) { + var eventId = $('#provenance-event-id').text(); - /** - * Views the content for the provenance event that is currently loaded in the specified direction. - * - * @param {string} direction - */ - var viewContent = function (direction) { - var controllerUri = $('#nifi-controller-uri').text(); - var eventId = $('#provenance-event-id').text(); - - // build the uri to the data - var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); - - // generate tokens as necessary - var getAccessTokens = $.Deferred(function (deferred) { - if (nf.Storage.hasItem('jwt')) { - // generate a token for the ui extension and another for the callback - var uiExtensionToken = $.ajax({ - type: 'POST', - url: config.urls.uiExtensionToken - }); - var downloadToken = $.ajax({ - type: 'POST', - url: config.urls.downloadToken + // build the url + var dataUri = config.urls.provenanceEvents + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); + + // perform the request once we've received a token + common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) { + var parameters = {}; + + // conditionally include the ui extension token + if (!common.isBlank(downloadToken)) { + parameters['access_token'] = downloadToken; + } + + // conditionally include the cluster node id + var clusterNodeId = $('#provenance-event-cluster-node-id').text(); + if (!common.isBlank(clusterNodeId)) { + parameters['clusterNodeId'] = clusterNodeId; + } + + // open the url + if ($.isEmptyObject(parameters)) { + window.open(dataUri); + } else { + window.open(dataUri + '?' + $.param(parameters)); + } + }).fail(function () { + dialog.showOkDialog({ + headerText: 'Provenance', + dialogContent: 'Unable to generate access token for downloading content.' }); + }); + }; - // wait for each token - $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) { - var uiExtensionToken = uiExtensionTokenResult[0]; - var downloadToken = downloadTokenResult[0]; - deferred.resolve(uiExtensionToken, downloadToken); - }).fail(function () { - nf.Dialog.showOkDialog({ - headerText: 'Provenance', - dialogContent: 'Unable to generate access token for viewing content.' + /** + * Views the content for the provenance event that is currently loaded in the specified direction. + * + * @param {string} direction + */ + var viewContent = function (direction) { + var controllerUri = $('#nifi-controller-uri').text(); + var eventId = $('#provenance-event-id').text(); + + // build the uri to the data + var dataUri = controllerUri + 'provenance-events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); + + // generate tokens as necessary + var getAccessTokens = $.Deferred(function (deferred) { + if (storage.hasItem('jwt')) { + // generate a token for the ui extension and another for the callback + var uiExtensionToken = $.ajax({ + type: 'POST', + url: config.urls.uiExtensionToken + }); + var downloadToken = $.ajax({ + type: 'POST', + url: config.urls.downloadToken }); - deferred.reject(); - }); - } else { - deferred.resolve('', ''); - } - }).promise(); - // perform the request after we've received the tokens - getAccessTokens.done(function (uiExtensionToken, downloadToken) { - var dataUriParameters = {}; + // wait for each token + $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) { + var uiExtensionToken = uiExtensionTokenResult[0]; + var downloadToken = downloadTokenResult[0]; + deferred.resolve(uiExtensionToken, downloadToken); + }).fail(function () { + dialog.showOkDialog({ + headerText: 'Provenance', + dialogContent: 'Unable to generate access token for viewing content.' + }); + deferred.reject(); + }); + } else { + deferred.resolve('', ''); + } + }).promise(); - // conditionally include the cluster node id - var clusterNodeId = $('#provenance-event-cluster-node-id').text(); - if (!nf.Common.isBlank(clusterNodeId)) { - dataUriParameters['clusterNodeId'] = clusterNodeId; - } + // perform the request after we've received the tokens + getAccessTokens.done(function (uiExtensionToken, downloadToken) { + var dataUriParameters = {}; - // include the download token if applicable - if (!nf.Common.isBlank(downloadToken)) { - dataUriParameters['access_token'] = downloadToken; - } + // conditionally include the cluster node id + var clusterNodeId = $('#provenance-event-cluster-node-id').text(); + if (!common.isBlank(clusterNodeId)) { + dataUriParameters['clusterNodeId'] = clusterNodeId; + } - // include parameters if necessary - if ($.isEmptyObject(dataUriParameters) === false) { - dataUri = dataUri + '?' + $.param(dataUriParameters); - } + // include the download token if applicable + if (!common.isBlank(downloadToken)) { + dataUriParameters['access_token'] = downloadToken; + } - // open the content viewer - var contentViewerUrl = $('#nifi-content-viewer-url').text(); + // include parameters if necessary + if ($.isEmptyObject(dataUriParameters) === false) { + dataUri = dataUri + '?' + $.param(dataUriParameters); + } - // if there's already a query string don't add another ?... this assumes valid - // input meaning that if the url has already included a ? it also contains at - // least one query parameter - if (contentViewerUrl.indexOf('?') === -1) { - contentViewerUrl += '?'; - } else { - contentViewerUrl += '&'; - } + // open the content viewer + var contentViewerUrl = $('#nifi-content-viewer-url').text(); - var contentViewerParameters = { - 'ref': dataUri - }; + // if there's already a query string don't add another ?... this assumes valid + // input meaning that if the url has already included a ? it also contains at + // least one query parameter + if (contentViewerUrl.indexOf('?') === -1) { + contentViewerUrl += '?'; + } else { + contentViewerUrl += '&'; + } - // include the download token if applicable - if (!nf.Common.isBlank(uiExtensionToken)) { - contentViewerParameters['access_token'] = uiExtensionToken; - } + var contentViewerParameters = { + 'ref': dataUri + }; - // open the content viewer - window.open(contentViewerUrl + $.param(contentViewerParameters)); - }); - }; + // include the download token if applicable + if (!common.isBlank(uiExtensionToken)) { + contentViewerParameters['access_token'] = uiExtensionToken; + } - /** - * Initializes the details dialog. - */ - var initDetailsDialog = function () { - // initialize the properties tabs - $('#event-details-tabs').tabbs({ - tabStyle: 'tab', - selectedTabStyle: 'selected-tab', - scrollableTabContentStyle: 'scrollable', - tabs: [{ - name: 'Details', - tabContentId: 'event-details-tab-content' - }, { - name: 'Attributes', - tabContentId: 'attributes-tab-content' - }, { - name: 'Content', - tabContentId: 'content-tab-content' - }] - }); - - $('#event-details-dialog').modal({ - scrollableContentStyle: 'scrollable', - headerText: 'Provenance Event', - buttons: [{ - buttonText: 'Ok', - color: { - base: '#728E9B', - hover: '#004849', - text: '#ffffff' - }, + // open the content viewer + window.open(contentViewerUrl + $.param(contentViewerParameters)); + }); + }; + + /** + * Initializes the details dialog. + */ + var initDetailsDialog = function () { + // initialize the properties tabs + $('#event-details-tabs').tabbs({ + tabStyle: 'tab', + selectedTabStyle: 'selected-tab', + scrollableTabContentStyle: 'scrollable', + tabs: [{ + name: 'Details', + tabContentId: 'event-details-tab-content' + }, { + name: 'Attributes', + tabContentId: 'attributes-tab-content' + }, { + name: 'Content', + tabContentId: 'content-tab-content' + }] + }); + + $('#event-details-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Provenance Event', + buttons: [{ + buttonText: 'Ok', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + handler: { + click: function () { + $('#event-details-dialog').modal('hide'); + } + } + }], handler: { - click: function () { - $('#event-details-dialog').modal('hide'); + close: function () { + // clear the details + $('#additional-provenance-details').empty(); + $('#attributes-container').empty(); + $('#parent-flowfiles-container').empty(); + $('#child-flowfiles-container').empty(); + $('#provenance-event-cluster-node-id').text(''); + $('#modified-attribute-toggle').removeClass('checkbox-checked').addClass('checkbox-unchecked'); + }, + open: function () { + common.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0)); } } - }], - handler: { - close: function () { - // clear the details - $('#additional-provenance-details').empty(); - $('#attributes-container').empty(); - $('#parent-flowfiles-container').empty(); - $('#child-flowfiles-container').empty(); - $('#provenance-event-cluster-node-id').text(''); - $('#modified-attribute-toggle').removeClass('checkbox-checked').addClass('checkbox-unchecked'); - }, - open: function () { - nf.Common.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0)); - } - } - }); - - // toggle which attributes are visible - $('#modified-attribute-toggle').on('click', function () { - var unmodifiedAttributes = $('#attributes-container div.attribute-unmodified'); - if (unmodifiedAttributes.is(':visible')) { - $('#attributes-container div.attribute-unmodified').hide(); - } else { - $('#attributes-container div.attribute-unmodified').show(); - } - }); - - // input download - $('#input-content-download').on('click', function () { - downloadContent('input'); - }); - - // output download - $('#output-content-download').on('click', function () { - downloadContent('output'); - }); - - // if a content viewer url is specified, use it - if (nf.Common.isContentViewConfigured()) { - // input view - $('#input-content-view').on('click', function () { - viewContent('input'); }); - // output view - $('#output-content-view').on('click', function () { - viewContent('output'); + // toggle which attributes are visible + $('#modified-attribute-toggle').on('click', function () { + var unmodifiedAttributes = $('#attributes-container div.attribute-unmodified'); + if (unmodifiedAttributes.is(':visible')) { + $('#attributes-container div.attribute-unmodified').hide(); + } else { + $('#attributes-container div.attribute-unmodified').show(); + } }); - } - // handle the replay and downloading - $('#replay-content').on('click', function () { - var replayEntity = { - 'eventId': $('#provenance-event-id').text() - }; + // input download + $('#input-content-download').on('click', function () { + downloadContent('input'); + }); - // conditionally include the cluster node id - var clusterNodeId = $('#provenance-event-cluster-node-id').text(); - if (!nf.Common.isBlank(clusterNodeId)) { - replayEntity['clusterNodeId'] = clusterNodeId; - } + // output download + $('#output-content-download').on('click', function () { + downloadContent('output'); + }); - $.ajax({ - type: 'POST', - url: config.urls.replays, - data: JSON.stringify(replayEntity), - dataType: 'json', - contentType: 'application/json' - }).done(function (response) { - nf.Dialog.showOkDialog({ - headerText: 'Provenance', - dialogContent: 'Successfully submitted replay request.' + // if a content viewer url is specified, use it + if (common.isContentViewConfigured()) { + // input view + $('#input-content-view').on('click', function () { + viewContent('input'); }); - }).fail(nf.Common.handleAjaxError); - $('#event-details-dialog').modal('hide'); - }); + // output view + $('#output-content-view').on('click', function () { + viewContent('output'); + }); + } - // show the replay panel - $('#replay-details').show(); - }; + // handle the replay and downloading + $('#replay-content').on('click', function () { + var replayEntity = { + 'eventId': $('#provenance-event-id').text() + }; - /** - * Initializes the search dialog. - * - * @param {boolean} isClustered Whether or not this NiFi clustered - */ - var initSearchDialog = function (isClustered, provenanceTableCtrl) { - // configure the start and end date picker - $('#provenance-search-start-date, #provenance-search-end-date').datepicker({ - showAnim: '', - showOtherMonths: true, - selectOtherMonths: true - }); - - // initialize the default start date/time - $('#provenance-search-start-date').datepicker('setDate', '+0d'); - $('#provenance-search-end-date').datepicker('setDate', '+0d'); - $('#provenance-search-start-time').val('00:00:00'); - $('#provenance-search-end-time').val('23:59:59'); - - // initialize the default file sizes - $('#provenance-search-minimum-file-size').val(''); - $('#provenance-search-maximum-file-size').val(''); - - // allow users to be able to search a specific node - if (isClustered) { - // make the dialog larger to support the select location - $('#provenance-search-dialog').height(575); - - // get the nodes in the cluster - $.ajax({ - type: 'GET', - url: config.urls.clusterSearch, - dataType: 'json' - }).done(function (response) { - var nodeResults = response.nodeResults; - - // create the searchable options - var searchableOptions = [{ - text: 'cluster', - value: null - }]; - - // sort the nodes - nodeResults.sort(function (a, b) { - var compA = a.address.toUpperCase(); - var compB = b.address.toUpperCase(); - return (compA < compB) ? -1 : (compA > compB) ? 1 : 0; - }); + // conditionally include the cluster node id + var clusterNodeId = $('#provenance-event-cluster-node-id').text(); + if (!common.isBlank(clusterNodeId)) { + replayEntity['clusterNodeId'] = clusterNodeId; + } - // add each node - $.each(nodeResults, function (_, nodeResult) { - searchableOptions.push({ - text: nodeResult.address, - value: nodeResult.id + $.ajax({ + type: 'POST', + url: config.urls.replays, + data: JSON.stringify(replayEntity), + dataType: 'json', + contentType: 'application/json' + }).done(function (response) { + dialog.showOkDialog({ + headerText: 'Provenance', + dialogContent: 'Successfully submitted replay request.' }); - }); + }).fail(errorHandler.handleAjaxError); - // populate the combo - $('#provenance-search-location').combo({ - options: searchableOptions - }); - }).fail(nf.Common.handleAjaxError); + $('#event-details-dialog').modal('hide'); + }); - // show the node search combo - $('#provenance-search-location-container').show(); - } + // show the replay panel + $('#replay-details').show(); + }; - // configure the search dialog - $('#provenance-search-dialog').modal({ - scrollableContentStyle: 'scrollable', - headerText: 'Search Events', - buttons: [{ - buttonText: 'Search', - color: { - base: '#728E9B', - hover: '#004849', - text: '#ffffff' - }, - handler: { - click: function () { - $('#provenance-search-dialog').modal('hide'); - - var search = {}; - - // extract the start date time - var startDate = $.trim($('#provenance-search-start-date').val()); - var startTime = $.trim($('#provenance-search-start-time').val()); - if (startDate !== '') { - if (startTime === '') { - startTime = config.defaultStartTime; - $('#provenance-search-start-time').val(startTime); - } - search['startDate'] = startDate + ' ' + startTime + ' ' + $('.timezone:first').text(); - } + /** + * Initializes the search dialog. + * + * @param {boolean} isClustered Whether or not this NiFi clustered + */ + var initSearchDialog = function (isClustered, provenanceTableCtrl) { + // configure the start and end date picker + $('#provenance-search-start-date, #provenance-search-end-date').datepicker({ + showAnim: '', + showOtherMonths: true, + selectOtherMonths: true + }); - // extract the end date time - var endDate = $.trim($('#provenance-search-end-date').val()); - var endTime = $.trim($('#provenance-search-end-time').val()); - if (endDate !== '') { - if (endTime === '') { - endTime = config.defaultEndTime; - $('#provenance-search-end-time').val(endTime); - } - search['endDate'] = endDate + ' ' + endTime + ' ' + $('.timezone:first').text(); - } + // initialize the default start date/time + $('#provenance-search-start-date').datepicker('setDate', '+0d'); + $('#provenance-search-end-date').datepicker('setDate', '+0d'); + $('#provenance-search-start-time').val('00:00:00'); + $('#provenance-search-end-time').val('23:59:59'); - // extract the min/max file size - var minFileSize = $.trim($('#provenance-search-minimum-file-size').val()); - if (minFileSize !== '') { - search['minimumFileSize'] = minFileSize; - } + // initialize the default file sizes + $('#provenance-search-minimum-file-size').val(''); + $('#provenance-search-maximum-file-size').val(''); - var maxFileSize = $.trim($('#provenance-search-maximum-file-size').val()); - if (maxFileSize !== '') { - search['maximumFileSize'] = maxFileSize; - } + // allow users to be able to search a specific node + if (isClustered) { + // make the dialog larger to support the select location + $('#provenance-search-dialog').height(575); + + // get the nodes in the cluster + $.ajax({ + type: 'GET', + url: config.urls.clusterSearch, + dataType: 'json' + }).done(function (response) { + var nodeResults = response.nodeResults; + + // create the searchable options + var searchableOptions = [{ + text: 'cluster', + value: null + }]; + + // sort the nodes + nodeResults.sort(function (a, b) { + var compA = a.address.toUpperCase(); + var compB = b.address.toUpperCase(); + return (compA < compB) ? -1 : (compA > compB) ? 1 : 0; + }); - // limit search to a specific node - if (isClustered) { - var searchLocation = $('#provenance-search-location').combo('getSelectedOption'); - if (searchLocation.value !== null) { - search['clusterNodeId'] = searchLocation.value; - } - } + // add each node + $.each(nodeResults, function (_, nodeResult) { + searchableOptions.push({ + text: nodeResult.address, + value: nodeResult.id + }); + }); - // add the search criteria - search['searchTerms'] = getSearchCriteria(); + // populate the combo + $('#provenance-search-location').combo({ + options: searchableOptions + }); + }).fail(errorHandler.handleAjaxError); - // reload the table - provenanceTableCtrl.loadProvenanceTable(search); - } - } - }, - { - buttonText: 'Cancel', + // show the node search combo + $('#provenance-search-location-container').show(); + } + + // configure the search dialog + $('#provenance-search-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Search Events', + buttons: [{ + buttonText: 'Search', color: { - base: '#E3E8EB', - hover: '#C7D2D7', - text: '#004849' + base: '#728E9B', + hover: '#004849', + text: '#ffffff' }, handler: { click: function () { $('#provenance-search-dialog').modal('hide'); + + var search = {}; + + // extract the start date time + var startDate = $.trim($('#provenance-search-start-date').val()); + var startTime = $.trim($('#provenance-search-start-time').val()); + if (startDate !== '') { + if (startTime === '') { + startTime = config.defaultStartTime; + $('#provenance-search-start-time').val(startTime); + } + search['startDate'] = startDate + ' ' + startTime + ' ' + $('.timezone:first').text(); + } + + // extract the end date time + var endDate = $.trim($('#provenance-search-end-date').val()); + var endTime = $.trim($('#provenance-search-end-time').val()); + if (endDate !== '') { + if (endTime === '') { + endTime = config.defaultEndTime; + $('#provenance-search-end-time').val(endTime); + } + search['endDate'] = endDate + ' ' + endTime + ' ' + $('.timezone:first').text(); + } + + // extract the min/max file size + var minFileSize = $.trim($('#provenance-search-minimum-file-size').val()); + if (minFileSize !== '') { + search['minimumFileSize'] = minFileSize; + } + + var maxFileSize = $.trim($('#provenance-search-maximum-file-size').val()); + if (maxFileSize !== '') { + search['maximumFileSize'] = maxFileSize; + } + + // limit search to a specific node + if (isClustered) { + var searchLocation = $('#provenance-search-location').combo('getSelectedOption'); + if (searchLocation.value !== null) { + search['clusterNodeId'] = searchLocation.value; + } + } + + // add the search criteria + search['searchTerms'] = getSearchCriteria(); + + // reload the table + provenanceTableCtrl.loadProvenanceTable(search); } } - }] - }); - - return $.ajax({ - type: 'GET', - url: config.urls.searchOptions, - dataType: 'json' - }).done(function (response) { - var provenanceOptions = response.provenanceOptions; - - // load all searchable fields - $.each(provenanceOptions.searchableFields, function (_, field) { - appendSearchableField(field); + }, + { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + $('#provenance-search-dialog').modal('hide'); + } + } + }] }); - }); - }; - /** - * Initializes the provenance query dialog. - */ - var initProvenanceQueryDialog = function () { - // initialize the dialog - $('#provenance-query-dialog').modal({ - scrollableContentStyle: 'scrollable', - headerText: 'Searching provenance events...' - }); - }; + return $.ajax({ + type: 'GET', + url: config.urls.searchOptions, + dataType: 'json' + }).done(function (response) { + var provenanceOptions = response.provenanceOptions; - /** - * Appends the specified searchable field to the search dialog. - * - * @param {type} field The searchable field - */ - var appendSearchableField = function (field) { - var searchableField = $('<div class="searchable-field"></div>').appendTo('#searchable-fields-container'); - $('<span class="searchable-field-id hidden"></span>').text(field.id).appendTo(searchableField); - $('<div class="searchable-field-name"></div>').text(field.label).appendTo(searchableField); - $('<div class="searchable-field-value"><input type="text" class="searchable-field-input"/></div>').appendTo(searchableField); - $('<div class="clear"></div>').appendTo(searchableField); - - // make the searchable accessible for populating - if (field.id === 'ProcessorID') { - searchableField.find('input').addClass('searchable-component-id'); - } else if (field.id === 'FlowFileUUID') { - searchableField.find('input').addClass('searchable-flowfile-uuid'); - } + // load all searchable fields + $.each(provenanceOptions.searchableFields, function (_, field) { + appendSearchableField(field); + }); + }); + }; - // ensure the no searchable fields message is hidden - $('#no-searchable-fields').hide(); - }; + /** + * Initializes the provenance query dialog. + */ + var initProvenanceQueryDialog = function () { + // initialize the dialog + $('#provenance-query-dialog').modal({ + scrollableContentStyle: 'scrollable', + headerText: 'Searching provenance events...' + }); + }; - /** - * Gets the search criteria that the user has specified. - */ - var getSearchCriteria = function () { - var searchCriteria = {}; - $('#searchable-fields-container').children('div.searchable-field').each(function () { - var searchableField = $(this); - var fieldId = searchableField.children('span.searchable-field-id').text(); - var searchValue = $.trim(searchableField.find('input.searchable-field-input').val()); - - // if the field isn't blank include it in the search - if (!nf.Common.isBlank(searchValue)) { - searchCriteria[fieldId] = searchValue; + /** + * Appends the specified searchable field to the search dialog. + * + * @param {type} field The searchable field + */ + var appendSearchableField = function (field) { + var searchableField = $('<div class="searchable-field"></div>').appendTo('#searchable-fields-container'); + $('<span class="searchable-field-id hidden"></span>').text(field.id).appendTo(searchableField); + $('<div class="searchable-field-name"></div>').text(field.label).appendTo(searchableField); + $('<div class="searchable-field-value"><input type="text" class="searchable-field-input"/></div>').appendTo(searchableField); + $('<div class="clear"></div>').appendTo(searchableField); + + // make the searchable accessible for populating + if (field.id === 'ProcessorID') { + searchableField.find('input').addClass('searchable-component-id'); + } else if (field.id === 'FlowFileUUID') { + searchableField.find('input').addClass('searchable-flowfile-uuid'); } - }); - return searchCriteria; - }; - /** - * Initializes the provenance table. - * - * @param {boolean} isClustered Whether or not this instance is clustered - */ - var initProvenanceTable = function (isClustered, provenanceTableCtrl) { - // define the function for filtering the list - $('#provenance-filter').keyup(function () { - applyFilter(); - }); - - // filter options - var filterOptions = [{ - text: 'by component name', - value: 'componentName' - }, { - text: 'by component type', - value: 'componentType' - }, { - text: 'by type', - value: 'eventType' - }]; - - // if clustered, allowing filtering by node id - if (isClustered) { - filterOptions.push({ - text: 'by node', - value: 'clusterNodeAddress' + // ensure the no searchable fields message is hidden + $('#no-searchable-fields').hide(); + }; + + /** + * Gets the search criteria that the user has specified. + */ + var getSearchCriteria = function () { + var searchCriteria = {}; + $('#searchable-fields-container').children('div.searchable-field').each(function () { + var searchableField = $(this); + var fieldId = searchableField.children('span.searchable-field-id').text(); + var searchValue = $.trim(searchableField.find('input.searchable-field-input').val()); + + // if the field isn't blank include it in the search + if (!common.isBlank(searchValue)) { + searchCriteria[fieldId] = searchValue; + } }); - } + return searchCriteria; + }; - // initialize the filter combo - $('#provenance-filter-type').combo({ - options: filterOptions, - select: function (option) { + /** + * Initializes the provenance table. + * + * @param {boolean} isClustered Whether or not this instance is clustered + */ + var initProvenanceTable = function (isClustered, provenanceTableCtrl) { + // define the function for filtering the list + $('#provenance-filter').keyup(function () { applyFilter(); - } - }); - - // clear the current search - $('#clear-provenance-search').click(function () { - // clear each searchable field - $('#searchable-fields-container').find('input.searchable-field-input').each(function () { - $(this).val(''); }); - // reset the default start date/time - $('#provenance-search-start-date').datepicker('setDate', '+0d'); - $('#provenance-search-end-date').datepicker('setDate', '+0d'); - $('#provenance-search-start-time').val('00:00:00'); - $('#provenance-search-end-time').val('23:59:59'); - - // reset the minimum and maximum file size - $('#provenance-search-minimum-file-size').val(''); - $('#provenance-search-maximum-file-size').val(''); + // filter options + var filterOptions = [{ + text: 'by component name', + value: 'componentName' + }, { + text: 'by component type', + value: 'componentType' + }, { + text: 'by type', + value: 'eventType' + }]; - // if we are clustered reset the selected option + // if clustered, allowing filtering by node id if (isClustered) { - $('#provenance-search-location').combo('setSelectedOption', { - text: 'cluster' + filterOptions.push({ + text: 'by node', + value: 'clusterNodeAddress' }); } - // reset the stored query - cachedQuery = {}; - - // reload the table - provenanceTableCtrl.loadProvenanceTable(); - }); - - // add hover effect and click handler for opening the dialog - $('#provenance-search-button').click(function () { - $('#provenance-search-dialog').modal('show'); - - // adjust the field width for a potential scrollbar - var searchFieldContainer = $('#searchable-fields-container'); - if (searchFieldContainer.get(0).scrollHeight > searchFieldContainer.innerHeight()) { - $('input.searchable-field-input').width(245); - } else { - $('input.searchable-field-input').width(260); - } - }); + // initialize the filter combo + $('#provenance-filter-type').combo({ + options: filterOptions, + select: function (option) { + applyFilter(); + } + }); - // define a custom formatter for the more details column - var moreDetailsFormatter = function (row, cell, value, columnDef, dataContext) { - return '<div title="View Details" class="pointer show-event-details fa fa-info-circle"></div>'; - }; + // clear the current search + $('#clear-provenance-search').click(function () { + // clear each searchable field + $('#searchable-fields-container').find('input.searchable-field-input').each(function () { + $(this).val(''); + }); - // define how general values are formatted - var valueFormatter = function (row, cell, value, columnDef, dataContext) { - return nf.Common.formatValue(value); - }; + // reset the default start date/time + $('#provenance-search-start-date').datepicker('setDate', '+0d'); + $('#provenance-search-end-date').datepicker('setDate', '+0d'); + $('#provenance-search-start-time').val('00:00:00'); + $('#provenance-search-end-time').val('23:59:59'); - // determine if the this page is in the shell - var isInShell = (top !== window); + // reset the minimum and maximum file size + $('#provenance-search-minimum-file-size').val(''); + $('#provenance-search-maximum-file-size').val(''); - // define how the column is formatted - var showLineageFormatter = function (row, cell, value, columnDef, dataContext) { - var markup = ''; + // if we are clustered reset the selected option + if (isClustered) { + $('#provenance-search-location').combo('setSelectedOption', { + text: 'cluster' + }); + } - // conditionally include the cluster node id - if (nf.Common.SUPPORTS_SVG) { - markup += '<div title="Show Lineage" class="pointer show-lineage icon icon-lineage" style="margin-right: 3px;"></div>'; - } + // reset the stored query + cachedQuery = {}; - // conditionally support going to the component - var isRemotePort = dataContext.componentType === 'Remote Input Port' || dataContext.componentType === 'Remote Output Port'; - if (isInShell && nf.Common.isDefinedAndNotNull(dataContext.groupId) && isRemotePort === false) { - markup += '<div class="pointer go-to fa fa-long-arrow-right" title="Go To"></div>'; - } + // reload the table + provenanceTableCtrl.loadProvenanceTable(); + }); - return markup; - }; + // add hover effect and click handler for opening the dialog + $('#provenance-search-button').click(function () { + $('#provenance-search-dialog').modal('show'); - // initialize the provenance table - var provenanceColumns = [ - { - id: 'moreDetails', - name: ' ', - sortable: false, - resizable: false, - formatter: moreDetailsFormatter, - width: 50, - maxWidth: 50 - }, - { - id: 'eventTime', - name: 'Date/Time', - field: 'eventTime', - sortable: true, - defaultSortAsc: false, - resizable: true - }, - {id: 'eventType', name: 'Type', field: 'eventType', sortable: true, resizable: true}, - {id: 'flowFileUuid', name: 'FlowFile Uuid', field: 'flowFileUuid', sortable: true, resizable: true}, - {id: 'fileSize', name: 'Size', field: 'fileSize', sortable: true, defaultSortAsc: false, resizable: true}, - { - id: 'componentName', - name: 'Component Name', - field: 'componentName', - sortable: true, - resizable: true, - formatter: valueFormatter - }, - {id: 'componentType', name: 'Component Type', field: 'componentType', sortable: true, resizable: true} - ]; - - // conditionally show the cluster node identifier - if (isClustered) { - provenanceColumns.push({ - id: 'clusterNodeAddress', - name: 'Node', - field: 'clusterNodeAddress', - sortable: true, - resizable: true + // adjust the field width for a potential scrollbar + var searchFieldContainer = $('#searchable-fields-container'); + if (searchFieldContainer.get(0).scrollHeight > searchFieldContainer.innerHeight()) { + $('input.searchable-field-input').width(245); + } else { + $('input.searchable-field-input').width(260); + } }); - } - // conditionally show the action column - if (nf.Common.SUPPORTS_SVG || isInShell) { - provenanceColumns.push({ - id: 'actions', - name: ' ', - formatter: showLineageFormatter, - resizable: false, - sortable: false, - width: 50, - maxWidth: 50 - }); - } + // define a custom formatter for the more details column + var moreDetailsFormatter = function (row, cell, value, columnDef, dataContext) { + return '<div title="View Details" class="pointer show-event-details fa fa-info-circle"></div>'; + }; - var provenanceOptions = { - forceFitColumns: true, - enableTextSelectionOnCells: true, - enableCellNavigation: true, - enableColumnReorder: false, - autoEdit: false, - multiSelect: false, - rowHeight: 24 - }; + // define how general values are formatted + var valueFormatter = function (row, cell, value, columnDef, dataContext) { + return common.formatValue(value); + }; - // create the remote model - var provenanceData = new Slick.Data.DataView({ - inlineFilters: false - }); - provenanceData.setItems([]); - provenanceData.setFilterArgs({ - searchString: '', - property: 'name' - }); - provenanceData.setFilter(filter); - - // initialize the sort - sort({ - columnId: 'eventTime', - sortAsc: false - }, provenanceData); - - // initialize the grid - var provenanceGrid = new Slick.Grid('#provenance-table', provenanceData, provenanceColumns, provenanceOptions); - provenanceGrid.setSelectionModel(new Slick.RowSelectionModel()); - provenanceGrid.registerPlugin(new Slick.AutoTooltips()); - - // initialize the grid sorting - provenanceGrid.setSortColumn('eventTime', false); - provenanceGrid.onSort.subscribe(function (e, args) { - sort({ - columnId: args.sortCol.field, - sortAsc: args.sortAsc - }, provenanceData); - }); + // determine if the this page is in the shell + var isInShell = (top !== window); - // configure a click listener - provenanceGrid.onClick.subscribe(function (e, args) { - var target = $(e.target); + // define how the column is formatted + var showLineageFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; - // get the node at this row - var item = provenanceData.getItem(args.row); + // conditionally include the cluster node id + if (common.SUPPORTS_SVG) { + markup += '<div title="Show Lineage" class="pointer show-lineage icon icon-lineage" style="margin-right: 3px;"></div>'; + } - // determine the desired action - if (provenanceGrid.getColumns()[args.cell].id === 'actions') { - if (target.hasClass('show-lineage')) { - provenanceLineageCtrl.showLineage(item.flowFileUuid, item.eventId.toString(), item.clusterNodeId, provenanceTableCtrl); - } else if (target.hasClass('go-to')) { - goTo(item); + // conditionally support going to the component + var isRemotePort = dataContext.componentType === 'Remote Input Port' || dataContext.componentType === 'Remote Output Port'; + if (isInShell && common.isDefinedAndNotNull(dataContext.groupId) && isRemotePort === false) { + markup += '<div class="pointer go-to fa fa-long-arrow-right" title="Go To"></div>'; } - } else if (provenanceGrid.getColumns()[args.cell].id === 'moreDetails') { - if (target.hasClass('show-event-details')) { - provenanceTableCtrl.showEventDetails(item.eventId, item.clusterNodeId); + + return markup; + }; + + // initialize the provenance table + var provenanceColumns = [ + { + id: 'moreDetails', + name: ' ', + sortable: false, + resizable: false, + formatter: moreDetailsFormatter, + width: 50, + maxWidth: 50 + }, + { + id: 'eventTime', + name: 'Date/Time', + field: 'eventTime', + sortable: true, + defaultSortAsc: false, + resizable: true + }, + { + id: 'eventType', + name: 'Type', + field: 'eventType', + sortable: true, + resizable: true + }, + { + id: 'flowFileUuid', + name: 'FlowFile Uuid', + field: 'flowFileUuid', + sortable: true, + resizable: true + }, + { + id: 'fileSize', + name: 'Size', + field: 'fileSize', + sortable: true, + defaultSortAsc: false, + resizable: true + }, + { + id: 'componentName', + name: 'Component Name', + field: 'componentName', + sortable: true, + resizable: true, + formatter: valueFormatter + }, + { + id: 'componentType', + name: 'Component Type', + field: 'componentType', + sortable: true, + resizable: true } + ]; + + // conditionally show the cluster node identifier + if (isClustered) { + provenanceColumns.push({ + id: 'clusterNodeAddress', + name: 'Node', + field: 'clusterNodeAddress', + sortable: true, + resizable: true + }); } - }); - - // wire up the dataview to the grid - provenanceData.onRowCountChanged.subscribe(function (e, args) { - provenanceGrid.updateRowCount(); - provenanceGrid.render(); - - // update the total number of displayed events if necessary - $('#displayed-events').text(nf.Common.formatInteger(args.current)); - }); - provenanceData.onRowsChanged.subscribe(function (e, args) { - provenanceGrid.invalidateRows(args.rows); - provenanceGrid.render(); - }); - - // hold onto an instance of the grid - $('#provenance-table').data('gridInstance', provenanceGrid); - - // initialize the number of displayed items - $('#displayed-events').text('0'); - $('#total-events').text('0'); - }; - /** - * Applies the filter found in the filter expression text field. - */ - var applyFilter = function () { - // get the dataview - var provenanceGrid = $('#provenance-table').data('gridInstance'); + // conditionally show the action column + if (common.SUPPORTS_SVG || isInShell) { + provenanceColumns.push({ + id: 'actions', + name: ' ', + formatter: showLineageFormatter, + resizable: false, + sortable: false, + width: 50, + maxWidth: 50 + }); + } - // ensure the grid has been initialized - if (nf.Common.isDefinedAndNotNull(provenanceGrid)) { - var provenanceData = provenanceGrid.getData(); + var provenanceOptions = { + forceFitColumns: true, + enableTextSelectionOnCells: true, + enableCellNavigation: true, + enableColumnReorder: false, + autoEdit: false, + multiSelect: false, + rowHeight: 24 + }; - // update the search criteria + // create the remote model + var provenanceData = new Slick.Data.DataView({ + inlineFilters: false + }); + provenanceData.setItems([]); provenanceData.setFilterArgs({ - searchString: getFilterText(), - property: $('#provenance-filter-type').combo('getSelectedOption').value + searchString: '', + property: 'name' }); - provenanceData.refresh(); - } - }; + provenanceData.setFilter(filter); - /** - * Get the text out of the filter field. If the filter field doesn't - * have any text it will contain the text 'filter list' so this method - * accounts for that. - */ - var getFilterText = function () { - return $('#provenance-filter').val(); - }; + // initialize the sort + sort({ + columnId: 'eventTime', + sortAsc: false + }, provenanceData); - /** - * Performs the provenance filtering. - * - * @param {object} item The item subject to filtering - * @param {object} args Filter arguments - * @returns {Boolean} Whether or not to include the item - */ - var filter = function (item, args) { - if (args.searchString === '') { - return true; - } + // initialize the grid + var provenanceGrid = new Slick.Grid('#provenance-table', provenanceData, provenanceColumns, provenanceOptions); + provenanceGrid.setSelectionModel(new Slick.RowSelectionModel()); + provenanceGrid.registerPlugin(new Slick.AutoTooltips()); + + // initialize the grid sorting + provenanceGrid.setSortColumn('eventTime', false); + provenanceGrid.onSort.subscribe(function (e, args) { + sort({ + columnId: args.sortCol.field, + sortAsc: args.sortAsc + }, provenanceData); + }); - try { - // perform the row filtering - var filterExp = new RegExp(args.searchString, 'i'); - } catch (e) { - // invalid regex - return false; - } + // configure a click listener + provenanceGrid.onClick.subscribe(function (e, args) { + var target = $(e.target); - return item[args.property].search(filterExp) >= 0; - }; + // get the node at this row + var item = provenanceData.getItem(args.row); - /** - * Sorts the data according to the sort details. - * - * @param {type} sortDetails - * @param {type} data - */ - var sort = function (sortDetails, data) { - // defines a function for sorting - var comparer = function (a, b) { - if (sortDetails.columnId === 'eventTime') { - var aTime = nf.Common.parseDateTime(a[sortDetails.columnId]).getTime(); - var bTime = nf.Common.parseDateTime(b[sortDetails.columnId]).getTime(); - if (aTime === bTime) { - return a['id'] - b['id']; - } else { - return aTime - bTime; - } - } else if (sortDetails.columnId === 'fileSize') { - var aSize = nf.Common.parseSize(a[sortDetails.columnId]); - var bSize = nf.Common.parseSize(b[sortDetails.columnId]); - if (aSize === bSize) { - return a['id'] - b['id']; - } else { - return aSize - bSize; - } - } else { - var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; - var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; - if (aString === bString) { - return a['id'] - b['id']; - } else { - return aString === bString ? 0 : aString > bString ? 1 : -1; + // determine the desired action + if (provenanceGrid.getColumns()[args.cell].id === 'actions') { + if (target.hasClass('show-lineage')) { + provenanceLineageCtrl.showLineage(item.flowFileUuid, item.eventId.toString(), item.clusterNodeId, provenanceTableCtrl); + } else if (target.hasClass('go-to')) { + goTo(item); + } + } else if (provenanceGrid.getColumns()[args.cell].id === 'moreDetails') { + if (target.hasClass('show-event-details')) { + provenanceTableCtrl.showEventDetails(item.eventId, item.clusterNodeId); + } } - } - }; + }); - // perform the sort - data.sort(comparer, sortDetails.sortAsc); - }; + // wire up the dataview to the grid + provenanceData.onRowCountChanged.subscribe(function (e, args) { + provenanceGrid.updateRowCount(); + provenanceGrid.render(); - /** - * Submits a new provenance query. - * - * @argument {object} provenance The provenance query - * @returns {deferred} - */ - var submitProvenance = function (provenance) { - var provenanceEntity = { - 'provenance': { - 'request': $.extend({ - maxResults: config.maxResults, - summarize: true, - incrementalResults: false - }, provenance) - } - }; + // update the total number of displayed events if necessary + $('#displayed-events').text(common.formatInteger(args.current)); + }); + provenanceData.onRowsChanged.subscribe(function (e, args) { + provenanceGrid.invalidateRows(args.rows); + provenanceGrid.render(); + }); - // submit the provenance request - return $.ajax({ - type: 'POST', - url: config.urls.provenance, - data: JSON.stringify(provenanceEntity), - dataType: 'json', - contentType: 'application/json' - }).fail(nf.Common.handleAjaxError); - }; + // hold onto an instance of the grid + $('#provenance-table').data('gridInstance', provenanceGrid); - /** - * Gets the results from the provenance query for the specified id. - * - * @param {object} provenance - * @returns {deferred} - */ - var getProvenance = function (provenance) { - var url = provenance.uri; - if (nf.Common.isDefinedAndNotNull(provenance.request.clusterNodeId)) { - url += '?' + $.param({ - clusterNodeId: provenance.request.clusterNodeId, - summarize: true, - incrementalResults: false - }); - } else { - url += '?' + $.param({ - summarize: true, - incrementalResults: false - }); - } + // initialize the number of displayed items + $('#displayed-events').text('0'); + $('#total-events').text('0'); + }; - return $.ajax({ - type: 'GET', - url: url, - dataType: 'json' - }).fail(nf.Common.handleAjaxError); - }; + /** + * Applies the filter found in the filter expression text field. + */ + var applyFilter = function () { + // get the dataview + var provenanceGrid = $('#provenance-table').data('gridInstance'); - /** - * Cancels the specified provenance query. - * - * @param {object} provenance - * @return {deferred} - */ - var cancelProvenance = function (provenance) { - var url = provenance.uri; - if (nf.Common.isDefinedAndNotNull(provenance.request.clusterNodeId)) { - url += '?' + $.param({ - clusterNodeId: provenance.request.clusterNodeId - }); - } + // ensure the grid has been initialized + if (common.isDefinedAndNotNull(provenanceGrid)) { + var provenanceData = provenanceGrid.getData(); - return $.ajax({ - type: 'DELETE', - url: url, - dataType: 'json' - }).fail(nf.Common.handleAjaxError); - }; + // update the search criteria + provenanceData.setFilterArgs({ + searchString: getFilterText(), + property: $('#provenance-filter-type').combo('getSelectedOption').value + }); + provenanceData.refresh(); + } + }; - /** - * Checks the results of the specified provenance. - * - * @param {object} provenance - */ - var loadProvenanceResults = function (provenance, provenanceTableCtrl) { - var provenanceRequest = provenance.request; - var provenanceResults = provenance.results; - - // ensure there are groups specified - if (nf.Common.isDefinedAndNotNull(provenanceResults.provenanceEvents)) { - var provenanceTable = $('#provenance-table').data('gridInstance'); - var provenanceData = provenanceTable.getData(); - - // set the items - provenanceData.setItems(provenanceResults.provenanceEvents); - provenanceData.reSort(); - provenanceTable.invalidate(); - - // update the stats last refreshed timestamp - $('#provenance-last-refreshed').text(provenanceResults.generated); - - // update the oldest event available - $('#oldest-event').html(nf.Common.formatValue(provenanceResults.oldestEvent)); - - // record the server offset - provenanceTableCtrl.serverTimeOffset = provenanceResults.timeOffset; - - // determines if the specified query is blank (no search terms, start or end date) - var isBlankQuery = function (query) { - return nf.Common.isUndefinedOrNull(query.startDate) && nf.Common.isUndefinedOrNull(query.endDate) && $.isEmptyObject(query.searchTerms); - }; + /** + * Get the text out of the filter field. If the filter field doesn't + * have any text it will contain the text 'filter list' so this method + * accounts for that. + */ + var getFilterText = function () { + return $('#provenance-filter').val(); + }; - // update the filter message based on the request - if (isBlankQuery(provenanceRequest)) { - var message = 'Showing the most recent '; - if (provenanceResults.totalCount >= config.maxResults) { - message += (nf.Common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events, please refine the search.'); - } else { - message += ('events.'); - } - $('#provenance-query-message').text(message); - $('#clear-provenance-search').hide(); - } else { - var message = 'Showing '; - if (provenanceResults.totalCount >= config.maxResults) { - message += (nf.Common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events that match the specified query, please refine the search.'); - } else { - message += ('the events that match the specified query.'); - } - $('#provenance-query-message').text(message); - $('#clear-provenance-search').show(); + /** + * Performs the provenance filtering. + * + * @param {object} item The item subject to filtering + * @param {object} args Filter arguments + * @returns {Boolean} Whether or not to include the item + */ + var filter = function (item, args) { + if (args.searchString === '') { + return true; } - // update the total number of events - $('#total-events').text(nf.Common.formatInteger(provenanceResults.provenanceEvents.length)); - } else { - $('#total-events').text('0'); - } - }; - - /** - * Goes to the specified component if possible. - * - * @argument {object} item The event it - */ - var goTo = function (item) { - // ensure the component is still present in the flow - if (nf.Common.isDefinedAndNotNull(item.groupId)) { - // only attempt this if we're within a frame - if (top !== window) { - // and our parent has canvas utils and shell defined - if (nf.Common.isDefinedAndNotNull(parent.nf) && nf.Common.isDefinedAndNotNull(parent.nf.CanvasUtils) && nf.Common.isDefinedAndNotNull(parent.nf.Shell)) { - parent.nf.CanvasUtils.showComponent(item.groupId, item.componentId); - parent.$('#shell-close-button').click(); - } + try { + // perform the row filtering + var filterExp = new RegExp(args.searchString, 'i'); + } catch (e) { + // invalid regex + return false; } - } - }; - function ProvenanceTableCtrl() { + return item[args.property].search(filterExp) >= 0; + }; /** - * The server time offset + * Sorts the data according to the sort details. + * + * @param {type} sortDetails + * @param {type} data */ - this.serverTimeOffset = null; - } + var sort = function (sortDetails, data) { + // defines a function for sorting + var comparer = function (a, b) { + if (sortDetails.columnId === 'eventTime') { + var aTime = common.parseDateTime(a[sortDetails.columnId]).getTime(); + var bTime = common.parseDateTime(b[sortDetails.columnId]).getTime(); + if (aTime === bTime) { + return a['id'] - b['id']; + } else { + return aTime - bTime; + } + } else if (sortDetails.columnId === 'fileSize') { + var aSize = common.parseSize(a[sortDetails.columnId]); + var bSize = common.parseSize(b[sortDetails.columnId]); + if (aSize === bSize) { + return a['id'] - b['id']; + } else { + return aSize - bSize; + } + } else { + var aString = common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; + var bString = common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; + if (aString === bString) { + return a['id'] - b['id']; + } else { + return aString === bString ? 0 : aString > bString ? 1 : -1; + } + } + }; - ProvenanceTableCtrl.prototype = { - constructor: ProvenanceTableCtrl, + // perform the sort + data.sort(comparer, sortDetails.sortAsc); + }; /** - * Initializes the provenance table. Returns a deferred that will indicate when/if the table has initialized successfully. + * Submits a new provenance query. * - * @param {boolean} isClustered Whether or not this instance is clustered + * @argument {object} provenance The provenance query + * @returns {deferred} */ - init: function (isClustered) { - var self = this; - return $.Deferred(function (deferred) { - // handles init failure - var failure = function (xhr, status, error) { - deferred.reject(); - nf.Common.handleAjaxError(xhr, status, error); - }; - - // initialize the lineage view - provenanceLineageCtrl.init(); + var submitProvenance = function (provenance) { + var provenanceEntity = { + 'provenance': { + 'request': $.extend({ + maxResults: config.maxResults, + summarize: true, + incrementalResults: false + }, provenance) + } + }; - // initialize the table view - initDetailsDialog(); - initProvenanceQueryDialog(); - initProvenanceTable(isClustered, self); - initSearchDialog(isClustered, self).done(function () { - deferred.resolve(); - }).fail(failure); - }).promise(); - }, + // submit the provenance request + return $.ajax({ + type: 'POST', + url: config.urls.provenance, + data: JSON.stringify(provenanceEntity), + dataType: 'json', + contentType: 'application/json' + }).fail(errorHandler.handleAjaxError); + }; /** - * Update the size of the grid based on its container's current size. + * Gets the results from the provenance query for the specified id. + * + * @param {object} provenance + * @returns {deferred} */ - resetTableSize: function () { - var provenanceGrid = $('#provenance-table').data('gridInstance'); - if (nf.Common.isDefinedAndNotNull(provenanceGrid)) { - provenanceGrid.resizeCanvas(); + var getProvenance = function (provenance) { + var url = provenance.uri; + if (common.isDefinedAndNotNull(provenance.request.clusterNodeId)) { + url += '?' + $.param({ + clusterNodeId: provenance.request.clusterNodeId, + summarize: true, + incrementalResults: false + }); + } else { + url += '?' + $.param({ + summarize: true, + incrementalResults: false + }); } - }, + + return $.ajax({ + type: 'GET', + url: url, + dataType: 'json' + }).fail(errorHandler.handleAjaxError); + }; /** - * Updates the value of the specified progress bar. + * Cancels the specified provenance query. * - * @param {jQuery} progressBar - * @param {integer} value - * @returns {undefined} + * @param {object} provenance + * @return {deferred} */ - updateProgress: function (progressBar, value) { - // remove existing labels - progressBar.find('div.progress-label').remove(); - progressBar.find('md-progress-linear').remove(); + var cancelProvenance = function (provenance) { + var url = provenance.uri; + if (common.isDefinedAndNotNull(provenance.request.clusterNodeId)) { + url += '?' + $.param({ + clusterNodeId: provenance.request.clusterNodeId + }); + } - // update the progress bar - var label = $('<div class="progress-label"></div>').text(value + '%'); - (nf.ng.Bridge.injector.get('$compile')($('<md-progress-linear ng-cloak ng-value="' + value + '" class="md-hue-2" md-mode="determinate" aria-label="Progress"></md-progress-linear>'))(nf.ng.Bridge.rootScope)).appendTo(progressBar); - progressBar.append(label); - }, + return $.ajax({ + type: 'DELETE', + url: url, + dataType: 'json' + }).fail(errorHandler.handleAjaxError); + }; /** - * Loads the provenance table with events according to the specified optional - * query. If not query is specified or it is empty, the most recent entries will - * be returned. + * Checks the results of the specified provenance. * - * @param {object} query + * @param {object} provenance */ - loadProvenanceTable: function (query) { - var self = this; - var provenanceProgress = $('#provenance-percent-complete'); - - // add support to cancel outstanding requests - when the button is pressed we - // could be in one of two stages, 1) waiting to GET the status or 2) - // in the process of GETting the status. Handle both cases by cancelling - // the setTimeout (1) and by setting a flag to indicate that a request has - // been request so we can ignore the results (2). - - var cancelled = false; - var provenance = null; - var provenanceTimer = null; - - // update the progress bar value - self.updateProgress(provenanceProgress, 0); - - // show the 'searching...' dialog - $('#provenance-query-dialog').modal('setButtonModel', [{ - buttonText: 'Cancel', - color: { - base: '#E3E8EB', - hover: '#C7D2D7', - text: '#004849' - }, - handler: { - click: function () { - cancelled = true; + var loadProvenanceResults = function (provenance, provenanceTableCtrl) { + var provenanceRequest = provenance.request; + var provenanceResults = provenance.results; - // we are waiting for the next poll attempt - if (provenanceTimer !== null) { - // cancel it - clearTimeout(provenanceTimer); + // ensure there are groups specified + if (common.isDefinedAndNotNull(provenanceResults.provenanceEvents)) { + var provenanceTable = $('#provenance-table').data('gridInstance'); + var provenanceData = provenanceTable.getData(); - // cancel the provenance - closeDialog(); - } + // set the items + provenanceData.setItems(provenanceResults.provenanceEvents); + provenanceData.reSort(); + provenanceTable.invalidate(); + + // update the stats last refreshed timestamp + $('#provenance-last-refreshed').text(provenanceResults.generated); + + // update the oldest event available + $('#oldest-event').html(common.formatValue(provenanceResults.oldestEvent)); + + // record the server offset + provenanceTableCtrl.serverTimeOffset = provenanceResults.timeOffset; + + // determines if the specified query is blank (no search terms, start or end date) + var isBlankQuery = function (query) { + return common.isUndefinedOrNull(query.startDate) && common.isUndefinedOrNull(query.endDate) && $.isEmptyObject(query.searchTerms); + }; + + // update the filter message based on the request + if (isBlankQuery(provenanceRequest)) { + var message = 'Showing the most recent '; + if (provenanceResults.totalCount >= config.maxResults) { + message += (common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events, please refine the search.'); + } else { + message += ('events.'); + } + $('#provenance-query-message').text(message); + $('#clear-provenance-search').hide(); + } else { + var message = 'Showing '; + if (provenanceResults.totalCount >= config.maxResults) { + message += (common.formatInteger(config.maxResults) + ' of ' + provenanceResults.total + ' events that match the specified query, please refine the search.'); + } else { + message += ('the events that match the specified query.'); } + $('#provenance-query-message').text(message); + $('#clear-provenance-search').show(); } - }]).modal('show'); - - // ----------------------------- - // determine the provenance query - // ----------------------------- - - // handle the specified query appropriately - if (nf.Common.isDefinedAndNotNull(query)) { - // store the last query performed - cachedQuery = query; - } else if (!$.isEmptyObject(cachedQuery)) { - // use the last query performed - query = cachedQuery; + + // update the total number of events + $('#total-events').text(common.formatInteger(provenanceResults.provenanceEvents.length)); } else { - // don't use a query - query = {}; + $('#total-events').text('0'); } + }; - // closes the searching dialog and cancels the query on the server - var closeDialog = function () { - // cancel the provenance results since we've successfully processed the results - if (nf.Common.isDefinedAndNotNull(provenance)) { - cancelProvenance(provenance); + /** + * Goes to the specified component if possible. + * + * @argument {object} item The event it + */ + var goTo = function (item) { + // ensure the component is still present in the flow + if (common.isDefinedAndNotNull(item.groupId)) { + // only attempt this if we're within a frame + if (top !== window) { + // and our parent has canvas utils and shell defined + if (common.isDefinedAndNotNull(parent.nf) && common.isDefinedAndNotNull(parent.nf.CanvasUtils) && common.isDefinedAndNotNull(parent.nf.Shell)) { + parent.nf.CanvasUtils.showComponent(item.groupId, item.componentId); + parent.$('#shell-close-button').click(); + } } + } + }; - // close the dialog - $('#provenance-query-dialog').modal('hide'); - }; + function ProvenanceTableCtrl() { - // polls the server for the status of the provenance - var pollProvenance = function () { - getProvenance(provenance).done(function (response) { - // update the provenance - provenance = response.provenance; + /** - // process the provenance - processProvenanceResponse(); - }).fail(closeDialog); - }; + * The server time offset + */ + this.serverTimeOffset = null; + } + + ProvenanceTableCtrl.prototype = { + constructor: ProvenanceTableCtrl, + + /** + * Initializes the provenance table. Returns a deferred that will indicate when/if the table has initialized successfully. + * + * @param {boolean} isClustered Whether or not this instance is clustered + */ + init: function (isClustered) { + var provenanceTableCtrl = this; + return $.Deferred(function (deferred) { + // handles init failure + var failure = function (xhr, status, error) { + deferred.reject(); + errorHandler.handleAjaxError(xhr, status, error); + }; + + // initialize the lineage view + provenanceLineageCtrl.init(); + + // initialize the table view + initDetailsDialog(); + initProvenanceQueryDialog(); + initProvenanceTable(isClustered, provenanceTableCtrl); + initSearchDialog(isClustered, provenanceTableCtrl).done(function () { + deferred.resolve(); + }).fail(failure); +
<TRUNCATED>
