diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionedFlowDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionedFlowDTO.java index 1e1f5f579a..cba70ea399 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionedFlowDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionedFlowDTO.java @@ -23,12 +23,16 @@ import javax.xml.bind.annotation.XmlType; @XmlType(name = "versionedFlow") public class VersionedFlowDTO { + public static final String COMMIT_ACTION = "COMMIT"; + public static final String FORCE_COMMIT_ACTION = "FORCE_COMMIT"; + private String registryId = "default"; // placeholder for now. private String bucketId; private String flowId; private String flowName; private String description; private String comments; + private String action; @ApiModelProperty("The ID of the registry that the flow is tracked to") public String getRegistryId() { @@ -83,4 +87,13 @@ public class VersionedFlowDTO { public void setComments(String comments) { this.comments = comments; } + + @ApiModelProperty(value = "The action being performed", allowableValues = COMMIT_ACTION + ", " + FORCE_COMMIT_ACTION) + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java index 01451df907..a5edfdd5c2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java @@ -16,13 +16,6 @@ */ package org.apache.nifi.groups; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; - import org.apache.nifi.authorization.resource.ComponentAuthorizable; import org.apache.nifi.components.VersionedComponent; import org.apache.nifi.components.validation.ValidationStatus; @@ -46,6 +39,13 @@ import org.apache.nifi.registry.flow.VersionControlInformation; import org.apache.nifi.registry.flow.VersionedFlowSnapshot; import org.apache.nifi.remote.RemoteGroupPort; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + /** *
* ProcessGroup objects are containers for processing entities, such as @@ -901,7 +901,7 @@ public interface ProcessGroup extends ComponentAuthorizable, Positionable, Versi * * @throws IllegalStateException if the Process Group cannot currently be saved to a Flow Registry */ - void verifyCanSaveToFlowRegistry(String registryId, String bucketId, String flowId); + void verifyCanSaveToFlowRegistry(String registryId, String bucketId, String flowId, String saveAction); /** * Adds the given template to this Process Group diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java index 8679e3a624..d443a37d56 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java @@ -121,6 +121,7 @@ import org.apache.nifi.util.ReflectionUtils; import org.apache.nifi.util.SnippetUtils; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.TemplateDTO; +import org.apache.nifi.web.api.dto.VersionedFlowDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -4730,7 +4731,7 @@ public final class StandardProcessGroup implements ProcessGroup { } @Override - public void verifyCanSaveToFlowRegistry(final String registryId, final String bucketId, final String flowId) { + public void verifyCanSaveToFlowRegistry(final String registryId, final String bucketId, final String flowId, final String saveAction) { verifyNoDescendantsWithLocalModifications("be saved to a Flow Registry"); final StandardVersionControlInformation vci = versionControlInfo.get(); @@ -4739,7 +4740,8 @@ public final class StandardProcessGroup implements ProcessGroup { // Flow ID is the same. We want to publish the Process Group as the next version of the Flow. // In order to do this, we have to ensure that the Process Group is 'current'. final VersionedFlowState state = vci.getStatus().getState(); - if (state == VersionedFlowState.STALE || state == VersionedFlowState.LOCALLY_MODIFIED_AND_STALE) { + if (state == VersionedFlowState.STALE + || (state == VersionedFlowState.LOCALLY_MODIFIED_AND_STALE && VersionedFlowDTO.COMMIT_ACTION.equals(saveAction))) { throw new IllegalStateException("Cannot update Version Control Information for Process Group with ID " + getIdentifier() + " because the Process Group in the flow is not synchronized with the most recent version of the Flow in the Flow Registry. " + "In order to publish a new version of the Flow, the Process Group must first be in synch with the latest version in the Flow Registry."); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java index 9387269250..4a726b9f17 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/MockProcessGroup.java @@ -649,7 +649,7 @@ public class MockProcessGroup implements ProcessGroup { } @Override - public void verifyCanSaveToFlowRegistry(String registryId, String bucketId, String flowId) { + public void verifyCanSaveToFlowRegistry(String registryId, String bucketId, String flowId, String action) { } @Override diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index b20a2825cb..af37577ae7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -1439,10 +1439,11 @@ public interface NiFiServiceFacade { * @param registryId the ID of the Flow Registry * @param bucketId the ID of the bucket * @param flowId the ID of the flow + * @param saveAction the save action being performed * * @throws IllegalStateException if the Process Group cannot be saved to the flow registry with the coordinates specified */ - void verifyCanSaveToFlowRegistry(String groupId, String registryId, String bucketId, String flowId); + void verifyCanSaveToFlowRegistry(String groupId, String registryId, String bucketId, String flowId, String saveAction); /** * Verifies that the Process Group with the given identifier can have its local modifications reverted to the given VersionedFlowSnapshot diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index fa967bf326..fc495ef1f8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -3753,15 +3753,19 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { @Override public VersionControlComponentMappingEntity registerFlowWithFlowRegistry(final String groupId, final StartVersionControlRequestEntity requestEntity) { - final ProcessGroup processGroup = processGroupDAO.getProcessGroup(groupId); + final VersionedFlowDTO versionedFlowDto = requestEntity.getVersionedFlow(); - final VersionControlInformation currentVci = processGroup.getVersionControlInformation(); - final int expectedVersion = currentVci == null ? 1 : currentVci.getVersion() + 1; + int snapshotVersion; + if (VersionedFlowDTO.FORCE_COMMIT_ACTION.equals(versionedFlowDto.getAction())) { + snapshotVersion = -1; + } else { + final ProcessGroup processGroup = processGroupDAO.getProcessGroup(groupId); + final VersionControlInformation currentVci = processGroup.getVersionControlInformation(); + snapshotVersion = currentVci == null ? 1 : currentVci.getVersion() + 1; + } // Create a VersionedProcessGroup snapshot of the flow as it is currently. final InstantiatedVersionedProcessGroup versionedProcessGroup = createFlowSnapshot(groupId); - - final VersionedFlowDTO versionedFlowDto = requestEntity.getVersionedFlow(); final String flowId = versionedFlowDto.getFlowId() == null ? UUID.randomUUID().toString() : versionedFlowDto.getFlowId(); final VersionedFlow versionedFlow = new VersionedFlow(); @@ -3793,7 +3797,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { try { // add a snapshot to the flow in the registry - registeredSnapshot = registerVersionedFlowSnapshot(registryId, registeredFlow, versionedProcessGroup, versionedFlowDto.getComments(), expectedVersion); + registeredSnapshot = registerVersionedFlowSnapshot(registryId, registeredFlow, versionedProcessGroup, versionedFlowDto.getComments(), snapshotVersion); } catch (final NiFiCoreException e) { // If the flow has been created, but failed to add a snapshot, // then we need to capture the created versioned flow information as a partial successful result. @@ -4020,9 +4024,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } @Override - public void verifyCanSaveToFlowRegistry(final String groupId, final String registryId, final String bucketId, final String flowId) { + public void verifyCanSaveToFlowRegistry(final String groupId, final String registryId, final String bucketId, final String flowId, final String saveAction) { final ProcessGroup group = processGroupDAO.getProcessGroup(groupId); - group.verifyCanSaveToFlowRegistry(registryId, bucketId, flowId); + group.verifyCanSaveToFlowRegistry(registryId, bucketId, flowId, saveAction); } @Override diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/VersionsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/VersionsResource.java index 518ceeb7e9..4fc490c9d6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/VersionsResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/VersionsResource.java @@ -468,6 +468,13 @@ public class VersionsResource extends ApplicationResource { if (versionedFlowDto.getComments() != null && versionedFlowDto.getComments().length() > 65535) { throw new IllegalArgumentException("Comments cannot exceed 65,535 characters"); } + if (StringUtils.isEmpty(versionedFlowDto.getAction())) { + throw new IllegalArgumentException("Action is required"); + } + if (!VersionedFlowDTO.COMMIT_ACTION.equals(versionedFlowDto.getAction()) + && !VersionedFlowDTO.FORCE_COMMIT_ACTION.equals(versionedFlowDto.getAction())) { + throw new IllegalArgumentException("Action must be one of " + VersionedFlowDTO.COMMIT_ACTION + " or " + VersionedFlowDTO.FORCE_COMMIT_ACTION); + } if (isDisconnectedFromCluster()) { verifyDisconnectedNodeModification(requestEntity.isDisconnectedNodeAcknowledged()); @@ -534,7 +541,8 @@ public class VersionsResource extends ApplicationResource { final String registryId = versionedFlow.getRegistryId(); final String bucketId = versionedFlow.getBucketId(); final String flowId = versionedFlow.getFlowId(); - serviceFacade.verifyCanSaveToFlowRegistry(groupId, registryId, bucketId, flowId); + final String action = versionedFlow.getAction(); + serviceFacade.verifyCanSaveToFlowRegistry(groupId, registryId, bucketId, flowId, action); }, (rev, flowEntity) -> { // Register the current flow with the Flow Registry. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/save-flow-version-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/save-flow-version-dialog.jsp index 35da1c0aa8..45331995e2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/save-flow-version-dialog.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/save-flow-version-dialog.jsp @@ -35,6 +35,7 @@