diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentDifferenceDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentDifferenceDTO.java index 8febce02b1..e45867ddeb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentDifferenceDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentDifferenceDTO.java @@ -17,12 +17,11 @@ package org.apache.nifi.web.api.dto; -import java.util.List; -import java.util.Objects; +import io.swagger.annotations.ApiModelProperty; import javax.xml.bind.annotation.XmlType; - -import io.swagger.annotations.ApiModelProperty; +import java.util.List; +import java.util.Objects; @XmlType(name = "componentDifference") public class ComponentDifferenceDTO { @@ -30,7 +29,7 @@ public class ComponentDifferenceDTO { private String componentId; private String componentName; private String processGroupId; - private List differences; + private List differences; @ApiModelProperty("The type of component") public String getComponentType() { @@ -69,11 +68,11 @@ public class ComponentDifferenceDTO { } @ApiModelProperty("The differences in the component between the two flows") - public List getDifferences() { + public List getDifferences() { return differences; } - public void setDifferences(List differences) { + public void setDifferences(List differences) { this.differences = differences; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DifferenceDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DifferenceDTO.java new file mode 100644 index 0000000000..9f06149619 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DifferenceDTO.java @@ -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; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java index fe57dd6d0d..de56a4f4eb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java @@ -16,57 +16,12 @@ */ package org.apache.nifi.web.api; -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; - -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 io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AuthorizableLookup; 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.context.SecurityContextHolder; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.Authorization; +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 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. @@ -341,9 +339,7 @@ public class ProcessGroupResource extends ApplicationResource { // authorize access serviceFacade.authorizeAccess(lookup -> { final ProcessGroupAuthorizable groupAuthorizable = lookup.getProcessGroup(groupId); - final Authorizable processGroup = groupAuthorizable.getAuthorizable(); - processGroup.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser()); - super.authorizeProcessGroup(groupAuthorizable, authorizer, lookup, RequestAction.READ, false, false, true, false); + authorizeProcessGroup(groupAuthorizable, authorizer, lookup, RequestAction.READ, false, false, true, false); }); 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())) { 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)); @@ -1688,15 +1689,22 @@ public class ProcessGroupResource extends ApplicationResource { serviceFacade.verifyComponentTypes(versionedFlowSnapshot.getFlowContents()); } }, - processGroupGroupEntity -> { + processGroupEntity -> { + final ProcessGroupDTO processGroup = processGroupEntity.getComponent(); + // 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 - final Revision revision = getRevision(processGroupGroupEntity, processGroupGroupEntity.getComponent().getId()); - ProcessGroupEntity entity = serviceFacade.createProcessGroup(revision, groupId, processGroupGroupEntity.getComponent()); + final Revision revision = getRevision(processGroupEntity, processGroup.getId()); + ProcessGroupEntity entity = serviceFacade.createProcessGroup(revision, groupId, processGroup); - final VersionedFlowSnapshot flowSnapshot = requestProcessGroupEntity.getVersionedFlowSnapshot(); if (flowSnapshot != null) { final RevisionDTO revisionDto = entity.getRevision(); final String newGroupId = entity.getComponent().getId(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index 1a12dcf8ab..6077268921 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -16,33 +16,6 @@ */ 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.StringUtils; 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.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 { @SuppressWarnings("rawtypes") @@ -2181,15 +2180,20 @@ public final class DtoFactory { public Set createComponentDifferenceDtos(final FlowComparison comparison) { - final Map> differencesByComponent = new HashMap<>(); + final Map> differencesByComponent = new HashMap<>(); for (final FlowDifference difference : comparison.getDifferences()) { final ComponentDifferenceDTO componentDiff = createComponentDifference(difference); - final List differences = differencesByComponent.computeIfAbsent(componentDiff, key -> new ArrayList<>()); - differences.add(difference.getDescription()); + final List differences = differencesByComponent.computeIfAbsent(componentDiff, key -> new ArrayList<>()); + + final DifferenceDTO dto = new DifferenceDTO(); + dto.setDifferenceType(difference.getDifferenceType().getDescription()); + dto.setDifference(difference.getDescription()); + + differences.add(dto); } - for (final Map.Entry> entry : differencesByComponent.entrySet()) { + for (final Map.Entry> entry : differencesByComponent.entrySet()) { entry.getKey().setDifferences(entry.getValue()); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java index 78f3e314ab..e3c4725c8e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessGroupDAO.java @@ -61,7 +61,9 @@ public class StandardProcessGroupDAO extends ComponentDAO implements ProcessGrou // create the process group ProcessGroup group = flowController.createProcessGroup(processGroup.getId()); - group.setName(processGroup.getName()); + if (processGroup.getName() != null) { + group.setName(processGroup.getName()); + } if (processGroup.getPosition() != null) { group.setPosition(new Position(processGroup.getPosition().getX(), processGroup.getPosition().getY())); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index 258bdad68e..52fc43cea3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -117,6 +117,8 @@ + +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp new file mode 100644 index 0000000000..45d4c4c969 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/revert-local-changes-dialog.jsp @@ -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" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp new file mode 100644 index 0000000000..9b122ae3f8 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/show-local-changes-dialog.jsp @@ -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" %> + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css index 29a8c0e01e..69d4beaf57 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css @@ -263,6 +263,23 @@ div.progress-label { 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 */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js index ae9e228550..2ce843866e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/header/components/nf-ng-group-component.js @@ -25,9 +25,10 @@ 'nf.Graph', 'nf.CanvasUtils', 'nf.ErrorHandler', - 'nf.Common'], - function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon) { - return (nf.ng.GroupComponent = factory($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon)); + 'nf.Common', + 'nf.Dialog'], + 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') { module.exports = (nf.ng.GroupComponent = @@ -37,7 +38,8 @@ require('nf.Graph'), require('nf.CanvasUtils'), require('nf.ErrorHandler'), - require('nf.Common'))); + require('nf.Common'), + require('nf.Dialog'))); } else { nf.ng.GroupComponent = factory(root.$, root.nf.Client, @@ -45,9 +47,10 @@ root.nf.Graph, root.nf.CanvasUtils, 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'; return function (serviceProvider) { @@ -236,12 +239,22 @@ // hide the dialog groupComponent.modal.hide(); - // create the group and resolve the deferred accordingly - createGroup(groupName, pt).done(function (response) { - deferred.resolve(response.component); - }).fail(function () { + // ensure the group name is specified + if (nfCommon.isBlank(groupName)) { + nfDialog.showOkDialog({ + headerText: 'Create Process Group', + dialogContent: 'The group name is required.' + }); + 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', [{ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js index 9dc20b1a4b..725b6a1e9b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js @@ -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. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js index 865603564f..616e1512bd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js @@ -427,6 +427,123 @@ 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. * @@ -640,9 +757,10 @@ {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'}}, {separator: true}, - {id: 'commit-menu-item', condition: supportsStopFlowVersioning, 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: 'change-version-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Change version', action: 'changeFlowVersion'}}, + {id: 'commit-menu-item', condition: supportsCommitFlowVersion, menuItem: {clazz: 'fa fa-upload', text: 'Commit local changes', action: 'saveFlowVersion'}}, + {id: 'local-changes-menu-item', condition: hasLocalChanges, menuItem: {clazz: 'fa', text: 'Show local changes', action: 'showLocalChanges'}}, + {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}, {id: 'stop-version-control-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Stop version control', action: 'stopVersionControl'}} ]}, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js index 4eb5286840..28e430346b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js @@ -30,10 +30,11 @@ 'nf.Client', 'nf.CanvasUtils', 'nf.ProcessGroup', + 'nf.ProcessGroupConfiguration', 'nf.Graph', 'nf.Birdseye'], - function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph, nfBirdseye) { - return (nf.FlowVersion = factory($, 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, nfProcessGroupConfiguration, nfGraph, nfBirdseye)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.FlowVerison = @@ -45,6 +46,7 @@ require('nf.Client'), require('nf.CanvasUtils'), require('nf.ProcessGroup'), + require('nf.ProcessGroupConfiguration'), require('nf.Graph'), require('nf.Birdseye'))); } else { @@ -56,10 +58,11 @@ root.nf.Client, root.nf.CanvasUtils, root.nf.ProcessGroup, + root.nf.ProcessGroupConfiguration, root.nf.Graph, 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'; var serverTimeOffset = null; @@ -96,6 +99,55 @@ $('#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. */ @@ -110,14 +162,7 @@ $('#import-flow-version-bucket').text('').hide(); $('#import-flow-version-name').text('').hide(); - var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); - if (nfCommon.isDefinedAndNotNull(importFlowVersionGrid)) { - importFlowVersionGrid.setSelectedRows([]); - importFlowVersionGrid.resetActiveCell(); - - var importFlowVersionData = importFlowVersionGrid.getData(); - importFlowVersionData.setItems([]); - } + clearFlowVersionsGrid(); $('#import-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); @@ -131,9 +176,10 @@ * @param dialog * @param registryCombo * @param bucketCombo + * @param flowCombo * @returns {deferred} */ - var loadRegistries = function (dialog, registryCombo, bucketCombo, selectBucket) { + var loadRegistries = function (dialog, registryCombo, bucketCombo, flowCombo, selectBucket) { return $.ajax({ type: 'GET', url: '../nifi-api/flow/registries', @@ -167,7 +213,7 @@ registryCombo.combo({ options: registries, select: function (selectedOption) { - selectRegistry(dialog, selectedOption, bucketCombo, selectBucket) + selectRegistry(dialog, selectedOption, bucketCombo, flowCombo, selectBucket) } }); }).fail(nfErrorHandler.handleAjaxError); @@ -178,6 +224,7 @@ * * @param registryIdentifier * @param bucketCombo + * @param selectBucket * @returns {*} */ var loadBuckets = function (registryIdentifier, bucketCombo, selectBucket) { @@ -224,9 +271,10 @@ * @param dialog * @param selectedOption * @param bucketCombo + * @param flowCombo * @param selectBucket */ - var selectRegistry = function (dialog, selectedOption, bucketCombo, selectBucket) { + var selectRegistry = function (dialog, selectedOption, bucketCombo, flowCombo, selectBucket) { var showNoBucketsAvailable = function () { bucketCombo.combo('destroy').combo({ options: [{ @@ -243,6 +291,28 @@ if (selectedOption.disabled === true) { showNoBucketsAvailable(); } 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 () { showNoBucketsAvailable(); }); @@ -344,7 +414,7 @@ return nfCommon.formatDateTime(date); }; - // define the column model for the controller services table + // define the column model for flow versions var importFlowVersionColumns = [ { id: 'version', @@ -426,6 +496,185 @@ 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 += '
'; + } + + 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: ' ', + 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. */ @@ -450,7 +699,7 @@ disabled: true }] }).show(); - $('#import-flow-version-name-combo').combo('destroy').combo({ + var flowCombo = $('#import-flow-version-name-combo').combo('destroy').combo({ options: [{ text: 'Loading flows...', value: null, @@ -459,7 +708,7 @@ }] }).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 $('#import-flow-version-dialog').modal('setHeaderText', 'Import Version').modal('setButtonModel', [{ buttonText: 'Import', @@ -619,6 +868,19 @@ * @param 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'); // load the flows for the currently selected registry and bucket @@ -646,7 +908,6 @@ } }), 'component': { - 'name': selectedFlow.text, // TODO - name from versioned PG? 'position': { 'x': pt.x, 'y': pt.y @@ -966,6 +1227,240 @@ 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 { init: function (timeOffset) { serverTimeOffset = timeOffset; @@ -998,13 +1493,11 @@ handler: { click: function () { var processGroupId = $('#save-flow-version-process-group-id').text(); - saveFlowVersion().done(function (response) { 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 $('#import-process-group-link').on('click', function() { showImportFlowVersionDialog(); @@ -1056,6 +1612,8 @@ // initialize the import flow version table 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 $('#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(); }).fail(function () { deferred.reject(); @@ -1144,184 +1702,20 @@ * @param 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({ - headerText: 'Revert Changes', - dialogContent: 'Are you sure you want to revert changes? All flow configuration changes will be reverted to the last version.', - noText: 'Cancel', - yesText: 'Revert', - yesHandler: function () { - 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); - } + /** + * Shows local changes for the specified process group. + * + * @param processGroupId + */ + showLocalChanges: function (processGroupId) { + loadLocalChanges(processGroupId, $('#show-local-changes-table'), $('#total-show-local-changes-entries')).done(function () { + $('#show-local-changes-dialog').modal('show'); }); }, @@ -1407,8 +1801,8 @@ stopVersionControl: function (processGroupId) { // prompt the user before disconnecting nfDialog.showYesNoDialog({ - headerText: 'Disconnect', - dialogContent: 'Are you sure you want to disconnect?', + headerText: 'Stop Version Control', + dialogContent: 'Are you sure you want to stop version control?', noText: 'Cancel', yesText: 'Disconnect', yesHandler: function () { @@ -1434,7 +1828,7 @@ nfDialog.showOkDialog({ headerText: 'Disconnect', - dialogContent: 'This Process Group has been disconnected.' + dialogContent: 'This Process Group is no longer under version control.' }); }).fail(nfErrorHandler.handleAjaxError); } else {