NIFI-4436:

- Clearing bucket/flow/versions when changing the selected registry/bucket.
- Using the versioned flow to get the group name when importing.
- Adding menu items for viewing local changes.
- Showing local changes during revert request.
This commit is contained in:
Matt Gilman 2017-11-16 14:41:41 -05:00 committed by Bryan Bende
parent f6cc5b6cdc
commit 3d8b1e4890
No known key found for this signature in database
GPG Key ID: A0DDA9ED50711C39
13 changed files with 1008 additions and 321 deletions

View File

@ -17,12 +17,11 @@
package org.apache.nifi.web.api.dto; package org.apache.nifi.web.api.dto;
import java.util.List; import io.swagger.annotations.ApiModelProperty;
import java.util.Objects;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
import java.util.List;
import io.swagger.annotations.ApiModelProperty; import java.util.Objects;
@XmlType(name = "componentDifference") @XmlType(name = "componentDifference")
public class ComponentDifferenceDTO { public class ComponentDifferenceDTO {
@ -30,7 +29,7 @@ public class ComponentDifferenceDTO {
private String componentId; private String componentId;
private String componentName; private String componentName;
private String processGroupId; private String processGroupId;
private List<String> differences; private List<DifferenceDTO> differences;
@ApiModelProperty("The type of component") @ApiModelProperty("The type of component")
public String getComponentType() { public String getComponentType() {
@ -69,11 +68,11 @@ public class ComponentDifferenceDTO {
} }
@ApiModelProperty("The differences in the component between the two flows") @ApiModelProperty("The differences in the component between the two flows")
public List<String> getDifferences() { public List<DifferenceDTO> getDifferences() {
return differences; return differences;
} }
public void setDifferences(List<String> differences) { public void setDifferences(List<DifferenceDTO> differences) {
this.differences = differences; this.differences = differences;
} }

View File

@ -0,0 +1,47 @@
/*
* 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.
*/
package org.apache.nifi.web.api.dto;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "difference")
public class DifferenceDTO {
private String differenceType;
private String difference;
@ApiModelProperty("The type of difference")
public String getDifferenceType() {
return differenceType;
}
public void setDifferenceType(String differenceType) {
this.differenceType = differenceType;
}
@ApiModelProperty("Description of the difference")
public String getDifference() {
return difference;
}
public void setDifference(String difference) {
this.difference = difference;
}
}

View File

@ -16,57 +16,12 @@
*/ */
package org.apache.nifi.web.api; package org.apache.nifi.web.api;
import java.io.IOException; import io.swagger.annotations.Api;
import java.io.InputStream; import io.swagger.annotations.ApiOperation;
import java.net.URI; import io.swagger.annotations.ApiParam;
import java.net.URISyntaxException; import io.swagger.annotations.ApiResponse;
import java.util.Collection; import io.swagger.annotations.ApiResponses;
import java.util.Collections; import io.swagger.annotations.Authorization;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AuthorizableLookup; import org.apache.nifi.authorization.AuthorizableLookup;
import org.apache.nifi.authorization.AuthorizeAccess; import org.apache.nifi.authorization.AuthorizeAccess;
@ -156,12 +111,55 @@ import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import io.swagger.annotations.Api; import javax.servlet.http.HttpServletRequest;
import io.swagger.annotations.ApiOperation; import javax.ws.rs.Consumes;
import io.swagger.annotations.ApiParam; import javax.ws.rs.DELETE;
import io.swagger.annotations.ApiResponse; import javax.ws.rs.DefaultValue;
import io.swagger.annotations.ApiResponses; import javax.ws.rs.GET;
import io.swagger.annotations.Authorization; import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* RESTful endpoint for managing a Group. * RESTful endpoint for managing a Group.
@ -341,9 +339,7 @@ public class ProcessGroupResource extends ApplicationResource {
// authorize access // authorize access
serviceFacade.authorizeAccess(lookup -> { serviceFacade.authorizeAccess(lookup -> {
final ProcessGroupAuthorizable groupAuthorizable = lookup.getProcessGroup(groupId); final ProcessGroupAuthorizable groupAuthorizable = lookup.getProcessGroup(groupId);
final Authorizable processGroup = groupAuthorizable.getAuthorizable(); authorizeProcessGroup(groupAuthorizable, authorizer, lookup, RequestAction.READ, false, false, true, false);
processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
super.authorizeProcessGroup(groupAuthorizable, authorizer, lookup, RequestAction.READ, false, false, true, false);
}); });
final FlowComparisonEntity entity = serviceFacade.getLocalModifications(groupId); final FlowComparisonEntity entity = serviceFacade.getLocalModifications(groupId);
@ -1625,6 +1621,11 @@ public class ProcessGroupResource extends ApplicationResource {
} }
} }
// if the group name isn't specified, ensure the group is being imported from version control
if (StringUtils.isBlank(requestProcessGroupEntity.getComponent().getName()) && requestProcessGroupEntity.getComponent().getVersionControlInformation() == null) {
throw new IllegalArgumentException("The group name is required when the group is not imported from version control.");
}
if (requestProcessGroupEntity.getComponent().getParentGroupId() != null && !groupId.equals(requestProcessGroupEntity.getComponent().getParentGroupId())) { if (requestProcessGroupEntity.getComponent().getParentGroupId() != null && !groupId.equals(requestProcessGroupEntity.getComponent().getParentGroupId())) {
throw new IllegalArgumentException(String.format("If specified, the parent process group id %s must be the same as specified in the URI %s", throw new IllegalArgumentException(String.format("If specified, the parent process group id %s must be the same as specified in the URI %s",
requestProcessGroupEntity.getComponent().getParentGroupId(), groupId)); requestProcessGroupEntity.getComponent().getParentGroupId(), groupId));
@ -1688,15 +1689,22 @@ public class ProcessGroupResource extends ApplicationResource {
serviceFacade.verifyComponentTypes(versionedFlowSnapshot.getFlowContents()); serviceFacade.verifyComponentTypes(versionedFlowSnapshot.getFlowContents());
} }
}, },
processGroupGroupEntity -> { processGroupEntity -> {
final ProcessGroupDTO processGroup = processGroupEntity.getComponent();
// set the processor id as appropriate // set the processor id as appropriate
processGroupGroupEntity.getComponent().setId(generateUuid()); processGroup.setId(generateUuid());
// ensure the group name comes from the versioned flow
final VersionedFlowSnapshot flowSnapshot = processGroupEntity.getVersionedFlowSnapshot();
if (flowSnapshot != null && StringUtils.isNotBlank(flowSnapshot.getFlowContents().getName()) && StringUtils.isBlank(processGroup.getName())) {
processGroup.setName(flowSnapshot.getFlowContents().getName());
}
// create the process group contents // create the process group contents
final Revision revision = getRevision(processGroupGroupEntity, processGroupGroupEntity.getComponent().getId()); final Revision revision = getRevision(processGroupEntity, processGroup.getId());
ProcessGroupEntity entity = serviceFacade.createProcessGroup(revision, groupId, processGroupGroupEntity.getComponent()); ProcessGroupEntity entity = serviceFacade.createProcessGroup(revision, groupId, processGroup);
final VersionedFlowSnapshot flowSnapshot = requestProcessGroupEntity.getVersionedFlowSnapshot();
if (flowSnapshot != null) { if (flowSnapshot != null) {
final RevisionDTO revisionDto = entity.getRevision(); final RevisionDTO revisionDto = entity.getRevision();
final String newGroupId = entity.getComponent().getId(); final String newGroupId = entity.getComponent().getId();

View File

@ -16,33 +16,6 @@
*/ */
package org.apache.nifi.web.api.dto; package org.apache.nifi.web.api.dto;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException;
import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.action.Action; import org.apache.nifi.action.Action;
@ -215,6 +188,32 @@ import org.apache.nifi.web.api.entity.VariableEntity;
import org.apache.nifi.web.controller.ControllerFacade; import org.apache.nifi.web.controller.ControllerFacade;
import org.apache.nifi.web.revision.RevisionManager; import org.apache.nifi.web.revision.RevisionManager;
import javax.ws.rs.WebApplicationException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public final class DtoFactory { public final class DtoFactory {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -2181,15 +2180,20 @@ public final class DtoFactory {
public Set<ComponentDifferenceDTO> createComponentDifferenceDtos(final FlowComparison comparison) { public Set<ComponentDifferenceDTO> createComponentDifferenceDtos(final FlowComparison comparison) {
final Map<ComponentDifferenceDTO, List<String>> differencesByComponent = new HashMap<>(); final Map<ComponentDifferenceDTO, List<DifferenceDTO>> differencesByComponent = new HashMap<>();
for (final FlowDifference difference : comparison.getDifferences()) { for (final FlowDifference difference : comparison.getDifferences()) {
final ComponentDifferenceDTO componentDiff = createComponentDifference(difference); final ComponentDifferenceDTO componentDiff = createComponentDifference(difference);
final List<String> differences = differencesByComponent.computeIfAbsent(componentDiff, key -> new ArrayList<>()); final List<DifferenceDTO> differences = differencesByComponent.computeIfAbsent(componentDiff, key -> new ArrayList<>());
differences.add(difference.getDescription());
final DifferenceDTO dto = new DifferenceDTO();
dto.setDifferenceType(difference.getDifferenceType().getDescription());
dto.setDifference(difference.getDescription());
differences.add(dto);
} }
for (final Map.Entry<ComponentDifferenceDTO, List<String>> entry : differencesByComponent.entrySet()) { for (final Map.Entry<ComponentDifferenceDTO, List<DifferenceDTO>> entry : differencesByComponent.entrySet()) {
entry.getKey().setDifferences(entry.getValue()); entry.getKey().setDifferences(entry.getValue());
} }

View File

@ -61,7 +61,9 @@ public class StandardProcessGroupDAO extends ComponentDAO implements ProcessGrou
// create the process group // create the process group
ProcessGroup group = flowController.createProcessGroup(processGroup.getId()); ProcessGroup group = flowController.createProcessGroup(processGroup.getId());
group.setName(processGroup.getName()); if (processGroup.getName() != null) {
group.setName(processGroup.getName());
}
if (processGroup.getPosition() != null) { if (processGroup.getPosition() != null) {
group.setPosition(new Position(processGroup.getPosition().getX(), processGroup.getPosition().getY())); group.setPosition(new Position(processGroup.getPosition().getX(), processGroup.getPosition().getY()));
} }

View File

@ -117,6 +117,8 @@
<jsp:include page="/WEB-INF/partials/canvas/connections-dialog.jsp"/> <jsp:include page="/WEB-INF/partials/canvas/connections-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/save-flow-version-dialog.jsp"/> <jsp:include page="/WEB-INF/partials/canvas/save-flow-version-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/import-flow-version-dialog.jsp"/> <jsp:include page="/WEB-INF/partials/canvas/import-flow-version-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/show-local-changes-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/registry-configuration-dialog.jsp"/> <jsp:include page="/WEB-INF/partials/canvas/registry-configuration-dialog.jsp"/>
<div id="canvas-container" class="unselectable"></div> <div id="canvas-container" class="unselectable"></div>
<div id="canvas-tooltips"> <div id="canvas-tooltips">

View File

@ -0,0 +1,36 @@
<%--
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.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="revert-local-changes-dialog" layout="column" class="hidden large-dialog">
<div class="dialog-content">
<div class="setting">
<div class="setting-field">
Are you sure you want to revert changes? All flow configuration changes detailed below will be reverted to the last version.
</div>
</div>
<span id="revert-local-changes-process-group-id" class="hidden"></span>
<div id="revert-local-changes-filter-controls">
<div id="revert-local-changes-filter-status" class="filter-status">
Displaying&nbsp;<span id="displayed-revert-local-changes-entries"></span>&nbsp;of&nbsp;<span id="total-revert-local-changes-entries"></span>
</div>
<div id="revert-local-changes-filter-container">
<input type="text" id="revert-local-changes-filter" placeholder="Filter"/>
</div>
</div>
<div id="revert-local-changes-table"></div>
</div>
</div>

View File

@ -0,0 +1,35 @@
<%--
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.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="show-local-changes-dialog" layout="column" class="hidden large-dialog">
<div class="dialog-content">
<div class="setting">
<div class="setting-field">
The following changes have been made to the flow since the last version.
</div>
</div>
<div id="show-local-changes-filter-controls">
<div id="show-local-changes-filter-status" class="filter-status">
Displaying&nbsp;<span id="displayed-show-local-changes-entries"></span>&nbsp;of&nbsp;<span id="total-show-local-changes-entries"></span>
</div>
<div id="show-local-changes-filter-container">
<input type="text" id="show-local-changes-filter" placeholder="Filter"/>
</div>
</div>
<div id="show-local-changes-table"></div>
</div>
</div>

View File

@ -263,6 +263,23 @@ div.progress-label {
border-radius: 0; border-radius: 0;
} }
/*
Local changes
*/
#revert-local-changes-table, #show-local-changes-table {
position: absolute;
top: 80px;
left: 0px;
right: 0px;
bottom: 0px;
height: 225px;
}
#revert-local-changes-filter, #show-local-changes-filter {
width: 173px;
}
/* /*
Variable Registry Variable Registry
*/ */

View File

@ -25,9 +25,10 @@
'nf.Graph', 'nf.Graph',
'nf.CanvasUtils', 'nf.CanvasUtils',
'nf.ErrorHandler', 'nf.ErrorHandler',
'nf.Common'], 'nf.Common',
function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon) { 'nf.Dialog'],
return (nf.ng.GroupComponent = factory($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon)); function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon, nfDialog) {
return (nf.ng.GroupComponent = factory($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon, nfDialog));
}); });
} else if (typeof exports === 'object' && typeof module === 'object') { } else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.ng.GroupComponent = module.exports = (nf.ng.GroupComponent =
@ -37,7 +38,8 @@
require('nf.Graph'), require('nf.Graph'),
require('nf.CanvasUtils'), require('nf.CanvasUtils'),
require('nf.ErrorHandler'), require('nf.ErrorHandler'),
require('nf.Common'))); require('nf.Common'),
require('nf.Dialog')));
} else { } else {
nf.ng.GroupComponent = factory(root.$, nf.ng.GroupComponent = factory(root.$,
root.nf.Client, root.nf.Client,
@ -45,9 +47,10 @@
root.nf.Graph, root.nf.Graph,
root.nf.CanvasUtils, root.nf.CanvasUtils,
root.nf.ErrorHandler, root.nf.ErrorHandler,
root.nf.Common); root.nf.Common,
root.nf.Dialog);
} }
}(this, function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon) { }(this, function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon, nfDialog) {
'use strict'; 'use strict';
return function (serviceProvider) { return function (serviceProvider) {
@ -236,12 +239,22 @@
// hide the dialog // hide the dialog
groupComponent.modal.hide(); groupComponent.modal.hide();
// create the group and resolve the deferred accordingly // ensure the group name is specified
createGroup(groupName, pt).done(function (response) { if (nfCommon.isBlank(groupName)) {
deferred.resolve(response.component); nfDialog.showOkDialog({
}).fail(function () { headerText: 'Create Process Group',
dialogContent: 'The group name is required.'
});
deferred.reject(); deferred.reject();
}); } else {
// create the group and resolve the deferred accordingly
createGroup(groupName, pt).done(function (response) {
deferred.resolve(response.component);
}).fail(function () {
deferred.reject();
});
}
}; };
groupComponent.modal.update('setButtonModel', [{ groupComponent.modal.update('setButtonModel', [{

View File

@ -1265,6 +1265,18 @@
} }
}, },
/**
* Shows local changes.
*/
showLocalChanges: function (selection) {
if (selection.empty()) {
nfFlowVersion.showLocalChanges(nfCanvasUtils.getGroupId());
} else if (selection.size() === 1) {
var selectionData = selection.datum();
nfFlowVersion.showLocalChanges(selectionData.id)
}
},
/** /**
* Changes the flow version. * Changes the flow version.
*/ */

View File

@ -427,6 +427,123 @@
return nfCommon.isUndefinedOrNull(processGroupData.component.versionControlInformation); return nfCommon.isUndefinedOrNull(processGroupData.component.versionControlInformation);
}; };
/**
* Returns whether the process group support supports commit.
*
* @param selection
* @returns {boolean}
*/
var supportsCommitFlowVersion = function (selection) {
// ensure this selection supports flow versioning above
if (supportsFlowVersioning(selection) === false) {
return false;
}
var versionControlInformation;
if (selection.empty()) {
// check bread crumbs for version control information in the current group
var breadcrumbEntities = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
if (breadcrumbEntities.length > 0) {
var breadcrumbEntity = breadcrumbEntities[breadcrumbEntities.length - 1];
if (breadcrumbEntity.permissions.canRead) {
versionControlInformation = breadcrumbEntity.breadcrumb.versionControlInformation;
} else {
return false;
}
} else {
return false;
}
} else {
var processGroupData = selection.datum();
versionControlInformation = processGroupData.component.versionControlInformation;
}
if (nfCommon.isUndefinedOrNull(versionControlInformation)) {
return false;
}
// check the selection for version control information
return versionControlInformation.current === true && versionControlInformation.modified === true;
};
/**
* Returns whether the process group supports revert local changes.
*
* @param selection
* @returns {boolean}
*/
var hasLocalChanges = function (selection) {
// ensure this selection supports flow versioning above
if (supportsFlowVersioning(selection) === false) {
return false;
}
var versionControlInformation;
if (selection.empty()) {
// check bread crumbs for version control information in the current group
var breadcrumbEntities = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
if (breadcrumbEntities.length > 0) {
var breadcrumbEntity = breadcrumbEntities[breadcrumbEntities.length - 1];
if (breadcrumbEntity.permissions.canRead) {
versionControlInformation = breadcrumbEntity.breadcrumb.versionControlInformation;
} else {
return false;
}
} else {
return false;
}
} else {
var processGroupData = selection.datum();
versionControlInformation = processGroupData.component.versionControlInformation;
}
if (nfCommon.isUndefinedOrNull(versionControlInformation)) {
return false;
}
// check the selection for version control information
return versionControlInformation.modified === true;
};
/**
* Returns whether the process group supports changing the flow version.
*
* @param selection
* @returns {boolean}
*/
var supportsChangeFlowVersion = function (selection) {
// ensure this selection supports flow versioning above
if (supportsFlowVersioning(selection) === false) {
return false;
}
var versionControlInformation;
if (selection.empty()) {
// check bread crumbs for version control information in the current group
var breadcrumbEntities = nfNgBridge.injector.get('breadcrumbsCtrl').getBreadcrumbs();
if (breadcrumbEntities.length > 0) {
var breadcrumbEntity = breadcrumbEntities[breadcrumbEntities.length - 1];
if (breadcrumbEntity.permissions.canRead) {
versionControlInformation = breadcrumbEntity.breadcrumb.versionControlInformation;
} else {
return false;
}
} else {
return false;
}
} else {
var processGroupData = selection.datum();
versionControlInformation = processGroupData.component.versionControlInformation;
}
if (nfCommon.isUndefinedOrNull(versionControlInformation)) {
return false;
}
// check the selection for version control information
return versionControlInformation.modified === false;
};
/** /**
* Determines whether the current selection supports stopping flow versioning. * Determines whether the current selection supports stopping flow versioning.
* *
@ -640,9 +757,10 @@
{id: 'version-menu-item', groupMenuItem: {clazz: 'fa', text: 'Version'}, menuItems: [ {id: 'version-menu-item', groupMenuItem: {clazz: 'fa', text: 'Version'}, menuItems: [
{id: 'start-version-control-menu-item', condition: supportsStartFlowVersioning, menuItem: {clazz: 'fa fa-upload', text: 'Start version control', action: 'saveFlowVersion'}}, {id: 'start-version-control-menu-item', condition: supportsStartFlowVersioning, menuItem: {clazz: 'fa fa-upload', text: 'Start version control', action: 'saveFlowVersion'}},
{separator: true}, {separator: true},
{id: 'commit-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa fa-upload', text: 'Commit local changes', action: 'saveFlowVersion'}}, {id: 'commit-menu-item', condition: supportsCommitFlowVersion, menuItem: {clazz: 'fa fa-upload', text: 'Commit local changes', action: 'saveFlowVersion'}},
{id: 'revert-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa fa-undo', text: 'Revert local changes', action: 'revertLocalChanges'}}, {id: 'local-changes-menu-item', condition: hasLocalChanges, menuItem: {clazz: 'fa', text: 'Show local changes', action: 'showLocalChanges'}},
{id: 'change-version-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Change version', action: 'changeFlowVersion'}}, {id: 'revert-menu-item', condition: hasLocalChanges, menuItem: {clazz: 'fa fa-undo', text: 'Revert local changes', action: 'revertLocalChanges'}},
{id: 'change-version-menu-item', condition: supportsChangeFlowVersion, menuItem: {clazz: 'fa', text: 'Change version', action: 'changeFlowVersion'}},
{separator: true}, {separator: true},
{id: 'stop-version-control-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Stop version control', action: 'stopVersionControl'}} {id: 'stop-version-control-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Stop version control', action: 'stopVersionControl'}}
]}, ]},

View File

@ -30,10 +30,11 @@
'nf.Client', 'nf.Client',
'nf.CanvasUtils', 'nf.CanvasUtils',
'nf.ProcessGroup', 'nf.ProcessGroup',
'nf.ProcessGroupConfiguration',
'nf.Graph', 'nf.Graph',
'nf.Birdseye'], 'nf.Birdseye'],
function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph, nfBirdseye) { function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfProcessGroupConfiguration, nfGraph, nfBirdseye) {
return (nf.FlowVersion = factory($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph, nfBirdseye)); return (nf.FlowVersion = factory($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfProcessGroupConfiguration, nfGraph, nfBirdseye));
}); });
} else if (typeof exports === 'object' && typeof module === 'object') { } else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.FlowVerison = module.exports = (nf.FlowVerison =
@ -45,6 +46,7 @@
require('nf.Client'), require('nf.Client'),
require('nf.CanvasUtils'), require('nf.CanvasUtils'),
require('nf.ProcessGroup'), require('nf.ProcessGroup'),
require('nf.ProcessGroupConfiguration'),
require('nf.Graph'), require('nf.Graph'),
require('nf.Birdseye'))); require('nf.Birdseye')));
} else { } else {
@ -56,10 +58,11 @@
root.nf.Client, root.nf.Client,
root.nf.CanvasUtils, root.nf.CanvasUtils,
root.nf.ProcessGroup, root.nf.ProcessGroup,
root.nf.ProcessGroupConfiguration,
root.nf.Graph, root.nf.Graph,
root.nf.Birdseye); root.nf.Birdseye);
} }
}(this, function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph, nfBirdseye) { }(this, function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfProcessGroupConfiguration, nfGraph, nfBirdseye) {
'use strict'; 'use strict';
var serverTimeOffset = null; var serverTimeOffset = null;
@ -96,6 +99,55 @@
$('#save-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); $('#save-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text('');
}; };
/**
* Reset the revert local changes dialog.
*/
var resetRevertLocalChangesDialog = function () {
$('#revert-local-changes-process-group-id').text('');
clearLocalChangesGrid($('#revert-local-changes-table'), $('#revert-local-changes-filter'), $('#displayed-revert-local-changes-entries'), $('#total-revert-local-changes-entries'));
};
/**
* Reset the show local changes dialog.
*/
var resetShowLocalChangesDialog = function () {
clearLocalChangesGrid($('#show-local-changes-table'), $('#show-local-changes-filter'), $('#displayed-show-local-changes-entries'), $('#total-show-local-changes-entries'));
};
/**
* Clears the local changes grid.
*/
var clearLocalChangesGrid = function (localChangesTable, filterInput, displayedLabel, totalLabel) {
var localChangesGrid = localChangesTable.data('gridInstance');
if (nfCommon.isDefinedAndNotNull(localChangesGrid)) {
localChangesGrid.setSelectedRows([]);
localChangesGrid.resetActiveCell();
var localChangesData = localChangesGrid.getData();
localChangesData.setItems([]);
}
filterInput.val('');
displayedLabel.text('0');
totalLabel.text('0');
}
/**
* Clears the version grid
*/
var clearFlowVersionsGrid = function () {
var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance');
if (nfCommon.isDefinedAndNotNull(importFlowVersionGrid)) {
importFlowVersionGrid.setSelectedRows([]);
importFlowVersionGrid.resetActiveCell();
var importFlowVersionData = importFlowVersionGrid.getData();
importFlowVersionData.setItems([]);
}
};
/** /**
* Reset the import flow version dialog. * Reset the import flow version dialog.
*/ */
@ -110,14 +162,7 @@
$('#import-flow-version-bucket').text('').hide(); $('#import-flow-version-bucket').text('').hide();
$('#import-flow-version-name').text('').hide(); $('#import-flow-version-name').text('').hide();
var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); clearFlowVersionsGrid();
if (nfCommon.isDefinedAndNotNull(importFlowVersionGrid)) {
importFlowVersionGrid.setSelectedRows([]);
importFlowVersionGrid.resetActiveCell();
var importFlowVersionData = importFlowVersionGrid.getData();
importFlowVersionData.setItems([]);
}
$('#import-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); $('#import-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text('');
@ -131,9 +176,10 @@
* @param dialog * @param dialog
* @param registryCombo * @param registryCombo
* @param bucketCombo * @param bucketCombo
* @param flowCombo
* @returns {deferred} * @returns {deferred}
*/ */
var loadRegistries = function (dialog, registryCombo, bucketCombo, selectBucket) { var loadRegistries = function (dialog, registryCombo, bucketCombo, flowCombo, selectBucket) {
return $.ajax({ return $.ajax({
type: 'GET', type: 'GET',
url: '../nifi-api/flow/registries', url: '../nifi-api/flow/registries',
@ -167,7 +213,7 @@
registryCombo.combo({ registryCombo.combo({
options: registries, options: registries,
select: function (selectedOption) { select: function (selectedOption) {
selectRegistry(dialog, selectedOption, bucketCombo, selectBucket) selectRegistry(dialog, selectedOption, bucketCombo, flowCombo, selectBucket)
} }
}); });
}).fail(nfErrorHandler.handleAjaxError); }).fail(nfErrorHandler.handleAjaxError);
@ -178,6 +224,7 @@
* *
* @param registryIdentifier * @param registryIdentifier
* @param bucketCombo * @param bucketCombo
* @param selectBucket
* @returns {*} * @returns {*}
*/ */
var loadBuckets = function (registryIdentifier, bucketCombo, selectBucket) { var loadBuckets = function (registryIdentifier, bucketCombo, selectBucket) {
@ -224,9 +271,10 @@
* @param dialog * @param dialog
* @param selectedOption * @param selectedOption
* @param bucketCombo * @param bucketCombo
* @param flowCombo
* @param selectBucket * @param selectBucket
*/ */
var selectRegistry = function (dialog, selectedOption, bucketCombo, selectBucket) { var selectRegistry = function (dialog, selectedOption, bucketCombo, flowCombo, selectBucket) {
var showNoBucketsAvailable = function () { var showNoBucketsAvailable = function () {
bucketCombo.combo('destroy').combo({ bucketCombo.combo('destroy').combo({
options: [{ options: [{
@ -243,6 +291,28 @@
if (selectedOption.disabled === true) { if (selectedOption.disabled === true) {
showNoBucketsAvailable(); showNoBucketsAvailable();
} else { } else {
bucketCombo.combo('destroy').combo({
options: [{
text: 'Loading buckets...',
value: null,
optionClass: 'unset',
disabled: true
}]
});
if (nfCommon.isDefinedAndNotNull(flowCombo)) {
flowCombo.combo('destroy').combo({
options: [{
text: 'Loading flows...',
value: null,
optionClass: 'unset',
disabled: true
}]
});
clearFlowVersionsGrid();
}
loadBuckets(selectedOption.value, bucketCombo, selectBucket).fail(function () { loadBuckets(selectedOption.value, bucketCombo, selectBucket).fail(function () {
showNoBucketsAvailable(); showNoBucketsAvailable();
}); });
@ -344,7 +414,7 @@
return nfCommon.formatDateTime(date); return nfCommon.formatDateTime(date);
}; };
// define the column model for the controller services table // define the column model for flow versions
var importFlowVersionColumns = [ var importFlowVersionColumns = [
{ {
id: 'version', id: 'version',
@ -426,6 +496,185 @@
importFlowVersionTable.data('gridInstance', importFlowVersionGrid); importFlowVersionTable.data('gridInstance', importFlowVersionGrid);
}; };
/**
* Initializes the specified local changes table.
*
* @param localChangesTable
* @param filterInput
* @param displayedLabel
* @param totalLabel
*/
var initLocalChangesTable = function (localChangesTable, filterInput, displayedLabel, totalLabel) {
var getFilterText = function () {
return filterInput.val();
};
var applyFilter = function () {
// get the dataview
var localChangesGrid = localChangesTable.data('gridInstance');
// ensure the grid has been initialized
if (nfCommon.isDefinedAndNotNull(localChangesGrid)) {
var localChangesData = localChangesGrid.getData();
// update the search criteria
localChangesData.setFilterArgs({
searchString: getFilterText()
});
localChangesData.refresh();
}
};
var filter = function (item, args) {
if (args.searchString === '') {
return true;
}
try {
// perform the row filtering
var filterExp = new RegExp(args.searchString, 'i');
} catch (e) {
// invalid regex
return false;
}
// determine if the item matches the filter
var matchesId = item['componentId'].search(filterExp) >= 0;
var matchesComponent = item['componentName'].search(filterExp) >= 0;
var matchesDifferenceType = item['differenceType'].search(filterExp) >= 0;
var matchesDifference = item['difference'].search(filterExp) >= 0;
return matchesId || matchesComponent || matchesDifferenceType || matchesDifference;
};
// initialize the component state filter
filterInput.on('keyup', function () {
applyFilter();
});
var valueFormatter = function (row, cell, value, columnDef, dataContext) {
return nfCommon.escapeHtml(value);
};
var actionsFormatter = function (row, cell, value, columnDef, dataContext) {
var markup = '';
if (dataContext.differenceType !== 'Component Removed' && nfCommon.isDefinedAndNotNull(dataContext.processGroupId)) {
markup += '<div class="pointer go-to-component fa fa-long-arrow-right" title="Go To" style="margin-top: 2px" ></div>';
}
return markup;
};
// define the column model for local changes
var localChangesColumns = [
{
id: 'component',
name: 'Component Name',
field: 'componentName',
formatter: valueFormatter,
sortable: true,
resizable: true
},
{
id: 'differenceType',
name: 'Type',
field: 'differenceType',
formatter: valueFormatter,
sortable: true,
resizable: true
},
{
id: 'difference',
name: 'Difference',
field: 'difference',
formatter: valueFormatter,
sortable: true,
resizable: true,
minWidth: 300
},
{
id: 'actions',
name: '&nbsp;',
formatter: actionsFormatter,
sortable: false,
resizable: false,
width: 25
}
];
// initialize the dataview
var localChangesData = new Slick.Data.DataView({
inlineFilters: false
});
localChangesData.setFilterArgs({
searchString: '',
property: 'component'
});
localChangesData.setFilter(filter);
// initialize the sort
sort({
columnId: 'version',
sortAsc: false
}, localChangesData);
// initialize the grid
var localChangesGrid = new Slick.Grid(localChangesTable, localChangesData, localChangesColumns, gridOptions);
localChangesGrid.setSelectionModel(new Slick.RowSelectionModel());
localChangesGrid.registerPlugin(new Slick.AutoTooltips());
localChangesGrid.setSortColumn('version', false);
localChangesGrid.onSort.subscribe(function (e, args) {
sort({
columnId: args.sortCol.id,
sortAsc: args.sortAsc
}, localChangesData);
});
// configure a click listener
localChangesGrid.onClick.subscribe(function (e, args) {
var target = $(e.target);
// get the node at this row
var componentDifference = localChangesData.getItem(args.row);
// determine the desired action
if (localChangesGrid.getColumns()[args.cell].id === 'actions') {
if (target.hasClass('go-to-component')) {
if (componentDifference.componentType === 'Controller Service') {
nfProcessGroupConfiguration.showConfiguration(componentDifference.processGroupId).done(function () {
nfProcessGroupConfiguration.selectControllerService(componentDifference.componentId);
});
} else {
nfCanvasUtils.showComponent(componentDifference.processGroupId, componentDifference.componentId);
}
}
}
});
// wire up the dataview to the grid
localChangesData.onRowCountChanged.subscribe(function (e, args) {
localChangesGrid.updateRowCount();
localChangesGrid.render();
// update the total number of displayed items
displayedLabel.text(nfCommon.formatInteger(args.current));
});
localChangesData.onRowsChanged.subscribe(function (e, args) {
localChangesGrid.invalidateRows(args.rows);
localChangesGrid.render();
});
localChangesData.syncGridSelection(localChangesGrid, true);
// hold onto an instance of the grid
localChangesTable.data('gridInstance', localChangesGrid);
// initialize the number of display items
displayedLabel.text('0');
totalLabel.text('0');
};
/** /**
* Shows the import flow version dialog. * Shows the import flow version dialog.
*/ */
@ -450,7 +699,7 @@
disabled: true disabled: true
}] }]
}).show(); }).show();
$('#import-flow-version-name-combo').combo('destroy').combo({ var flowCombo = $('#import-flow-version-name-combo').combo('destroy').combo({
options: [{ options: [{
text: 'Loading flows...', text: 'Loading flows...',
value: null, value: null,
@ -459,7 +708,7 @@
}] }]
}).show(); }).show();
loadRegistries($('#import-flow-version-dialog'), registryCombo, bucketCombo, selectBucketImportVersion).done(function () { loadRegistries($('#import-flow-version-dialog'), registryCombo, bucketCombo, flowCombo, selectBucketImportVersion).done(function () {
// show the import dialog // show the import dialog
$('#import-flow-version-dialog').modal('setHeaderText', 'Import Version').modal('setButtonModel', [{ $('#import-flow-version-dialog').modal('setHeaderText', 'Import Version').modal('setButtonModel', [{
buttonText: 'Import', buttonText: 'Import',
@ -619,6 +868,19 @@
* @param selectedBucket * @param selectedBucket
*/ */
var selectBucketImportVersion = function (selectedBucket) { var selectBucketImportVersion = function (selectedBucket) {
// mark the flows as loading
$('#import-flow-version-name-combo').combo('destroy').combo({
options: [{
text: 'Loading flows...',
value: null,
optionClass: 'unset',
disabled: true
}]
});
// clear the flow versions grid
clearFlowVersionsGrid();
var selectedRegistry = $('#import-flow-version-registry-combo').combo('getSelectedOption'); var selectedRegistry = $('#import-flow-version-registry-combo').combo('getSelectedOption');
// load the flows for the currently selected registry and bucket // load the flows for the currently selected registry and bucket
@ -646,7 +908,6 @@
} }
}), }),
'component': { 'component': {
'name': selectedFlow.text, // TODO - name from versioned PG?
'position': { 'position': {
'x': pt.x, 'x': pt.x,
'y': pt.y 'y': pt.y
@ -966,6 +1227,240 @@
progressBar.append(label); progressBar.append(label);
}; };
/**
* Shows local changes for the specified process group.
*
* @param processGroupId
* @param localChangesTable
* @param totalLabel
*/
var loadLocalChanges = function (processGroupId, localChangesTable, totalLabel) {
var localChangesGrid = localChangesTable.data('gridInstance');
var localChangesData = localChangesGrid.getData();
// begin the update
localChangesData.beginUpdate();
// remove the current versions
localChangesGrid.setSelectedRows([]);
localChangesGrid.resetActiveCell();
localChangesData.setItems([]);
return $.ajax({
type: 'GET',
url: '../nifi-api/process-groups/' + encodeURIComponent(processGroupId) + '/local-modifications',
dataType: 'json'
}).done(function (response) {
if (nfCommon.isDefinedAndNotNull(response.componentDifferences) && response.componentDifferences.length > 0) {
var totalDifferences = 0;
$.each(response.componentDifferences, function (_, componentDifference) {
$.each(componentDifference.differences, function (_, difference) {
localChangesData.addItem({
id: totalDifferences++,
componentId: componentDifference.componentId,
componentName: componentDifference.componentName,
componentType: componentDifference.componentType,
processGroupId: componentDifference.processGroupId,
differenceType: difference.differenceType,
difference: difference.difference
});
});
});
// end the update
localChangesData.endUpdate();
// resort
localChangesData.reSort();
localChangesGrid.invalidate();
// update the total displayed
totalLabel.text(nfCommon.formatInteger(totalDifferences));
} else {
nfDialog.showOkDialog({
headerText: 'Local Changes',
dialogContent: 'This Process Group does not have any local changes.'
});
}
}).fail(nfErrorHandler.handleAjaxError);
};
/**
* Revert local changes for the specified process group.
*
* @param processGroupId
*/
var revertLocalChanges = function (processGroupId) {
getVersionControlInformation(processGroupId).done(function (response) {
if (nfCommon.isDefinedAndNotNull(response.versionControlInformation)) {
var revertTimer = null;
var revertRequest = null;
var cancelled = false;
// update the button model of the revert status dialog
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Stop',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
cancelled = true;
$('#change-version-status-dialog').modal('setButtonModel', []);
// we are waiting for the next poll attempt
if (revertTimer !== null) {
// cancel it
clearTimeout(revertTimer);
// cancel the revert request
completeRevertRequest();
}
}
}
}]);
// hide the import dialog immediately
$('#import-flow-version-dialog').modal('hide');
var submitRevertRequest = function () {
var revertFlowVersionRequest = {
'processGroupRevision': nfClient.getRevision({
'revision': {
'version': response.processGroupRevision.version
}
}),
'versionControlInformation': response.versionControlInformation
};
return $.ajax({
type: 'POST',
data: JSON.stringify(revertFlowVersionRequest),
url: '../nifi-api/versions/revert-requests/process-groups/' + encodeURIComponent(processGroupId),
dataType: 'json',
contentType: 'application/json'
}).done(function () {
// initialize the progress bar value
updateProgress(0);
// show the progress dialog
$('#change-version-status-dialog').modal('show');
}).fail(nfErrorHandler.handleAjaxError);
};
var pollRevertRequest = function () {
getRevertRequest().done(processRevertResponse);
};
var getRevertRequest = function () {
return $.ajax({
type: 'GET',
url: revertRequest.uri,
dataType: 'json'
}).fail(completeRevertRequest).fail(nfErrorHandler.handleAjaxError);
};
var completeRevertRequest = function () {
if (cancelled === true) {
// update the message to indicate successful completion
$('#change-version-status-message').text('The revert request has been cancelled.');
// update the button model
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}]);
}
if (nfCommon.isDefinedAndNotNull(revertRequest)) {
$.ajax({
type: 'DELETE',
url: revertRequest.uri,
dataType: 'json'
}).done(function (response) {
revertRequest = response.request;
// update the component that was changing
updateProcessGroup(processGroupId);
if (nfCommon.isDefinedAndNotNull(revertRequest.failureReason)) {
// hide the progress dialog
$('#change-version-status-dialog').modal('hide');
nfDialog.showOkDialog({
headerText: 'Revert Local Changes',
dialogContent: nfCommon.escapeHtml(changeRequest.failureReason)
});
} else {
// update the percent complete
updateProgress(revertRequest.percentCompleted);
// update the message to indicate successful completion
$('#change-version-status-message').text('This Process Group version has changed.');
// update the button model
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}]);
}
});
}
};
var processRevertResponse = function (response) {
revertRequest = response.request;
if (revertRequest.complete === true || cancelled === true) {
completeRevertRequest();
} else {
// update the percent complete
updateProgress(revertRequest.percentCompleted);
// update the status of the revert request
$('#change-version-status-message').text(revertRequest.state);
revertTimer = setTimeout(function () {
// clear the timer since we've been invoked
revertTimer = null;
// poll revert request
pollRevertRequest();
}, 2000);
}
};
submitRevertRequest().done(processRevertResponse);
} else {
nfDialog.showOkDialog({
headerText: 'Revert Changes',
dialogContent: 'This Process Group is not currently under version control.'
});
}
}).fail(nfErrorHandler.handleAjaxError);
};
return { return {
init: function (timeOffset) { init: function (timeOffset) {
serverTimeOffset = timeOffset; serverTimeOffset = timeOffset;
@ -998,13 +1493,11 @@
handler: { handler: {
click: function () { click: function () {
var processGroupId = $('#save-flow-version-process-group-id').text(); var processGroupId = $('#save-flow-version-process-group-id').text();
saveFlowVersion().done(function (response) { saveFlowVersion().done(function (response) {
updateVersionControlInformation(processGroupId, response.versionControlInformation); updateVersionControlInformation(processGroupId, response.versionControlInformation);
// close the dialog
$('#save-flow-version-dialog').modal('hide');
}); });
$(this).modal('hide');
} }
} }
}, { }, {
@ -1049,6 +1542,69 @@
} }
}); });
// init the revert local changes dialog
$('#revert-local-changes-dialog').modal({
scrollableContentStyle: 'scrollable',
headerText: 'Revert Local Changes',
buttons: [{
buttonText: 'Revert',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
var processGroupId = $('#revert-local-changes-process-group-id').text();
revertLocalChanges(processGroupId);
$(this).modal('hide');
}
}
}, {
buttonText: 'Cancel',
color: {
base: '#E3E8EB',
hover: '#C7D2D7',
text: '#004849'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}],
handler: {
close: function () {
resetRevertLocalChangesDialog();
}
}
});
// init the show local changes dialog
$('#show-local-changes-dialog').modal({
scrollableContentStyle: 'scrollable',
headerText: 'Local Changes',
buttons: [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}],
handler: {
close: function () {
resetShowLocalChangesDialog();
}
}
});
// handle the click for the process group import // handle the click for the process group import
$('#import-process-group-link').on('click', function() { $('#import-process-group-link').on('click', function() {
showImportFlowVersionDialog(); showImportFlowVersionDialog();
@ -1056,6 +1612,8 @@
// initialize the import flow version table // initialize the import flow version table
initImportFlowVersionTable(); initImportFlowVersionTable();
initLocalChangesTable($('#revert-local-changes-table'), $('#revert-local-changes-filter'), $('#displayed-revert-local-changes-entries'), $('#total-revert-local-changes-entries'));
initLocalChangesTable($('#show-local-changes-table'), $('#show-local-changes-filter'), $('#displayed-show-local-changes-entries'), $('#total-show-local-changes-entries'));
}, },
/** /**
@ -1118,7 +1676,7 @@
// reposition the version label // reposition the version label
$('#save-flow-version-label').css('margin-top', '0'); $('#save-flow-version-label').css('margin-top', '0');
loadRegistries($('#save-flow-version-dialog'), registryCombo, bucketCombo, selectBucketSaveFlowVersion).done(function () { loadRegistries($('#save-flow-version-dialog'), registryCombo, bucketCombo, null, selectBucketSaveFlowVersion).done(function () {
deferred.resolve(); deferred.resolve();
}).fail(function () { }).fail(function () {
deferred.reject(); deferred.reject();
@ -1144,184 +1702,20 @@
* @param processGroupId * @param processGroupId
*/ */
revertLocalChanges: function (processGroupId) { revertLocalChanges: function (processGroupId) {
// TODO update to show user the ramifications of reverting for confirmation loadLocalChanges(processGroupId, $('#revert-local-changes-table'), $('#total-revert-local-changes-entries')).done(function () {
$('#revert-local-changes-process-group-id').text(processGroupId);
$('#revert-local-changes-dialog').modal('show');
});
},
// prompt the user before reverting /**
nfDialog.showYesNoDialog({ * Shows local changes for the specified process group.
headerText: 'Revert Changes', *
dialogContent: 'Are you sure you want to revert changes? All flow configuration changes will be reverted to the last version.', * @param processGroupId
noText: 'Cancel', */
yesText: 'Revert', showLocalChanges: function (processGroupId) {
yesHandler: function () { loadLocalChanges(processGroupId, $('#show-local-changes-table'), $('#total-show-local-changes-entries')).done(function () {
getVersionControlInformation(processGroupId).done(function (response) { $('#show-local-changes-dialog').modal('show');
if (nfCommon.isDefinedAndNotNull(response.versionControlInformation)) {
var revertTimer = null;
var revertRequest = null;
var cancelled = false;
// update the button model of the revert status dialog
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Stop',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
cancelled = true;
$('#change-version-status-dialog').modal('setButtonModel', []);
// we are waiting for the next poll attempt
if (revertTimer !== null) {
// cancel it
clearTimeout(revertTimer);
// cancel the revert request
completeRevertRequest();
}
}
}
}]);
// hide the import dialog immediately
$('#import-flow-version-dialog').modal('hide');
var submitRevertRequest = function () {
var revertFlowVersionRequest = {
'processGroupRevision': nfClient.getRevision({
'revision': {
'version': response.processGroupRevision.version
}
}),
'versionControlInformation': response.versionControlInformation
};
return $.ajax({
type: 'POST',
data: JSON.stringify(revertFlowVersionRequest),
url: '../nifi-api/versions/revert-requests/process-groups/' + encodeURIComponent(processGroupId),
dataType: 'json',
contentType: 'application/json'
}).done(function () {
// initialize the progress bar value
updateProgress(0);
// show the progress dialog
$('#change-version-status-dialog').modal('show');
}).fail(nfErrorHandler.handleAjaxError);
};
var pollRevertRequest = function () {
getRevertRequest().done(processRevertResponse);
};
var getRevertRequest = function () {
return $.ajax({
type: 'GET',
url: revertRequest.uri,
dataType: 'json'
}).fail(completeRevertRequest).fail(nfErrorHandler.handleAjaxError);
};
var completeRevertRequest = function () {
if (cancelled === true) {
// update the message to indicate successful completion
$('#change-version-status-message').text('The revert request has been cancelled.');
// update the button model
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}]);
}
if (nfCommon.isDefinedAndNotNull(revertRequest)) {
$.ajax({
type: 'DELETE',
url: revertRequest.uri,
dataType: 'json'
}).done(function (response) {
revertRequest = response.request;
// update the component that was changing
updateProcessGroup(processGroupId);
if (nfCommon.isDefinedAndNotNull(revertRequest.failureReason)) {
// hide the progress dialog
$('#change-version-status-dialog').modal('hide');
nfDialog.showOkDialog({
headerText: 'Revert Local Changes',
dialogContent: nfCommon.escapeHtml(changeRequest.failureReason)
});
} else {
// update the percent complete
updateProgress(revertRequest.percentCompleted);
// update the message to indicate successful completion
$('#change-version-status-message').text('This Process Group version has changed.');
// update the button model
$('#change-version-status-dialog').modal('setButtonModel', [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}]);
}
});
}
};
var processRevertResponse = function (response) {
revertRequest = response.request;
if (revertRequest.complete === true || cancelled === true) {
completeRevertRequest();
} else {
// update the percent complete
updateProgress(revertRequest.percentCompleted);
// update the status of the revert request
$('#change-version-status-message').text(revertRequest.state);
revertTimer = setTimeout(function () {
// clear the timer since we've been invoked
revertTimer = null;
// poll revert request
pollRevertRequest();
}, 2000);
}
};
submitRevertRequest().done(processRevertResponse);
} else {
nfDialog.showOkDialog({
headerText: 'Revert Changes',
dialogContent: 'This Process Group is not currently under version control.'
});
}
}).fail(nfErrorHandler.handleAjaxError);
}
}); });
}, },
@ -1407,8 +1801,8 @@
stopVersionControl: function (processGroupId) { stopVersionControl: function (processGroupId) {
// prompt the user before disconnecting // prompt the user before disconnecting
nfDialog.showYesNoDialog({ nfDialog.showYesNoDialog({
headerText: 'Disconnect', headerText: 'Stop Version Control',
dialogContent: 'Are you sure you want to disconnect?', dialogContent: 'Are you sure you want to stop version control?',
noText: 'Cancel', noText: 'Cancel',
yesText: 'Disconnect', yesText: 'Disconnect',
yesHandler: function () { yesHandler: function () {
@ -1434,7 +1828,7 @@
nfDialog.showOkDialog({ nfDialog.showOkDialog({
headerText: 'Disconnect', headerText: 'Disconnect',
dialogContent: 'This Process Group has been disconnected.' dialogContent: 'This Process Group is no longer under version control.'
}); });
}).fail(nfErrorHandler.handleAjaxError); }).fail(nfErrorHandler.handleAjaxError);
} else { } else {