From 696d583b148b60d41853add8d3e2d24b4ff907f3 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Wed, 1 Nov 2017 17:21:04 -0400 Subject: [PATCH] NIFI-4436: - Added the import dialog for importing a versioned flow into a new process group. - Added the change version dialog for upgrading/downgrading a versioned flow. --- .../web/api/dto/FlowConfigurationDTO.java | 14 - .../api/dto/VersionControlInformationDTO.java | 10 + .../web/api/entity/CurrentUserEntity.java | 14 + .../StartVersionControlRequestEntity.java | 48 + .../web/api/entity/VersionedFlowEntity.java | 11 - .../VersionedFlowSnapshotMetadataEntity.java | 47 + ...ersionedFlowSnapshotMetadataSetEntity.java | 38 + .../web/api/entity/VersionedFlowsEntity.java | 38 + .../apache/nifi/web/NiFiServiceFacade.java | 62 +- .../nifi/web/StandardNiFiServiceFacade.java | 158 ++- .../nifi/web/api/ControllerResource.java | 67 +- .../org/apache/nifi/web/api/FlowResource.java | 128 +- .../apache/nifi/web/api/VersionsResource.java | 91 +- .../apache/nifi/web/api/dto/DtoFactory.java | 5 +- .../apache/nifi/web/dao/ProcessGroupDAO.java | 7 + .../org/apache/nifi/web/dao/RegistryDAO.java | 16 +- .../nifi/web/dao/impl/FlowRegistryDAO.java | 59 +- .../web/dao/impl/StandardProcessGroupDAO.java | 22 + .../main/resources/nifi-web-api-context.xml | 2 - .../src/main/webapp/WEB-INF/pages/canvas.jsp | 1 + .../canvas/import-flow-version-dialog.jsp | 44 + .../WEB-INF/partials/canvas/navigation.jsp | 4 +- .../canvas/new-process-group-dialog.jsp | 3 + .../new-remote-process-group-dialog.jsp | 2 +- .../canvas/registry-configuration-dialog.jsp | 4 +- .../canvas/save-flow-version-dialog.jsp | 20 +- .../src/main/webapp/css/dialog.css | 18 +- .../components/nf-ng-group-component.js | 32 +- .../main/webapp/js/nf/canvas/nf-actions.js | 23 +- .../js/nf/canvas/nf-canvas-bootstrap.js | 3 +- .../webapp/js/nf/canvas/nf-canvas-utils.js | 7 - .../src/main/webapp/js/nf/canvas/nf-canvas.js | 18 - .../webapp/js/nf/canvas/nf-context-menu.js | 16 +- .../webapp/js/nf/canvas/nf-flow-version.js | 1038 ++++++++++++++--- .../main/webapp/js/nf/canvas/nf-settings.js | 23 +- .../src/main/webapp/js/nf/nf-common.js | 11 + 36 files changed, 1699 insertions(+), 405 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/StartVersionControlRequestEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataSetEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowsEntity.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowConfigurationDTO.java index 03e1a7d0e3..d7a0c8327d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowConfigurationDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowConfigurationDTO.java @@ -32,7 +32,6 @@ public class FlowConfigurationDTO { private Boolean supportsManagedAuthorizer; private Boolean supportsConfigurableAuthorizer; private Boolean supportsConfigurableUsersAndGroups; - private Boolean supportsFlowVersioning; private Long autoRefreshIntervalSeconds; private Date currentTime; @@ -129,17 +128,4 @@ public class FlowConfigurationDTO { this.timeOffset = timeOffset; } - /** - * @return whether this NiFi is configured for support flow versioning - */ - @ApiModelProperty( - value = "Whether this NiFi supports flow versioning." - ) - public Boolean getSupportsFlowVersioning() { - return supportsFlowVersioning; - } - - public void setSupportsFlowVersioning(Boolean supportsFlowVersioning) { - this.supportsFlowVersioning = supportsFlowVersioning; - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionControlInformationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionControlInformationDTO.java index d27e830fad..e9aa24669a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionControlInformationDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/VersionControlInformationDTO.java @@ -27,6 +27,7 @@ public class VersionControlInformationDTO { private String registryId; private String bucketId; private String flowId; + private String flowName; private Integer version; private Boolean modified; private Boolean current; @@ -67,6 +68,15 @@ public class VersionControlInformationDTO { this.flowId = flowId; } + @ApiModelProperty("The name of the flow") + public String getFlowName() { + return flowName; + } + + public void setFlowName(String flowName) { + this.flowName = flowName; + } + @ApiModelProperty("The version of the flow") public Integer getVersion() { return version; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java index 6b8e28f6b7..8121ce4bce 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CurrentUserEntity.java @@ -38,6 +38,8 @@ public class CurrentUserEntity extends Entity { private PermissionsDTO systemPermissions; private PermissionsDTO restrictedComponentsPermissions; + private boolean canVersionFlows; + /** * @return the user identity being serialized */ @@ -145,4 +147,16 @@ public class CurrentUserEntity extends Entity { public void setRestrictedComponentsPermissions(PermissionsDTO restrictedComponentsPermissions) { this.restrictedComponentsPermissions = restrictedComponentsPermissions; } + + /** + * @return whether the current user can version flows + */ + @ApiModelProperty("Whether the current user can version flows.") + public boolean isCanVersionFlows() { + return canVersionFlows; + } + + public void setCanVersionFlows(boolean canVersionFlows) { + this.canVersionFlows = canVersionFlows; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/StartVersionControlRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/StartVersionControlRequestEntity.java new file mode 100644 index 0000000000..08761407ed --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/StartVersionControlRequestEntity.java @@ -0,0 +1,48 @@ +/* + * 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.entity; + +import io.swagger.annotations.ApiModelProperty; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.dto.VersionedFlowDTO; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "versionedFlow") +public class StartVersionControlRequestEntity extends Entity { + private VersionedFlowDTO versionedFlow; + private RevisionDTO processGroupRevision; + + @ApiModelProperty("The versioned flow") + public VersionedFlowDTO getVersionedFlow() { + return versionedFlow; + } + + public void setVersionedFlow(VersionedFlowDTO versionedFLow) { + this.versionedFlow = versionedFLow; + } + + @ApiModelProperty("The Revision of the Process Group under Version Control") + public RevisionDTO getProcessGroupRevision() { + return processGroupRevision; + } + + public void setProcessGroupRevision(final RevisionDTO revision) { + this.processGroupRevision = revision; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowEntity.java index b94255a810..4a43826f3c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowEntity.java @@ -18,7 +18,6 @@ package org.apache.nifi.web.api.entity; import io.swagger.annotations.ApiModelProperty; -import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.VersionedFlowDTO; import javax.xml.bind.annotation.XmlRootElement; @@ -26,7 +25,6 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "versionedFlow") public class VersionedFlowEntity extends Entity { private VersionedFlowDTO versionedFlow; - private RevisionDTO processGroupRevision; @ApiModelProperty("The versioned flow") public VersionedFlowDTO getVersionedFlow() { @@ -36,13 +34,4 @@ public class VersionedFlowEntity extends Entity { public void setVersionedFlow(VersionedFlowDTO versionedFLow) { this.versionedFlow = versionedFLow; } - - @ApiModelProperty("The Revision of the Process Group under Version Control") - public RevisionDTO getProcessGroupRevision() { - return processGroupRevision; - } - - public void setProcessGroupRevision(final RevisionDTO revision) { - this.processGroupRevision = revision; - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataEntity.java new file mode 100644 index 0000000000..29b57cc559 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataEntity.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.entity; + +import io.swagger.annotations.ApiModelProperty; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "versionedFlowSnapshotMetadata") +public class VersionedFlowSnapshotMetadataEntity extends Entity { + private VersionedFlowSnapshotMetadata versionedFlowSnapshotMetadata; + private String registryId; + + @ApiModelProperty("The collection of versioned flow snapshot metadata") + public VersionedFlowSnapshotMetadata getVersionedFlowSnapshotMetadata() { + return versionedFlowSnapshotMetadata; + } + + public void setVersionedFlowMetadata(VersionedFlowSnapshotMetadata versionedFlowSnapshotMetadata) { + this.versionedFlowSnapshotMetadata = versionedFlowSnapshotMetadata; + } + + @ApiModelProperty("The ID of the Registry that this flow belongs to") + public String getRegistryId() { + return registryId; + } + + public void setRegistryId(String registryId) { + this.registryId = registryId; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataSetEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataSetEntity.java new file mode 100644 index 0000000000..d49b40f2c0 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowSnapshotMetadataSetEntity.java @@ -0,0 +1,38 @@ +/* + * 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.entity; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Set; + +@XmlRootElement(name = "versionedFlowSnapshotMetadataSetEntity") +public class VersionedFlowSnapshotMetadataSetEntity extends Entity { + + private Set versionedFlowSnapshotMetadataSet; + + /** + * @return collection of VersionedFlowSnapshotMetadataEntity's that are being serialized + */ + public Set getVersionedFlowSnapshotMetadataSet() { + return versionedFlowSnapshotMetadataSet; + } + + public void setVersionedFlowSnapshotMetadataSet(Set versionedFlowSnapshotMetadataSet) { + this.versionedFlowSnapshotMetadataSet = versionedFlowSnapshotMetadataSet; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowsEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowsEntity.java new file mode 100644 index 0000000000..1104eae25f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/VersionedFlowsEntity.java @@ -0,0 +1,38 @@ +/* + * 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.entity; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Set; + +@XmlRootElement(name = "versionedFlowsEntity") +public class VersionedFlowsEntity extends Entity { + + private Set versionedFlows; + + /** + * @return collection of VersionedEntity's that are being serialized + */ + public Set getVersionedFlows() { + return versionedFlows; + } + + public void setVersionedFlows(Set versionedFlows) { + this.versionedFlows = versionedFlows; + } + +} 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 d851677b70..e299059cae 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 @@ -77,6 +77,7 @@ import org.apache.nifi.web.api.entity.AccessPolicyEntity; import org.apache.nifi.web.api.entity.ActionEntity; import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity; import org.apache.nifi.web.api.entity.AffectedComponentEntity; +import org.apache.nifi.web.api.entity.BucketEntity; import org.apache.nifi.web.api.entity.BulletinEntity; import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionStatusEntity; @@ -103,6 +104,7 @@ import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusEntity; import org.apache.nifi.web.api.entity.ReportingTaskEntity; import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import org.apache.nifi.web.api.entity.SnippetEntity; +import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity; import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.entity.TemplateEntity; import org.apache.nifi.web.api.entity.UserEntity; @@ -111,6 +113,7 @@ import org.apache.nifi.web.api.entity.VariableRegistryEntity; import org.apache.nifi.web.api.entity.VersionControlComponentMappingEntity; import org.apache.nifi.web.api.entity.VersionControlInformationEntity; import org.apache.nifi.web.api.entity.VersionedFlowEntity; +import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataEntity; import java.io.IOException; import java.util.Date; @@ -1300,7 +1303,7 @@ public interface NiFiServiceFacade { * @return a VersionControlComponentMappingEntity that contains the information needed to notify a Process Group where it is tracking to and map * component ID's to their Versioned Component ID's */ - VersionControlComponentMappingEntity registerFlowWithFlowRegistry(String groupId, VersionedFlowEntity requestEntity); + VersionControlComponentMappingEntity registerFlowWithFlowRegistry(String groupId, StartVersionControlRequestEntity requestEntity); /** * Adds the given snapshot to the already existing Versioned Flow, which resides in the given Flow Registry with the given id @@ -1854,7 +1857,7 @@ public interface NiFiServiceFacade { * @param registryDTO The registry DTO * @return The reporting task DTO */ - RegistryEntity createRegistry(Revision revision, RegistryDTO registryDTO); + RegistryEntity createRegistryClient(Revision revision, RegistryDTO registryDTO); /** * Gets a registry with the specified id. @@ -1862,14 +1865,52 @@ public interface NiFiServiceFacade { * @param registryId id * @return entity */ - RegistryEntity getRegistry(String registryId); + RegistryEntity getRegistryClient(String registryId); /** - * Gets all registries. + * Returns all registry clients. * + * @return registry clients + */ + Set getRegistryClients(); + + /** + * Gets all registries for the current user. + * + * @param user current user * @return registries */ - Set getRegistries(); + Set getRegistriesForUser(NiFiUser user); + + /** + * Gets all buckets for a given registry. + * + * @param registryId registry id + * @param user current user + * @return the buckets + */ + Set getBucketsForUser(String registryId, NiFiUser user); + + /** + * Gets the flows for the current user for the specified registry and bucket. + * + * @param registryId registry id + * @param bucketId bucket id + * @param user current user + * @return the flows + */ + Set getFlowsForUser(String registryId, String bucketId, NiFiUser user); + + /** + * Gets the versions of the specified registry, bucket, and flow for the current user. + * + * @param registryId registry id + * @param bucketId bucket id + * @param flowId flow id + * @param user current user + * @return the versions of the flow + */ + Set getFlowVersionsForUser(String registryId, String bucketId, String flowId, NiFiUser user); /** * Updates the specified registry using the specified revision. @@ -1878,7 +1919,7 @@ public interface NiFiServiceFacade { * @param registryDTO the registry dto * @return the updated registry registry entity */ - RegistryEntity updateRegistry(Revision revision, RegistryDTO registryDTO); + RegistryEntity updateRegistryClient(Revision revision, RegistryDTO registryDTO); /** * Deletes the specified registry using the specified revision. @@ -1887,7 +1928,14 @@ public interface NiFiServiceFacade { * @param registryId id * @return the deleted registry entity */ - RegistryEntity deleteRegistry(Revision revision, String registryId); + RegistryEntity deleteRegistryClient(Revision revision, String registryId); + + /** + * Verifies the specified registry can be removed. + * + * @param registryId the registry id + */ + void verifyDeleteRegistry(String registryId); // ---------------------------------------- // History methods 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 2b5b5c344f..a319f27ab4 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 @@ -16,32 +16,8 @@ */ package org.apache.nifi.web; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -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.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; - +import com.google.common.collect.Sets; +import org.apache.commons.collections4.CollectionUtils; import org.apache.nifi.action.Action; import org.apache.nifi.action.Component; import org.apache.nifi.action.FlowChangeAction; @@ -111,15 +87,17 @@ import org.apache.nifi.history.History; import org.apache.nifi.history.HistoryQuery; import org.apache.nifi.history.PreviousValue; import org.apache.nifi.registry.ComponentVariableRegistry; +import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.registry.flow.FlowRegistry; import org.apache.nifi.registry.flow.FlowRegistryClient; -import org.apache.nifi.registry.flow.VersionedFlowCoordinates; import org.apache.nifi.registry.flow.UnknownResourceException; import org.apache.nifi.registry.flow.VersionControlInformation; import org.apache.nifi.registry.flow.VersionedComponent; import org.apache.nifi.registry.flow.VersionedConnection; import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.flow.VersionedFlowCoordinates; import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.registry.flow.VersionedProcessGroup; import org.apache.nifi.registry.flow.diff.ComparableDataFlow; import org.apache.nifi.registry.flow.diff.DifferenceType; @@ -144,6 +122,7 @@ import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.AccessPolicySummaryDTO; import org.apache.nifi.web.api.dto.AffectedComponentDTO; +import org.apache.nifi.web.api.dto.BucketDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO; @@ -215,6 +194,7 @@ import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity; import org.apache.nifi.web.api.entity.ActionEntity; import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity; import org.apache.nifi.web.api.entity.AffectedComponentEntity; +import org.apache.nifi.web.api.entity.BucketEntity; import org.apache.nifi.web.api.entity.BulletinEntity; import org.apache.nifi.web.api.entity.ComponentReferenceEntity; import org.apache.nifi.web.api.entity.ConnectionEntity; @@ -253,7 +233,9 @@ import org.apache.nifi.web.api.entity.VariableEntity; import org.apache.nifi.web.api.entity.VariableRegistryEntity; import org.apache.nifi.web.api.entity.VersionControlComponentMappingEntity; import org.apache.nifi.web.api.entity.VersionControlInformationEntity; +import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity; import org.apache.nifi.web.api.entity.VersionedFlowEntity; +import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataEntity; import org.apache.nifi.web.controller.ControllerFacade; import org.apache.nifi.web.dao.AccessPolicyDAO; import org.apache.nifi.web.dao.ConnectionDAO; @@ -282,7 +264,30 @@ import org.apache.nifi.web.util.SnippetUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +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.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; /** * Implementation of NiFiServiceFacade that performs revision checking. @@ -2268,7 +2273,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { @Override - public RegistryEntity createRegistry(Revision revision, RegistryDTO registryDTO) { + public RegistryEntity createRegistryClient(Revision revision, RegistryDTO registryDTO) { final NiFiUser user = NiFiUserUtils.getNiFiUser(); // read lock on the containing group @@ -2292,7 +2297,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } @Override - public RegistryEntity getRegistry(final String registryId) { + public RegistryEntity getRegistryClient(final String registryId) { final FlowRegistry registry = registryDAO.getFlowRegistry(registryId); return createRegistryEntity(registry); } @@ -2319,15 +2324,90 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { return entity; } + private BucketEntity createBucketEntity(final Bucket bucket) { + if (bucket == null) { + return null; + } + + final BucketDTO dto = new BucketDTO(); + dto.setId(bucket.getIdentifier()); + dto.setName(bucket.getName()); + dto.setDescription(bucket.getDescription()); + dto.setCreated(bucket.getCreatedTimestamp()); + + final BucketEntity entity = new BucketEntity(); + entity.setBucket(dto); + + return entity; + } + + private VersionedFlowEntity createVersionedFlowEntity(final String registryId, final VersionedFlow versionedFlow) { + if (versionedFlow == null) { + return null; + } + + final VersionedFlowDTO dto = new VersionedFlowDTO(); + dto.setRegistryId(registryId); + dto.setBucketId(versionedFlow.getBucketIdentifier()); + dto.setFlowId(versionedFlow.getIdentifier()); + dto.setFlowName(versionedFlow.getName()); + dto.setDescription(versionedFlow.getDescription()); + + final VersionedFlowEntity entity = new VersionedFlowEntity(); + entity.setVersionedFlow(dto); + + return entity; + } + + private VersionedFlowSnapshotMetadataEntity createVersionedFlowSnapshotMetadataEntity(final String registryId, final VersionedFlowSnapshotMetadata metadata) { + if (metadata == null) { + return null; + } + + final VersionedFlowSnapshotMetadataEntity entity = new VersionedFlowSnapshotMetadataEntity(); + entity.setRegistryId(registryId); + entity.setVersionedFlowMetadata(metadata); + + return entity; + } + @Override - public Set getRegistries() { + public Set getRegistryClients() { return registryDAO.getFlowRegistries().stream() .map(this::createRegistryEntity) .collect(Collectors.toSet()); } @Override - public RegistryEntity updateRegistry(Revision revision, RegistryDTO registryDTO) { + public Set getRegistriesForUser(final NiFiUser user) { + return registryDAO.getFlowRegistriesForUser(user).stream() + .map(this::createRegistryEntity) + .collect(Collectors.toSet()); + } + + @Override + public Set getBucketsForUser(final String registryId, final NiFiUser user) { + return registryDAO.getBucketsForUser(registryId, user).stream() + .map(this::createBucketEntity) + .collect(Collectors.toSet()); + } + + @Override + public Set getFlowsForUser(String registryId, String bucketId, NiFiUser user) { + return registryDAO.getFlowsForUser(registryId, bucketId, user).stream() + .map(vf -> createVersionedFlowEntity(registryId, vf)) + .collect(Collectors.toSet()); + } + + @Override + public Set getFlowVersionsForUser(String registryId, String bucketId, String flowId, NiFiUser user) { + return registryDAO.getFlowVersionsForUser(registryId, bucketId, flowId, user).stream() + .map(md -> createVersionedFlowSnapshotMetadataEntity(registryId, md)) + .collect(Collectors.toSet()); + } + + @Override + public RegistryEntity updateRegistryClient(Revision revision, RegistryDTO registryDTO) { final RevisionClaim revisionClaim = new StandardRevisionClaim(revision); final NiFiUser user = NiFiUserUtils.getNiFiUser(); @@ -2350,7 +2430,12 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } @Override - public RegistryEntity deleteRegistry(final Revision revision, final String registryId) { + public void verifyDeleteRegistry(String registryId) { + processGroupDAO.verifyDeleteFlowRegistry(registryId); + } + + @Override + public RegistryEntity deleteRegistryClient(final Revision revision, final String registryId) { final RevisionClaim claim = new StandardRevisionClaim(revision); final NiFiUser user = NiFiUserUtils.getNiFiUser(); @@ -3340,6 +3425,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { entity.setPoliciesPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getPolicies())); entity.setSystemPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getSystem())); entity.setRestrictedComponentsPermissions(dtoFactory.createPermissionsDto(authorizableLookup.getRestrictedComponents())); + + // TODO - update to be user specific + entity.setCanVersionFlows(CollectionUtils.isNotEmpty(flowRegistryClient.getRegistryIdentifiers())); + return entity; } @@ -3538,7 +3627,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } @Override - public VersionControlComponentMappingEntity registerFlowWithFlowRegistry(final String groupId, final VersionedFlowEntity requestEntity) { + public VersionControlComponentMappingEntity registerFlowWithFlowRegistry(final String groupId, final StartVersionControlRequestEntity requestEntity) { // Create a VersionedProcessGroup snapshot of the flow as it is currently. final InstantiatedVersionedProcessGroup versionedProcessGroup = createFlowSnapshot(groupId); @@ -3584,6 +3673,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { vci.setBucketId(registeredFlow.getBucketIdentifier()); vci.setCurrent(true); vci.setFlowId(registeredFlow.getIdentifier()); + vci.setFlowName(registeredFlow.getName()); vci.setGroupId(groupId); vci.setModified(false); vci.setRegistryId(registryId); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 959d06d7c9..356d23143d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -46,6 +46,7 @@ import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.Entity; import org.apache.nifi.web.api.entity.HistoryEntity; import org.apache.nifi.web.api.entity.NodeEntity; +import org.apache.nifi.web.api.entity.RegistriesEntity; import org.apache.nifi.web.api.entity.RegistryEntity; import org.apache.nifi.web.api.entity.ReportingTaskEntity; import org.apache.nifi.web.api.request.ClientIdParameter; @@ -69,6 +70,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.Date; +import java.util.Set; /** * RESTful endpoint for managing a Flow Controller. @@ -93,7 +95,7 @@ public class ControllerResource extends ApplicationResource { * @return dtos */ public RegistryEntity populateRemainingRegistryEntityContent(final RegistryEntity registryEntity) { - registryEntity.setUri(generateResourceUri("controller", "registries", registryEntity.getId())); + registryEntity.setUri(generateResourceUri("controller", "registry-clients", registryEntity.getId())); return registryEntity; } @@ -310,6 +312,36 @@ public class ControllerResource extends ApplicationResource { // registries // ---------- + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("registry-clients") + @ApiOperation(value = "Gets the listing of available registry clients", response = RegistriesEntity.class, authorizations = { + @Authorization(value = "Read - /flow") + }) + @ApiResponses(value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + }) + public Response getRegistryClients() { + authorizeController(RequestAction.READ); + + if (isReplicateRequest()) { + return replicate(HttpMethod.GET); + } + + final Set registries = serviceFacade.getRegistryClients(); + registries.forEach(registry -> populateRemainingRegistryEntityContent(registry)); + + final RegistriesEntity registryEntities = new RegistriesEntity(); + registryEntities.setRegistries(registries); + + return generateOkResponse(registryEntities).build(); + } + /** * Creates a new Registry. * @@ -320,9 +352,9 @@ public class ControllerResource extends ApplicationResource { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Path("registries") + @Path("registry-clients") @ApiOperation( - value = "Creates a new registry", + value = "Creates a new registry client", response = RegistryEntity.class, authorizations = { @Authorization(value = "Write - /controller") @@ -336,8 +368,7 @@ public class ControllerResource extends ApplicationResource { @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) - - public Response createRegistry( + public Response createRegistryClient( @Context final HttpServletRequest httpServletRequest, @ApiParam( value = "The registry configuration details.", @@ -376,7 +407,7 @@ public class ControllerResource extends ApplicationResource { // create the reporting task and generate the json final Revision revision = getRevision(registryEntity, registry.getId()); - final RegistryEntity entity = serviceFacade.createRegistry(revision, registry); + final RegistryEntity entity = serviceFacade.createRegistryClient(revision, registry); populateRemainingRegistryEntityContent(entity); // build the response @@ -394,9 +425,9 @@ public class ControllerResource extends ApplicationResource { @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) - @Path("/registries/{id}") + @Path("/registry-clients/{id}") @ApiOperation( - value = "Gets a registry", + value = "Gets a registry client", response = RegistryEntity.class, authorizations = { @Authorization(value = "Read - /controller") @@ -426,7 +457,7 @@ public class ControllerResource extends ApplicationResource { authorizeController(RequestAction.READ); // get the registry - final RegistryEntity entity = serviceFacade.getRegistry(id); + final RegistryEntity entity = serviceFacade.getRegistryClient(id); populateRemainingRegistryEntityContent(entity); return generateOkResponse(entity).build(); @@ -443,9 +474,9 @@ public class ControllerResource extends ApplicationResource { @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Path("/registries/{id}") + @Path("/registry-clients/{id}") @ApiOperation( - value = "Updates a registry", + value = "Updates a registry client", response = RegistryEntity.class, authorizations = { @Authorization(value = "Write - /controller") @@ -460,7 +491,7 @@ public class ControllerResource extends ApplicationResource { @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) - public Response updateControllerService( + public Response updateRegistryClient( @Context HttpServletRequest httpServletRequest, @ApiParam( value = "The registry id.", @@ -505,7 +536,7 @@ public class ControllerResource extends ApplicationResource { final RegistryDTO registry = registryEntity.getComponent(); // update the controller service - final RegistryEntity entity = serviceFacade.updateRegistry(revision, registry); + final RegistryEntity entity = serviceFacade.updateRegistryClient(revision, registry); populateRemainingRegistryEntityContent(entity); return generateOkResponse(entity).build(); @@ -528,9 +559,9 @@ public class ControllerResource extends ApplicationResource { @DELETE @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) - @Path("/registries/{id}") + @Path("/registry-clients/{id}") @ApiOperation( - value = "Deletes a reistry", + value = "Deletes a registry client", response = RegistryEntity.class, authorizations = { @Authorization(value = "Write - /controller") @@ -545,7 +576,7 @@ public class ControllerResource extends ApplicationResource { @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) - public Response deleteRegistry( + public Response deleteRegistryClient( @Context HttpServletRequest httpServletRequest, @ApiParam( value = "The revision is used to verify the client is working with the latest version of the flow.", @@ -579,10 +610,10 @@ public class ControllerResource extends ApplicationResource { lookup -> { authorizeController(RequestAction.WRITE); }, - null, + () -> serviceFacade.verifyDeleteRegistry(id), (revision, registryEntity) -> { // delete the specified registry - final RegistryEntity entity = serviceFacade.deleteRegistry(revision, registryEntity.getId()); + final RegistryEntity entity = serviceFacade.deleteRegistryClient(revision, registryEntity.getId()); return generateOkResponse(entity).build(); } ); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index 3e9be6d251..b8bdc14663 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -39,18 +39,13 @@ import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.nar.NarClassLoaders; -import org.apache.nifi.registry.bucket.Bucket; -import org.apache.nifi.registry.flow.FlowRegistry; -import org.apache.nifi.registry.flow.FlowRegistryClient; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.IllegalClusterResourceRequestException; -import org.apache.nifi.web.NiFiCoreException; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.ResourceNotFoundException; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.AboutDTO; import org.apache.nifi.web.api.dto.BannerDTO; -import org.apache.nifi.web.api.dto.BucketDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO; import org.apache.nifi.web.api.dto.ClusterDTO; @@ -102,6 +97,10 @@ import org.apache.nifi.web.api.entity.SearchResultsEntity; import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.entity.TemplateEntity; import org.apache.nifi.web.api.entity.TemplatesEntity; +import org.apache.nifi.web.api.entity.VersionedFlowEntity; +import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataEntity; +import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity; +import org.apache.nifi.web.api.entity.VersionedFlowsEntity; import org.apache.nifi.web.api.request.BulletinBoardPatternParameter; import org.apache.nifi.web.api.request.DateTimeParameter; import org.apache.nifi.web.api.request.IntegerParameter; @@ -121,7 +120,6 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; @@ -158,11 +156,8 @@ public class FlowResource extends ApplicationResource { private TemplateResource templateResource; private ProcessGroupResource processGroupResource; private ControllerServiceResource controllerServiceResource; - private ControllerResource controllerResource; private ReportingTaskResource reportingTaskResource; - private FlowRegistryClient flowRegistryClient; - public FlowResource() { super(); } @@ -1351,12 +1346,7 @@ public class FlowResource extends ApplicationResource { public Response getRegistries() { authorizeFlow(); - if (isReplicateRequest()) { - return replicate(HttpMethod.GET); - } - - final Set registries = serviceFacade.getRegistries(); - registries.forEach(registry -> controllerResource.populateRemainingRegistryEntityContent(registry)); + final Set registries = serviceFacade.getRegistriesForUser(NiFiUserUtils.getNiFiUser()); final RegistriesEntity registryEntities = new RegistriesEntity(); registryEntities.setRegistries(registries); @@ -1387,39 +1377,89 @@ public class FlowResource extends ApplicationResource { authorizeFlow(); - try { - final FlowRegistry flowRegistry = flowRegistryClient.getFlowRegistry(id); - if (flowRegistry == null) { - throw new IllegalArgumentException("The specified registry id is unknown to this NiFi."); - } + final Set buckets = serviceFacade.getBucketsForUser(id, NiFiUserUtils.getNiFiUser()); - final Set userBuckets = flowRegistry.getBuckets(NiFiUserUtils.getNiFiUser()); + final BucketsEntity bucketsEntity = new BucketsEntity(); + bucketsEntity.setBuckets(buckets); - final BucketsEntity bucketsEntity = new BucketsEntity(); + return generateOkResponse(bucketsEntity).build(); + } - if (userBuckets != null) { + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("registries/{registry-id}/buckets/{bucket-id}/flows") + @ApiOperation(value = "Gets the flows from the specified registry and bucket for the current user", response = BucketsEntity.class, authorizations = { + @Authorization(value = "Read - /flow") + }) + @ApiResponses(value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + }) + public Response getFlows( + @ApiParam( + value = "The registry id.", + required = true + ) + @PathParam("registry-id") String registryId, + @ApiParam( + value = "The bucket id.", + required = true + ) + @PathParam("bucket-id") String bucketId) { - final Set bucketSet = new HashSet<>(); - for (final Bucket userBucket : userBuckets) { - final BucketDTO bucket = new BucketDTO(); - bucket.setId(userBucket.getIdentifier()); - bucket.setName(userBucket.getName()); - bucket.setDescription(userBucket.getDescription()); - bucket.setCreated(userBucket.getCreatedTimestamp()); + authorizeFlow(); - final BucketEntity bucketEntity = new BucketEntity(); - bucketEntity.setBucket(bucket); + final Set versionedFlows = serviceFacade.getFlowsForUser(registryId, bucketId, NiFiUserUtils.getNiFiUser()); - bucketSet.add(bucketEntity); - } + final VersionedFlowsEntity versionedFlowsEntity = new VersionedFlowsEntity(); + versionedFlowsEntity.setVersionedFlows(versionedFlows); - bucketsEntity.setBuckets(bucketSet); - } + return generateOkResponse(versionedFlowsEntity).build(); + } - return generateOkResponse(bucketsEntity).build(); - } catch (final IOException ioe) { - throw new NiFiCoreException("Unable to obtain bucket listing: " + ioe.getMessage(), ioe); - } + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("registries/{registry-id}/buckets/{bucket-id}/flows/{flow-id}/versions") + @ApiOperation(value = "Gets the flow versions from the specified registry and bucket for the specified flow for the current user", response = BucketsEntity.class, authorizations = { + @Authorization(value = "Read - /flow") + }) + @ApiResponses(value = { + @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") + }) + public Response getVersions( + @ApiParam( + value = "The registry id.", + required = true + ) + @PathParam("registry-id") String registryId, + @ApiParam( + value = "The bucket id.", + required = true + ) + @PathParam("bucket-id") String bucketId, + @ApiParam( + value = "The flow id.", + required = true + ) + @PathParam("flow-id") String flowId) { + + authorizeFlow(); + + final Set versionedFlowSnapshotMetadataSet = serviceFacade.getFlowVersionsForUser(registryId, bucketId, flowId, NiFiUserUtils.getNiFiUser()); + + final VersionedFlowSnapshotMetadataSetEntity versionedFlowSnapshotMetadataSetEntity = new VersionedFlowSnapshotMetadataSetEntity(); + versionedFlowSnapshotMetadataSetEntity.setVersionedFlowSnapshotMetadataSet(versionedFlowSnapshotMetadataSet); + + return generateOkResponse(versionedFlowSnapshotMetadataSetEntity).build(); } // -------------- @@ -2629,10 +2669,6 @@ public class FlowResource extends ApplicationResource { this.processGroupResource = processGroupResource; } - public void setControllerResource(ControllerResource controllerResource) { - this.controllerResource = controllerResource; - } - public void setControllerServiceResource(ControllerServiceResource controllerServiceResource) { this.controllerServiceResource = controllerServiceResource; } @@ -2644,8 +2680,4 @@ public class FlowResource extends ApplicationResource { public void setAuthorizer(Authorizer authorizer) { this.authorizer = authorizer; } - - public void setFlowRegistryClient(FlowRegistryClient flowRegistryClient) { - this.flowRegistryClient = flowRegistryClient; - } } 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 27216a4b8f..b010bf33b7 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 @@ -17,39 +17,12 @@ package org.apache.nifi.web.api; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; - -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.MediaType; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - +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.Authorizer; @@ -58,6 +31,7 @@ import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.cluster.manager.NodeResponse; +import org.apache.nifi.controller.FlowController; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.registry.flow.ComponentType; @@ -83,7 +57,7 @@ import org.apache.nifi.web.api.entity.AffectedComponentEntity; import org.apache.nifi.web.api.entity.ProcessGroupEntity; import org.apache.nifi.web.api.entity.VersionControlComponentMappingEntity; import org.apache.nifi.web.api.entity.VersionControlInformationEntity; -import org.apache.nifi.web.api.entity.VersionedFlowEntity; +import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity; import org.apache.nifi.web.api.entity.VersionedFlowSnapshotEntity; import org.apache.nifi.web.api.entity.VersionedFlowUpdateRequestEntity; import org.apache.nifi.web.api.request.ClientIdParameter; @@ -96,12 +70,37 @@ import org.apache.nifi.web.util.Pause; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -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.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.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; @Path("/versions") @Api(value = "/versions", description = "Endpoint for managing version control for a flow") @@ -368,7 +367,7 @@ public class VersionsResource extends ApplicationResource { }) public Response startVersionControl( @ApiParam("The process group id.") @PathParam("id") final String groupId, - @ApiParam(value = "The versioned flow details.", required = true) final VersionedFlowEntity requestEntity) throws IOException { + @ApiParam(value = "The versioned flow details.", required = true) final StartVersionControlRequestEntity requestEntity) throws IOException { // Verify the request final RevisionDTO revisionDto = requestEntity.getProcessGroupRevision(); @@ -390,6 +389,12 @@ public class VersionsResource extends ApplicationResource { throw new IllegalArgumentException("The Registry ID must be supplied."); } + // ensure we're not attempting to version the root group + final ProcessGroupEntity root = serviceFacade.getProcessGroup(FlowController.ROOT_GROUP_ID_ALIAS); + if (root.getId().equals(groupId)) { + throw new IllegalArgumentException("The Root Process Group cannot be versioned."); + } + if (isReplicateRequest()) { // We first have to obtain a "lock" on all nodes in the cluster so that multiple Version Control requests // are not being made simultaneously. We do this by making a POST to /nifi-api/versions/start-requests. @@ -688,6 +693,7 @@ public class VersionsResource extends ApplicationResource { versionControlInfoDto.setBucketId(snapshotMetadata.getBucketIdentifier()); versionControlInfoDto.setCurrent(true); versionControlInfoDto.setFlowId(snapshotMetadata.getFlowIdentifier()); + versionControlInfoDto.setFlowName(snapshotMetadata.getFlowName()); versionControlInfoDto.setGroupId(groupId); versionControlInfoDto.setModified(false); versionControlInfoDto.setVersion(snapshotMetadata.getVersion()); @@ -1139,12 +1145,13 @@ public class VersionsResource extends ApplicationResource { updateRequestDto.setLastUpdated(new Date()); updateRequestDto.setProcessGroupId(groupId); updateRequestDto.setRequestId(requestId); - updateRequestDto.setUri(generateResourceUri("versions", "update-requests", requestId)); + updateRequestDto.setUri(generateResourceUri("versions", "revert-requests", requestId)); final VersionedFlowUpdateRequestEntity updateRequestEntity = new VersionedFlowUpdateRequestEntity(); updateRequestEntity.setProcessGroupRevision(revisionDto); updateRequestEntity.setRequest(updateRequestDto); + request.markComplete(currentVersionEntity); return generateOkResponse(updateRequestEntity).build(); } 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 8e0f0c7d81..3639b1897a 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,7 +16,6 @@ */ package org.apache.nifi.web.api.dto; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.action.Action; @@ -245,7 +244,6 @@ public final class DtoFactory { dto.setSupportsManagedAuthorizer(AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)); dto.setSupportsConfigurableUsersAndGroups(AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)); dto.setSupportsConfigurableAuthorizer(AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)); - dto.setSupportsFlowVersioning(CollectionUtils.isNotEmpty(flowRegistryClient.getRegistryIdentifiers())); final Date now = new Date(); dto.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime())); @@ -2191,6 +2189,8 @@ public final class DtoFactory { dto.setRegistryId(versionControlInfo.getRegistryIdentifier()); dto.setBucketId(versionControlInfo.getBucketIdentifier()); dto.setFlowId(versionControlInfo.getFlowIdentifier()); + // TODO - need to get flow name here + dto.setFlowName(group.getName()); dto.setVersion(versionControlInfo.getVersion()); dto.setCurrent(versionControlInfo.getCurrent().orElse(null)); dto.setModified(versionControlInfo.getModified().orElse(null)); @@ -3409,6 +3409,7 @@ public final class DtoFactory { copy.setRegistryId(original.getRegistryId()); copy.setBucketId(original.getBucketId()); copy.setFlowId(original.getFlowId()); + copy.setFlowName(original.getFlowName()); copy.setVersion(original.getVersion()); copy.setCurrent(original.getCurrent()); copy.setModified(original.getModified()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessGroupDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessGroupDAO.java index 650d4b3292..7cf61eab1b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessGroupDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ProcessGroupDAO.java @@ -158,6 +158,13 @@ public interface ProcessGroupDAO { */ void verifyDelete(String groupId); + /** + * Verifies the specified registry can be removed. + * + * @param registryId registry id + */ + void verifyDeleteFlowRegistry(String registryId); + /** * Deletes the specified process group. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/RegistryDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/RegistryDAO.java index 83b5c6df9b..8c22ff41f5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/RegistryDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/RegistryDAO.java @@ -17,11 +17,15 @@ package org.apache.nifi.web.dao; -import java.util.Set; - +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.registry.flow.FlowRegistry; +import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.web.api.dto.RegistryDTO; +import java.util.Set; + public interface RegistryDAO { FlowRegistry createFlowRegistry(RegistryDTO registryDto); @@ -30,6 +34,14 @@ public interface RegistryDAO { Set getFlowRegistries(); + Set getFlowRegistriesForUser(NiFiUser user); + + Set getBucketsForUser(String registry, NiFiUser user); + + Set getFlowsForUser(String registryId, String bucketId, NiFiUser user); + + Set getFlowVersionsForUser(String registryId, String bucketId, String flowId, NiFiUser user); + FlowRegistry removeFlowRegistry(String registryId); } 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/FlowRegistryDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/FlowRegistryDAO.java index eb2ac76454..19f2de49e7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/FlowRegistryDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/FlowRegistryDAO.java @@ -17,15 +17,21 @@ package org.apache.nifi.web.dao.impl; -import java.util.Set; -import java.util.stream.Collectors; - +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.registry.flow.FlowRegistry; import org.apache.nifi.registry.flow.FlowRegistryClient; +import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.web.NiFiCoreException; import org.apache.nifi.web.ResourceNotFoundException; import org.apache.nifi.web.api.dto.RegistryDTO; import org.apache.nifi.web.dao.RegistryDAO; +import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; + public class FlowRegistryDAO implements RegistryDAO { private FlowRegistryClient flowRegistryClient; @@ -51,6 +57,53 @@ public class FlowRegistryDAO implements RegistryDAO { .collect(Collectors.toSet()); } + @Override + public Set getFlowRegistriesForUser(final NiFiUser user) { + // TODO - implement to be user specific + return getFlowRegistries(); + } + + @Override + public Set getBucketsForUser(final String registryId, final NiFiUser user) { + try { + final FlowRegistry flowRegistry = flowRegistryClient.getFlowRegistry(registryId); + if (flowRegistry == null) { + throw new IllegalArgumentException("The specified registry id is unknown to this NiFi."); + } + + return flowRegistry.getBuckets(user); + } catch (final IOException ioe) { + throw new NiFiCoreException("Unable to obtain bucket listing: " + ioe.getMessage(), ioe); + } + } + + + @Override + public Set getFlowsForUser(String registryId, String bucketId, NiFiUser user) { + final Set bucketsForUser = getBucketsForUser(registryId, user); + + // TODO - implement getBucket(bucketId, user) + final Bucket bucket = bucketsForUser.stream().filter(b -> b.getIdentifier().equals(bucketId)).findFirst().orElse(null); + if (bucket == null) { + throw new IllegalArgumentException("The specified bucket is not available."); + } + + return bucket.getVersionedFlows(); + } + + @Override + public Set getFlowVersionsForUser(String registryId, String bucketId, String flowId, NiFiUser user) { + final Set flowsForUser = getFlowsForUser(registryId, bucketId, user); + + // TODO - implement getFlow(bucketId, flowId, user) + final VersionedFlow versionedFlow = flowsForUser.stream().filter(vf -> vf.getIdentifier().equals(flowId)).findFirst().orElse(null); + if (versionedFlow == null) { + throw new IllegalArgumentException("The specified flow is not available."); + } + + return versionedFlow.getSnapshotMetadata(); + } + @Override public FlowRegistry removeFlowRegistry(final String registryId) { final FlowRegistry registry = flowRegistryClient.removeFlowRegistry(registryId); 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 7828337690..963220e799 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 @@ -39,9 +39,11 @@ import org.apache.nifi.web.dao.ProcessGroupDAO; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; +import java.util.stream.Collectors; public class StandardProcessGroupDAO extends ComponentDAO implements ProcessGroupDAO { @@ -294,6 +296,26 @@ public class StandardProcessGroupDAO extends ComponentDAO implements ProcessGrou group.verifyCanDelete(); } + @Override + public void verifyDeleteFlowRegistry(String registryId) { + final ProcessGroup rootGroup = flowController.getRootGroup(); + + final VersionControlInformation versionControlInformation = rootGroup.getVersionControlInformation(); + if (versionControlInformation != null && versionControlInformation.getRegistryIdentifier().equals(registryId)) { + throw new IllegalStateException("The Registry cannot be removed because a Process Group currently under version control is tracking to it."); + } + + final Set trackedVersionControlInformation = rootGroup.findAllProcessGroups().stream() + .map(group -> group.getVersionControlInformation()) + .filter(Objects::nonNull) + .filter(vci -> vci.getRegistryIdentifier().equals(registryId)) + .collect(Collectors.toSet()); + + if (!trackedVersionControlInformation.isEmpty()) { + throw new IllegalStateException("The Registry cannot be removed because a Process Group currently under version control is tracking to it."); + } + } + @Override public void deleteProcessGroup(String processGroupId) { // get the group diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index e71de670d0..43843ef265 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -213,7 +213,6 @@ - @@ -221,7 +220,6 @@ - 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 c57b76f376..258bdad68e 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 @@ -116,6 +116,7 @@ +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-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/import-flow-version-dialog.jsp new file mode 100644 index 0000000000..5169c7cdc2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp @@ -0,0 +1,44 @@ +<%-- + 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/navigation.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp index 0732b3df47..86f4ba3647 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/navigation.jsp @@ -128,13 +128,13 @@
 
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-process-group-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-process-group-dialog.jsp index cb538c6edf..3f1b6a0733 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-process-group-dialog.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-process-group-dialog.jsp @@ -23,5 +23,8 @@
+
+ Import version... +
\ 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/new-remote-process-group-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-remote-process-group-dialog.jsp index 49480016e6..524a8243be 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-remote-process-group-dialog.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-remote-process-group-dialog.jsp @@ -22,7 +22,7 @@
- +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registry-configuration-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registry-configuration-dialog.jsp index 7fa90f7eab..56a5fd8b16 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registry-configuration-dialog.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registry-configuration-dialog.jsp @@ -25,9 +25,9 @@
-
Location
+
URL
- +
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 dfed40911f..5e653d2879 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 @@ -20,34 +20,36 @@
Registry
-
- + +
Location
-
- + +
Name
- - + + +
Description
- + +
-
Change Comments
+
Comments
- +
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 1c44e717d0..f8f5177027 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 @@ -213,13 +213,27 @@ div.progress-label { } /* - Flow Version + Save Flow Version */ -#flow-version-description, #flow-version-change-comments { +#save-flow-version-description-field, #save-flow-version-change-comments { height: 85px; } +/* + Import Flow Version + */ + +#import-flow-version-table { + overflow: hidden; + position: absolute; + top: 202px; + left: 0px; + right: 0px; + bottom: 0px; + height: 225px; +} + /* 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 fcd4aba234..ae9e228550 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 @@ -24,9 +24,10 @@ 'nf.Birdseye', 'nf.Graph', 'nf.CanvasUtils', - 'nf.ErrorHandler'], - function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler) { - return (nf.ng.GroupComponent = factory($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler)); + 'nf.ErrorHandler', + 'nf.Common'], + function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon) { + return (nf.ng.GroupComponent = factory($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.ng.GroupComponent = @@ -35,16 +36,18 @@ require('nf.Birdseye'), require('nf.Graph'), require('nf.CanvasUtils'), - require('nf.ErrorHandler'))); + require('nf.ErrorHandler'), + require('nf.Common'))); } else { nf.ng.GroupComponent = factory(root.$, root.nf.Client, root.nf.Birdseye, root.nf.Graph, root.nf.CanvasUtils, - root.nf.ErrorHandler); + root.nf.ErrorHandler, + root.nf.Common); } -}(this, function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler) { +}(this, function ($, nfClient, nfBirdseye, nfGraph, nfCanvasUtils, nfErrorHandler, nfCommon) { 'use strict'; return function (serviceProvider) { @@ -126,6 +129,7 @@ handler: { close: function () { $('#new-process-group-name').val(''); + $('#new-process-group-dialog').removeData('pt'); } } }); @@ -145,9 +149,24 @@ * Show the modal. */ show: function () { + if (nfCommon.canVersionFlows()) { + $('#import-process-group-link').show(); + } else { + $('#import-process-group-link').hide(); + } + this.getElement().modal('show'); }, + /** + * Stores the pt. + * + * @param pt + */ + storePt: function (pt) { + $('#new-process-group-dialog').data('pt', pt); + }, + /** * Hide the modal. */ @@ -255,6 +274,7 @@ }]); // show the dialog + groupComponent.modal.storePt(pt); groupComponent.modal.show(); // set up the focus and key handlers 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 ff093309f9..9dc20b1a4b 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 @@ -1254,14 +1254,14 @@ }, /** - * Reverts outstanding changes. + * Reverts local changes. */ - revertFlowChanges: function (selection) { + revertLocalChanges: function (selection) { if (selection.empty()) { - nfFlowVersion.revertFlowChanges(nfCanvasUtils.getGroupId()); + nfFlowVersion.revertLocalChanges(nfCanvasUtils.getGroupId()); } else if (selection.size() === 1) { var selectionData = selection.datum(); - nfFlowVersion.revertFlowChanges(selectionData.id); + nfFlowVersion.revertLocalChanges(selectionData.id); } }, @@ -1269,18 +1269,25 @@ * Changes the flow version. */ changeFlowVersion: function (selection) { - + if (selection.empty()) { + nfFlowVersion.showChangeFlowVersionDialog(nfCanvasUtils.getGroupId()); + } else if (selection.size() === 1) { + var selectionData = selection.datum(); + if (nfCanvasUtils.isProcessGroup(selection)) { + nfFlowVersion.showChangeFlowVersionDialog(selectionData.id); + } + } }, /** * Disconnects a Process Group from flow versioning. */ - disconnectFlowVersioning: function (selection) { + stopVersionControl: function (selection) { if (selection.empty()) { - nfFlowVersion.disconnectFlowVersioning(nfCanvasUtils.getGroupId()); + nfFlowVersion.stopVersionControl(nfCanvasUtils.getGroupId()); } else if (selection.size() === 1) { var selectionData = selection.datum(); - nfFlowVersion.disconnectFlowVersioning(selectionData.id); + nfFlowVersion.stopVersionControl(selectionData.id); } }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js index 6e17b27ba7..536f87b6eb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js @@ -337,7 +337,6 @@ nfCanvas.setManagedAuthorizer(configDetails.supportsManagedAuthorizer); nfCanvas.setConfigurableAuthorizer(configDetails.supportsConfigurableAuthorizer); nfCanvas.setConfigurableUsersAndGroups(configDetails.supportsConfigurableUsersAndGroups); - nfCanvas.setSupportsFlowVersioning(configDetails.supportsFlowVersioning); // init nfStorage nfStorage.init(); @@ -356,7 +355,7 @@ nfQueueListing.init(); nfVariableRegistry.init(); nfComponentState.init(); - nfFlowVersion.init(); + nfFlowVersion.init(configDetails.timeOffset); nfComponentVersion.init(nfSettings); // initialize the component behaviors diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js index 9506fef137..213b5724a5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js @@ -1819,13 +1819,6 @@ } }, - /** - * Returns whether this NiFi supports flow versioning. - */ - supportsFlowVersioning: function () { - return nfCanvas.supportsFlowVersioning(); - }, - /** * Returns whether the authorizer is managed. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index d0ad4ee368..49ded8cd69 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -85,7 +85,6 @@ var permissions = null; var parentGroupId = null; var managedAuthorizer = false; - var supportsFlowVersioning = false; var configurableAuthorizer = false; var configurableUsersAndGroups = false; var svg = null; @@ -909,23 +908,6 @@ return managedAuthorizer; }, - /** - * Set whether this NiFi supports flow versioning. - * - * @param bool Whether this NiFi supports flow versioning - */ - setSupportsFlowVersioning: function (bool) { - supportsFlowVersioning = bool; - }, - - /** - * - * @returns {boolean} - */ - supportsFlowVersioning: function () { - return supportsFlowVersioning; - }, - /** * Set whether the authorizer is configurable. * 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 f12808627b..865603564f 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 @@ -375,11 +375,17 @@ * @param selection */ var supportsFlowVersioning = function (selection) { - if (nfCanvasUtils.supportsFlowVersioning() === false) { + if (nfCommon.canVersionFlows() === false) { return false; } if (selection.empty()) { + // prevent versioning of the root group + if (nfCanvasUtils.getParentGroupId() === null) { + return false; + } + + // if not root group, ensure adequate permissions return nfCanvasUtils.canReadCurrentGroup() && nfCanvasUtils.canWriteCurrentGroup(); } @@ -632,13 +638,13 @@ {id: 'variable-registry-menu-item', condition: hasVariables, menuItem: {clazz: 'fa', text: 'Variables', action: 'openVariableRegistry'}}, {separator: true}, {id: 'version-menu-item', groupMenuItem: {clazz: 'fa', text: 'Version'}, menuItems: [ - {id: 'start-version-control-menu-item', condition: supportsStartFlowVersioning, menuItem: {clazz: 'fa fa-floppy-o', 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}, - {id: 'commit-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa fa-floppy-o', text: 'Commit local changes', action: 'saveFlowVersion'}}, - {id: 'revert-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Revert local changes', action: 'revertFlowChanges'}}, + {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'}}, {separator: true}, - {id: 'stop-version-control-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Stop version control', action: 'disconnectFlowVersioning'}} + {id: 'stop-version-control-menu-item', condition: supportsStopFlowVersioning, menuItem: {clazz: 'fa', text: 'Stop version control', action: 'stopVersionControl'}} ]}, {separator: true}, {id: 'enter-group-menu-item', condition: isProcessGroup, menuItem: {clazz: 'fa fa-sign-in', text: 'Enter group', action: 'enterGroup'}}, 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 6315d3032c..165262468d 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,9 +30,10 @@ 'nf.Client', 'nf.CanvasUtils', 'nf.ProcessGroup', - 'nf.Graph'], - function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph) { - return (nf.FlowVersion = factory($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph)); + '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)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.FlowVerison = @@ -44,7 +45,8 @@ require('nf.Client'), require('nf.CanvasUtils'), require('nf.ProcessGroup'), - require('nf.Graph'))); + require('nf.Graph'), + require('nf.Birdseye'))); } else { nf.FlowVersion = factory(root.$, root.nf.ng.Bridge, @@ -54,35 +56,121 @@ root.nf.Client, root.nf.CanvasUtils, root.nf.ProcessGroup, - root.nf.Graph); + root.nf.Graph, + root.nf.Birdseye); } -}(this, function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph) { +}(this, function ($, nfNgBridge, nfErrorHandler, nfDialog, nfCommon, nfClient, nfCanvasUtils, nfProcessGroup, nfGraph, nfBirdseye) { 'use strict'; + var serverTimeOffset = null; + + var gridOptions = { + forceFitColumns: true, + enableTextSelectionOnCells: true, + enableCellNavigation: true, + enableColumnReorder: false, + autoEdit: false, + multiSelect: false, + rowHeight: 24 + }; + /** - * Reset the dialog. + * Reset the save flow version dialog. */ - var resetDialog = function () { - $('#flow-version-registry-combo').combo('destroy').hide(); - $('#flow-version-bucket-combo').combo('destroy').hide(); + var resetSaveFlowVersionDialog = function () { + $('#save-flow-version-registry-combo').combo('destroy').hide(); + $('#save-flow-version-bucket-combo').combo('destroy').hide(); - $('#flow-version-registry').text('').hide(); - $('#flow-version-bucket').text('').hide(); + $('#save-flow-version-registry').text('').hide(); + $('#save-flow-version-bucket').text('').hide(); - $('#flow-version-name').val(''); - $('#flow-version-description').val(''); - $('#flow-version-change-comments').val(''); + $('#save-flow-version-name').text('').hide(); + $('#save-flow-version-description').text('').hide(); - $('#flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); + $('#save-flow-version-name-field').val('').hide(); + $('#save-flow-version-description-field').val('').hide(); + $('#save-flow-version-change-comments').val(''); + + $('#save-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); + }; + + /** + * Reset the import flow version dialog. + */ + var resetImportFlowVersionDialog = function () { + $('#import-flow-version-registry-combo').combo('destroy').hide(); + $('#import-flow-version-bucket-combo').combo('destroy').hide(); + $('#import-flow-version-name-combo').combo('destroy').hide(); + + $('#import-flow-version-registry').text('').hide(); + $('#import-flow-version-bucket').text('').hide(); + $('#import-flow-version-name').text('').hide(); + + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + if (nfCommon.isDefinedAndNotNull(importFlowVersionGrid)) { + var importFlowVersionData = importFlowVersionGrid.getData(); + importFlowVersionData.setItems([]); + } + + $('#import-flow-version-process-group-id').removeData('versionControlInformation').removeData('revision').text(''); + }; + + /** + * Loads the registries into the specified registry combo. + * + * @param dialog + * @param registryCombo + * @param bucketCombo + * @returns {deferred} + */ + var loadRegistries = function (dialog, registryCombo, bucketCombo, selectBucket) { + return $.ajax({ + type: 'GET', + url: '../nifi-api/flow/registries', + dataType: 'json' + }).done(function (registriesResponse) { + var registries = []; + + if (nfCommon.isDefinedAndNotNull(registriesResponse.registries) && registriesResponse.registries.length > 0) { + registriesResponse.registries.sort(function (a, b) { + return a.component.name > b.component.name; + }); + + $.each(registriesResponse.registries, function (_, registryEntity) { + var registry = registryEntity.component; + registries.push({ + text: registry.name, + value: registry.id, + description: nfCommon.escapeHtml(registry.description) + }); + }); + } else { + registries.push({ + text: 'No available registries', + value: null, + optionClass: 'unset', + disabled: true + }); + } + + // load the registries + registryCombo.combo({ + options: registries, + select: function (selectedOption) { + selectRegistry(dialog, selectedOption, bucketCombo, selectBucket) + } + }); + }).fail(nfErrorHandler.handleAjaxError); }; /** * Loads the buckets for the specified registryIdentifier for the current user. * * @param registryIdentifier + * @param bucketCombo * @returns {*} */ - var loadBuckets = function (registryIdentifier) { + var loadBuckets = function (registryIdentifier, bucketCombo, selectBucket) { return $.ajax({ type: 'GET', url: '../nifi-api/flow/registries/' + encodeURIComponent(registryIdentifier) + '/buckets', @@ -113,23 +201,24 @@ } // load the buckets - $('#flow-version-bucket-combo').combo('destroy').combo({ + bucketCombo.combo('destroy').combo({ options: buckets, select: selectBucket }); - }).fail(function () { - $('#save-flow-version-dialog').modal('refreshButtons'); }).fail(nfErrorHandler.handleAjaxError); }; /** * Select handler for the registries combo. * + * @param dialog * @param selectedOption + * @param bucketCombo + * @param selectBucket */ - var selectRegistry = function (selectedOption) { - if (selectedOption.disabled === true) { - $('#flow-version-bucket-combo').combo('destroy').combo({ + var selectRegistry = function (dialog, selectedOption, bucketCombo, selectBucket) { + var showNoBucketsAvailable = function () { + bucketCombo.combo('destroy').combo({ options: [{ text: 'No available buckets', value: null, @@ -138,9 +227,15 @@ }] }); - $('#save-flow-version-dialog').modal('refreshButtons'); + dialog.modal('refreshButtons'); + }; + + if (selectedOption.disabled === true) { + showNoBucketsAvailable(); } else { - loadBuckets(selectedOption.value); + loadBuckets(selectedOption.value, bucketCombo, selectBucket).fail(function () { + showNoBucketsAvailable(); + }); } }; @@ -149,7 +244,7 @@ * * @param selectedOption */ - var selectBucket = function (selectedOption) { + var selectBucketSaveFlowVersion = function (selectedOption) { $('#save-flow-version-dialog').modal('refreshButtons'); }; @@ -159,8 +254,8 @@ * @returns {*} */ var saveFlowVersion = function () { - var processGroupId = $('#flow-version-process-group-id').text(); - var processGroupRevision = $('#flow-version-process-group-id').data('revision'); + var processGroupId = $('#save-flow-version-process-group-id').text(); + var processGroupRevision = $('#save-flow-version-process-group-id').data('revision'); var saveFlowVersionRequest = { processGroupRevision: nfClient.getRevision({ @@ -170,27 +265,27 @@ }) }; - var versionControlInformation = $('#flow-version-process-group-id').data('versionControlInformation'); + var versionControlInformation = $('#save-flow-version-process-group-id').data('versionControlInformation'); if (nfCommon.isDefinedAndNotNull(versionControlInformation)) { saveFlowVersionRequest['versionedFlow'] = { registryId: versionControlInformation.registryId, bucketId: versionControlInformation.bucketId, flowId: versionControlInformation.flowId, - flowName: $('#flow-version-name').val(), - description: $('#flow-version-description').val(), - comments: $('#flow-version-change-comments').val() + flowName: $('#save-flow-version-name').text(), + description: $('#save-flow-version-description').text(), + comments: $('#save-flow-version-change-comments').val() } } else { - var selectedRegistry = $('#flow-version-registry-combo').combo('getSelectedOption'); - var selectedBucket = $('#flow-version-bucket-combo').combo('getSelectedOption'); + var selectedRegistry = $('#save-flow-version-registry-combo').combo('getSelectedOption'); + var selectedBucket = $('#save-flow-version-bucket-combo').combo('getSelectedOption'); saveFlowVersionRequest['versionedFlow'] = { registryId: selectedRegistry.value, bucketId: selectedBucket.value, - flowName: $('#flow-version-name').val(), - description: $('#flow-version-description').val(), - comments: $('#flow-version-change-comments').val() - } + flowName: $('#save-flow-version-name-field').val(), + description: $('#save-flow-version-description-field').val(), + comments: $('#save-flow-version-change-comments').val() + }; } return $.ajax({ @@ -202,8 +297,555 @@ }).fail(nfErrorHandler.handleAjaxError); }; + /** + * Sorts the specified data using the specified sort details. + * + * @param {object} sortDetails + * @param {object} data + */ + var sort = function (sortDetails, data) { + // defines a function for sorting + var comparer = function (a, b) { + var aString = nfCommon.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : ''; + var bString = nfCommon.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : ''; + return aString === bString ? 0 : aString > bString ? 1 : -1; + }; + + // perform the sort + data.sort(comparer, sortDetails.sortAsc); + }; + + var initImportFlowVersionTable = function () { + var importFlowVersionTable = $('#import-flow-version-table'); + + var valueFormatter = function (row, cell, value, columnDef, dataContext) { + return nfCommon.escapeHtml(value); + }; + + var timestampFormatter = function (row, cell, value, columnDef, dataContext) { + // get the current user time to properly convert the server time + var now = new Date(); + + // convert the user offset to millis + var userTimeOffset = now.getTimezoneOffset() * 60 * 1000; + + // create the proper date by adjusting by the offsets + var date = new Date(dataContext.timestamp + userTimeOffset + serverTimeOffset); + return nfCommon.formatDateTime(date); + }; + + // define the column model for the controller services table + var importFlowVersionColumns = [ + { + id: 'version', + name: 'Version', + field: 'version', + formatter: valueFormatter, + sortable: true, + resizable: true, + width: 75, + maxWidth: 75 + }, + { + id: 'timestamp', + name: 'Created', + field: 'timestamp', + formatter: timestampFormatter, + sortable: true, + resizable: true, + width: 175, + maxWidth: 175 + }, + { + id: 'changeComments', + name: 'Comments', + field: 'comments', + sortable: true, + resizable: true, + formatter: valueFormatter + } + ]; + + // initialize the dataview + var importFlowVersionData = new Slick.Data.DataView({ + inlineFilters: false + }); + + // initialize the sort + sort({ + columnId: 'version', + sortAsc: false + }, importFlowVersionData); + + // initialize the grid + var importFlowVersionGrid = new Slick.Grid(importFlowVersionTable, importFlowVersionData, importFlowVersionColumns, gridOptions); + importFlowVersionGrid.setSelectionModel(new Slick.RowSelectionModel()); + importFlowVersionGrid.registerPlugin(new Slick.AutoTooltips()); + importFlowVersionGrid.setSortColumn('version', false); + importFlowVersionGrid.onSort.subscribe(function (e, args) { + sort({ + columnId: args.sortCol.id, + sortAsc: args.sortAsc + }, importFlowVersionData); + }); + importFlowVersionGrid.onSelectedRowsChanged.subscribe(function (e, args) { + $('#import-flow-version-dialog').modal('refreshButtons'); + }); + importFlowVersionGrid.onDblClick.subscribe(function (e, args) { + changeFlowVersion().done(function () { + $('#import-flow-version-dialog').modal('hide'); + }); + }); + + // wire up the dataview to the grid + importFlowVersionData.onRowCountChanged.subscribe(function (e, args) { + importFlowVersionGrid.updateRowCount(); + importFlowVersionGrid.render(); + }); + importFlowVersionData.onRowsChanged.subscribe(function (e, args) { + importFlowVersionGrid.invalidateRows(args.rows); + importFlowVersionGrid.render(); + }); + importFlowVersionData.syncGridSelection(importFlowVersionGrid, true); + + // hold onto an instance of the grid + importFlowVersionTable.data('gridInstance', importFlowVersionGrid); + }; + + /** + * Shows the import flow version dialog. + */ + var showImportFlowVersionDialog = function () { + var pt = $('#new-process-group-dialog').data('pt'); + + // update the registry and bucket visibility + var registryCombo = $('#import-flow-version-registry-combo').show(); + var bucketCombo = $('#import-flow-version-bucket-combo').show(); + $('#import-flow-version-name-combo').show(); + + loadRegistries($('#import-flow-version-dialog'), registryCombo, bucketCombo, selectBucketImportVersion).done(function () { + // reposition the version table + $('#import-flow-version-table').css({ + 'top': '202px', + 'height': '225px' + }); + + // show the import dialog + $('#import-flow-version-dialog').modal('setHeaderText', 'Import Version').modal('setButtonModel', [{ + buttonText: 'Import', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + disabled: disableImportOrChangeButton, + handler: { + click: function () { + importFlowVersion(pt).always(function (response) { + // close the dialog + $('#import-flow-version-dialog').modal('hide'); + }); + } + } + }, { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + $(this).modal('hide'); + } + } + }]).modal('show'); + + // hide the new process group dialog + $('#new-process-group-dialog').modal('hide'); + }); + }; + + /** + * Loads the flow versions for the specified registry, bucket, and flow. + * + * @param registryIdentifier + * @param bucketIdentifier + * @param flowIdentifier + * @returns deferred + */ + var loadFlowVersions = function (registryIdentifier, bucketIdentifier, flowIdentifier) { + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + var importFlowVersionData = importFlowVersionGrid.getData(); + + // begin the update + importFlowVersionData.beginUpdate(); + + // remove the current versions + importFlowVersionGrid.setSelectedRows([]); + importFlowVersionGrid.resetActiveCell(); + importFlowVersionData.setItems([]); + + return $.ajax({ + type: 'GET', + url: '../nifi-api/flow/registries/' + encodeURIComponent(registryIdentifier) + '/buckets/' + encodeURIComponent(bucketIdentifier) + '/flows/' + encodeURIComponent(flowIdentifier) + '/versions', + dataType: 'json' + }).done(function (response) { + if (nfCommon.isDefinedAndNotNull(response.versionedFlowSnapshotMetadataSet) && response.versionedFlowSnapshotMetadataSet.length > 0) { + $.each(response.versionedFlowSnapshotMetadataSet, function (_, entity) { + importFlowVersionData.addItem($.extend({ + id: entity.versionedFlowSnapshotMetadata.version + }, entity.versionedFlowSnapshotMetadata)); + }); + } else { + nfDialog.showOkDialog({ + headerText: 'Flow Versions', + dialogContent: 'This flow does not have any versions available.' + }); + } + }).fail(nfErrorHandler.handleAjaxError).always(function () { + // end the update + importFlowVersionData.endUpdate(); + + // resort + importFlowVersionData.reSort(); + importFlowVersionGrid.invalidate(); + }); + }; + + /** + * Loads the versioned flows from the specified registry and bucket. + * + * @param registryIdentifier + * @param bucketIdentifier + * @param selectFlow + * @returns deferred + */ + var loadFlows = function (registryIdentifier, bucketIdentifier, selectFlow) { + return $.ajax({ + type: 'GET', + url: '../nifi-api/flow/registries/' + encodeURIComponent(registryIdentifier) + '/buckets/' + encodeURIComponent(bucketIdentifier) + '/flows', + dataType: 'json' + }).done(function (response) { + var versionedFlows = []; + + if (nfCommon.isDefinedAndNotNull(response.versionedFlows) && response.versionedFlows.length > 0) { + response.versionedFlows.sort(function (a, b) { + return a.versionedFlow.flowName > b.versionedFlow.flowName; + }); + + $.each(response.versionedFlows, function (_, versionedFlowEntity) { + var versionedFlow = versionedFlowEntity.versionedFlow; + versionedFlows.push({ + text: versionedFlow.flowName, + value: versionedFlow.flowId, + description: nfCommon.escapeHtml(versionedFlow.description) + }); + }); + } else { + versionedFlows.push({ + text: 'No available flows', + value: null, + optionClass: 'unset', + disabled: true + }); + } + + // load the buckets + $('#import-flow-version-name-combo').combo('destroy').combo({ + options: versionedFlows, + select: function (selectedFlow) { + if (nfCommon.isDefinedAndNotNull(selectedFlow.value)) { + selectFlow(registryIdentifier, bucketIdentifier, selectedFlow.value) + } else { + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + var importFlowVersionData = importFlowVersionGrid.getData(); + + // clear the current values + importFlowVersionData.beginUpdate(); + importFlowVersionData.setItems([]); + importFlowVersionData.endUpdate(); + } + } + }); + }).fail(nfErrorHandler.handleAjaxError); + }; + + /** + * Handler when a versioned flow is selected. + * + * @param registryIdentifier + * @param bucketIdentifier + * @param flowIdentifier + */ + var selectVersionedFlow = function (registryIdentifier, bucketIdentifier, flowIdentifier) { + loadFlowVersions(registryIdentifier, bucketIdentifier, flowIdentifier).done(function () { + $('#import-flow-version-dialog').modal('refreshButtons'); + }); + }; + + /** + * Handler when a bucket is selected. + * + * @param selectedBucket + */ + var selectBucketImportVersion = function (selectedBucket) { + var selectedRegistry = $('#import-flow-version-registry-combo').combo('getSelectedOption'); + + // load the flows for the currently selected registry and bucket + loadFlows(selectedRegistry.value, selectedBucket.value, selectVersionedFlow); + }; + + /** + * Imports the selected flow version. + */ + var importFlowVersion = function (pt) { + var selectedRegistry = $('#import-flow-version-registry-combo').combo('getSelectedOption'); + var selectedBucket = $('#import-flow-version-bucket-combo').combo('getSelectedOption'); + var selectedFlow = $('#import-flow-version-name-combo').combo('getSelectedOption'); + + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + var selectedVersionIndex = importFlowVersionGrid.getSelectedRows(); + var selectedVersion = importFlowVersionGrid.getDataItem(selectedVersionIndex[0]); + + var processGroupEntity = { + 'revision': nfClient.getRevision({ + 'revision': { + 'version': 0 + } + }), + 'component': { + 'name': selectedFlow.text, // TODO - name from versioned PG? + 'position': { + 'x': pt.x, + 'y': pt.y + }, + 'versionControlInformation': { + 'registryId': selectedRegistry.value, + 'bucketId': selectedBucket.value, + 'flowId': selectedFlow.value, + 'version': selectedVersion.version + } + } + }; + + return $.ajax({ + type: 'POST', + data: JSON.stringify(processGroupEntity), + url: '../nifi-api/process-groups/' + encodeURIComponent(nfCanvasUtils.getGroupId()) + '/process-groups', + dataType: 'json', + contentType: 'application/json' + }).done(function (response) { + // add the process group to the graph + nfGraph.add({ + 'processGroups': [response] + }, { + 'selectAll': true + }); + + // update component visibility + nfGraph.updateVisibility(); + + // update the birdseye + nfBirdseye.refresh(); + }).fail(nfErrorHandler.handleAjaxError); + }; + + /** + * Determines whether the import/change button is disabled. + * + * @returns {boolean} + */ + var disableImportOrChangeButton = function () { + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + if (nfCommon.isDefinedAndNotNull(importFlowVersionGrid)) { + var selected = importFlowVersionGrid.getSelectedRows(); + return selected.length !== 1; + } else { + return true; + } + }; + + /** + * Changes the flow version for the currently selected Process Group. + * + * @returns {deferred} + */ + var changeFlowVersion = function () { + var changeTimer = null; + + var processGroupId = $('#import-flow-version-process-group-id').text(); + var processGroupRevision = $('#import-flow-version-process-group-id').data('revision'); + var versionControlInformation = $('#import-flow-version-process-group-id').data('versionControlInformation'); + + var importFlowVersionGrid = $('#import-flow-version-table').data('gridInstance'); + var selectedVersionIndex = importFlowVersionGrid.getSelectedRows(); + var selectedVersion = importFlowVersionGrid.getDataItem(selectedVersionIndex[0]); + + // TODO - introduce dialog to show current state with option to cancel once available + + var submitChangeRequest = function () { + var changeVersionRequest = { + 'processGroupRevision': nfClient.getRevision({ + 'revision': { + 'version': processGroupRevision.version + } + }), + 'versionControlInformation': { + 'groupId': processGroupId, + 'registryId': versionControlInformation.registryId, + 'bucketId': versionControlInformation.bucketId, + 'flowId': versionControlInformation.flowId, + 'version': selectedVersion.version + } + }; + + return $.ajax({ + type: 'POST', + data: JSON.stringify(changeVersionRequest), + url: '../nifi-api/versions/update-requests/process-groups/' + encodeURIComponent(processGroupId), + dataType: 'json', + contentType: 'application/json' + }).done(function (response) { + console.log(response); + }).fail(nfErrorHandler.handleAjaxError); + }; + + var pollChangeRequest = function (changeRequest) { + getChangeRequest(changeRequest).done(function (response) { + processChangeResponse(response); + }) + }; + + var getChangeRequest = function (changeRequest) { + return $.ajax({ + type: 'GET', + url: changeRequest.uri, + dataType: 'json' + }).fail(nfErrorHandler.handleAjaxError); + }; + + var deleteChangeRequest = function (changeRequest) { + var deleteXhr = $.ajax({ + type: 'DELETE', + url: changeRequest.uri, + dataType: 'json' + }).fail(nfErrorHandler.handleAjaxError); + + updateProcessGroup(processGroupId); + + nfDialog.showOkDialog({ + headerText: 'Change Version', + dialogContent: 'This Process Group version has changed.' + }); + + return deleteXhr; + }; + + var processChangeResponse = function (response) { + var changeRequest = response.request; + + if (nfCommon.isDefinedAndNotNull(changeRequest.failureReason)) { + nfDialog.showOkDialog({ + headerText: 'Change Version', + dialogContent: nfCommon.escapeHtml(changeRequest.failureReason) + }); + } + + if (changeRequest.complete === true) { + deleteChangeRequest(changeRequest); + } else { + changeTimer = setTimeout(function () { + // clear the timer since we've been invoked + changeTimer = null; + + // poll revert request + pollChangeRequest(changeRequest); + }, 2000); + } + }; + + submitChangeRequest().done(function (response) { + processChangeResponse(response); + }); + + }; + + /** + * Gets the version control information for the specified process group id. + * + * @param processGroupId + * @return {deferred} + */ + var getVersionControlInformation = function (processGroupId) { + return $.Deferred(function (deferred) { + if (processGroupId === nfCanvasUtils.getGroupId()) { + $.ajax({ + type: 'GET', + url: '../nifi-api/versions/process-groups/' + encodeURIComponent(processGroupId), + dataType: 'json' + }).done(function (response) { + deferred.resolve(response); + }).fail(function () { + deferred.reject(); + }); + } else { + var processGroup = nfProcessGroup.get(processGroupId); + if (processGroup.permissions.canRead === true && processGroup.permissions.canWrite === true) { + deferred.resolve({ + 'processGroupRevision': processGroup.revision, + 'versionControlInformation': processGroup.component.versionControlInformation + }); + } else { + deferred.reject(); + } + } + }).promise(); + }; + + /** + * Updates the specified process group with the specified version control information. + * + * @param processGroupId + * @param versionControlInformation + */ + var updateVersionControlInformation = function (processGroupId, versionControlInformation) { + // refresh either selected PG or bread crumb to reflect connected/tracking status + if (nfCanvasUtils.getGroupId() === processGroupId) { + nfNgBridge.injector.get('breadcrumbsCtrl').updateVersionControlInformation(processGroupId, versionControlInformation); + nfNgBridge.digest(); + } else { + nfProcessGroup.reload(processGroupId); + } + }; + + /** + * Updates the specified process group following an operation that may change it's contents. + * + * @param processGroupId + */ + var updateProcessGroup = function (processGroupId) { + if (nfCanvasUtils.getGroupId() === processGroupId) { + // if reverting current PG... reload/refresh this group/canvas + + // TODO consider implementing this differently + $.ajax({ + type: 'GET', + url: '../nifi-api/flow/process-groups/' + encodeURIComponent(processGroupId), + dataType: 'json' + }).done(function (response) { + nfGraph.set(response.processGroupFlow.flow); + }).fail(nfErrorHandler.handleAjaxError); + } else { + // if reverting selected PG... reload selected PG to update counts, etc + nfProcessGroup.reload(processGroupId); + } + }; + return { - init: function () { + init: function (timeOffset) { + serverTimeOffset = timeOffset; + // initialize the flow version dialog $('#save-flow-version-dialog').modal({ scrollableContentStyle: 'scrollable', @@ -216,9 +858,9 @@ text: '#ffffff' }, disabled: function () { - if ($('#flow-version-registry-combo').is(':visible')) { - var selectedRegistry = $('#flow-version-registry-combo').combo('getSelectedOption'); - var selectedBucket = $('#flow-version-bucket-combo').combo('getSelectedOption'); + if ($('#save-flow-version-registry-combo').is(':visible')) { + var selectedRegistry = $('#save-flow-version-registry-combo').combo('getSelectedOption'); + var selectedBucket = $('#save-flow-version-bucket-combo').combo('getSelectedOption'); if (nfCommon.isDefinedAndNotNull(selectedRegistry) && nfCommon.isDefinedAndNotNull(selectedBucket)) { return selectedRegistry.disabled === true || selectedBucket.disabled === true; @@ -231,16 +873,10 @@ }, handler: { click: function () { - var processGroupId = $('#flow-version-process-group-id').text(); + var processGroupId = $('#save-flow-version-process-group-id').text(); saveFlowVersion().done(function (response) { - // refresh either selected PG or bread crumb to reflect connected/tracking status - if (nfCanvasUtils.getGroupId() === processGroupId) { - nfNgBridge.injector.get('breadcrumbsCtrl').updateVersionControlInformation(processGroupId, response.versionControlInformation); - nfNgBridge.digest(); - } else { - nfProcessGroup.reload(processGroupId); - } + updateVersionControlInformation(processGroupId, response.versionControlInformation); // close the dialog $('#save-flow-version-dialog').modal('hide'); @@ -262,10 +898,28 @@ }], handler: { close: function () { - resetDialog(); + resetSaveFlowVersionDialog(); } } }); + + // initialize the import flow version dialog + $('#import-flow-version-dialog').modal({ + scrollableContentStyle: 'scrollable', + handler: { + close: function () { + resetImportFlowVersionDialog(); + } + } + }); + + // handle the click for the process group import + $('#import-process-group-link').on('click', function() { + showImportFlowVersionDialog(); + }); + + // initialize the import flow version table + initImportFlowVersionTable(); }, /** @@ -274,88 +928,64 @@ * @param processGroupId */ showFlowVersionDialog: function (processGroupId) { - return $.Deferred(function (deferred) { - $.ajax({ - type: 'GET', - url: '../nifi-api/versions/process-groups/' + encodeURIComponent(processGroupId), - dataType: 'json' - }).done(function (response) { - // record the revision - $('#flow-version-process-group-id').data('revision', response.processGroupRevision).text(processGroupId); + var focusName = true; - if (nfCommon.isDefinedAndNotNull(response.versionControlInformation)) { - var versionControlInformation = response.versionControlInformation; + return $.Deferred(function (deferred) { + getVersionControlInformation(processGroupId).done(function (groupVersionControlInformation) { + // record the revision + $('#save-flow-version-process-group-id').data('revision', groupVersionControlInformation.processGroupRevision).text(processGroupId); + + if (nfCommon.isDefinedAndNotNull(groupVersionControlInformation.versionControlInformation)) { + var versionControlInformation = groupVersionControlInformation.versionControlInformation; // update the registry and bucket visibility - $('#flow-version-registry').text(versionControlInformation.registryId).show(); - $('#flow-version-bucket').text(versionControlInformation.bucketId).show(); + $('#save-flow-version-registry').text(versionControlInformation.registryId).show(); + $('#save-flow-version-bucket').text(versionControlInformation.bucketId).show(); - $('#flow-version-name').val(''); - $('#flow-version-description').val(''); + $('#save-flow-version-name').text(versionControlInformation.flowName).show(); + $('#save-flow-version-description').text('Flow description goes here').show(); // record the versionControlInformation - $('#flow-version-process-group-id').data('versionControlInformation', versionControlInformation) + $('#save-flow-version-process-group-id').data('versionControlInformation', versionControlInformation); + focusName = false; deferred.resolve(); } else { // update the registry and bucket visibility - $('#flow-version-registry-combo').show(); - $('#flow-version-bucket-combo').show(); + $('#save-flow-version-registry-combo').show(); + $('#save-flow-version-bucket-combo').show(); - $.ajax({ - type: 'GET', - url: '../nifi-api/flow/registries', - dataType: 'json' - }).done(function (registriesResponse) { - var registries = []; - - if (nfCommon.isDefinedAndNotNull(registriesResponse.registries) && registriesResponse.registries.length > 0) { - registriesResponse.registries.sort(function (a, b) { - return a.component.name > b.component.name; - }); - - $.each(registriesResponse.registries, function (_, registryEntity) { - var registry = registryEntity.component; - registries.push({ - text: registry.name, - value: registry.id, - description: nfCommon.escapeHtml(registry.description) - }); - }); - } else { - registries.push({ - text: 'No available registries', - value: null, - optionClass: 'unset', - disabled: true - }); - } - - // load the registries - $('#flow-version-registry-combo').combo({ - options: registries, - select: selectRegistry - }); + $('#save-flow-version-name-field').show(); + $('#save-flow-version-description-field').show(); + loadRegistries($('#save-flow-version-dialog'), $('#save-flow-version-registry-combo'), $('#save-flow-version-bucket-combo'), selectBucketSaveFlowVersion).done(function () { deferred.resolve(); }).fail(function () { deferred.reject(); - }).fail(nfErrorHandler.handleAjaxError); + }); } }).fail(nfErrorHandler.handleAjaxError); }).done(function () { $('#save-flow-version-dialog').modal('show'); + + if (focusName) { + $('#save-flow-version-name-field').focus(); + } else { + $('#save-flow-version-change-comments').focus(); + } }).fail(function () { $('#save-flow-version-dialog').modal('refreshButtons'); }).promise(); }, /** - * Reverts changes for the specified Process Group. + * Reverts local changes for the specified Process Group. * * @param processGroupId */ - revertFlowChanges: function (processGroupId) { + revertLocalChanges: function (processGroupId) { + // TODO update to show user the ramifications of reverting for confirmation + // prompt the user before reverting nfDialog.showYesNoDialog({ headerText: 'Revert Changes', @@ -363,50 +993,88 @@ noText: 'Cancel', yesText: 'Revert', yesHandler: function () { - $.ajax({ - type: 'GET', - url: '../nifi-api/versions/process-groups/' + encodeURIComponent(processGroupId), - dataType: 'json' - }).done(function (response) { + getVersionControlInformation(processGroupId).done(function (response) { if (nfCommon.isDefinedAndNotNull(response.versionControlInformation)) { - var revertFlowVersionRequest = { - processGroupRevision: nfClient.getRevision({ - revision: { - version: response.processGroupRevision.version - } - }), - versionControlInformation: response.versionControlInformation + var revertTimer = null; + + // TODO - introduce dialog to show current state once available + + 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' + }).fail(nfErrorHandler.handleAjaxError); }; - $.ajax({ - type: 'POST', - data: JSON.stringify(revertFlowVersionRequest), - url: '../nifi-api/versions/revert-requests/process-groups/' + encodeURIComponent(processGroupId), - dataType: 'json', - contentType: 'application/json' - }).done(function (response) { - // TODO update multi step to show user the ramifications of reverting for confirmation + var pollRevertRequest = function (revertRequest) { + getRevertRequest(revertRequest).done(function (response) { + processRevertResponse(response); + }) + }; - if (nfCanvasUtils.getGroupId() === processGroupId) { - // if reverting current PG... reload/refresh this group/canvas - // TODO consider implementing this differently - $.ajax({ - type: 'GET', - url: '../nifi-api/flow/process-groups/' + encodeURIComponent(processGroupId), - dataType: 'json' - }).done(function (response) { - nfGraph.set(response.processGroupFlow.flow); - }).fail(nfErrorHandler.handleAjaxError); - } else { - // if reverting selected PG... reload selected PG to update counts, etc - nfProcessGroup.reload(processGroupId); - } + var getRevertRequest = function (revertRequest) { + return $.ajax({ + type: 'GET', + url: revertRequest.uri, + dataType: 'json' + }).fail(nfErrorHandler.handleAjaxError); + }; + + var deleteRevertRequest = function (revertRequest) { + var deleteXhr = $.ajax({ + type: 'DELETE', + url: revertRequest.uri, + dataType: 'json' + }).fail(nfErrorHandler.handleAjaxError); + + updateProcessGroup(processGroupId); nfDialog.showOkDialog({ headerText: 'Revert Changes', dialogContent: 'This Process Group has been reverted.' }); - }).fail(nfErrorHandler.handleAjaxError); + + return deleteXhr; + }; + + var processRevertResponse = function (response) { + var revertRequest = response.request; + + if (nfCommon.isDefinedAndNotNull(revertRequest.failureReason)) { + nfDialog.showOkDialog({ + headerText: 'Revert Changes', + dialogContent: nfCommon.escapeHtml(revertRequest.failureReason) + }); + } + + if (revertRequest.complete === true) { + deleteRevertRequest(revertRequest); + } else { + revertTimer = setTimeout(function () { + // clear the timer since we've been invoked + revertTimer = null; + + // poll revert request + pollRevertRequest(revertRequest); + }, 2000); + } + }; + + submitRevertRequest().done(function (response) { + processRevertResponse(response); + }); } else { nfDialog.showOkDialog({ headerText: 'Revert Changes', @@ -419,11 +1087,89 @@ }, /** - * Disconnects the specified Process Group from flow versioning. + * Shows the change flow version dialog. * * @param processGroupId */ - disconnectFlowVersioning: function (processGroupId) { + showChangeFlowVersionDialog: function (processGroupId) { + return $.Deferred(function (deferred) { + getVersionControlInformation(processGroupId).done(function (groupVersionControlInformation) { + if (nfCommon.isDefinedAndNotNull(groupVersionControlInformation.versionControlInformation)) { + var versionControlInformation = groupVersionControlInformation.versionControlInformation; + + // update the registry and bucket visibility + $('#import-flow-version-registry').text(versionControlInformation.registryId).show(); + $('#import-flow-version-bucket').text(versionControlInformation.bucketId).show(); + $('#import-flow-version-name').text(versionControlInformation.flowId).show(); + + // record the versionControlInformation + $('#import-flow-version-process-group-id').data('versionControlInformation', versionControlInformation).data('revision', groupVersionControlInformation.processGroupRevision).text(processGroupId); + + // load the flow versions + loadFlowVersions(versionControlInformation.registryId, versionControlInformation.bucketId, versionControlInformation.flowId).done(function () { + deferred.resolve(); + }).fail(function () { + nfDialog.showOkDialog({ + headerText: 'Change Version', + dialogContent: 'Unable to load available versions for this Process Group.' + }); + + deferred.reject(); + }); + } else { + nfDialog.showOkDialog({ + headerText: 'Change Version', + dialogContent: 'This Process Group is not currently under version control.' + }); + + deferred.reject(); + } + }).fail(nfErrorHandler.handleAjaxError); + }).done(function () { + // reposition the version table + $('#import-flow-version-table').css({ + 'top': '150px', + 'height': '277px' + }); + + // show the dialog + $('#import-flow-version-dialog').modal('setHeaderText', 'Change Version').modal('setButtonModel', [{ + buttonText: 'Change', + color: { + base: '#728E9B', + hover: '#004849', + text: '#ffffff' + }, + disabled: disableImportOrChangeButton, + handler: { + click: function () { + changeFlowVersion().done(function () { + $('#import-flow-version-dialog').modal('hide'); + }); + } + } + }, { + buttonText: 'Cancel', + color: { + base: '#E3E8EB', + hover: '#C7D2D7', + text: '#004849' + }, + handler: { + click: function () { + $(this).modal('hide'); + } + } + }]).modal('show'); + }).promise(); + }, + + /** + * Stops version control for the specified Process Group. + * + * @param processGroupId + */ + stopVersionControl: function (processGroupId) { // prompt the user before disconnecting nfDialog.showYesNoDialog({ headerText: 'Disconnect', @@ -449,13 +1195,7 @@ dataType: 'json', contentType: 'application/json' }).done(function (response) { - // refresh either selected PG or bread crumb to reflect disconnected status - if (nfCanvasUtils.getGroupId() === processGroupId) { - nfNgBridge.injector.get('breadcrumbsCtrl').updateVersionControlInformation(processGroupId, undefined); - nfNgBridge.digest(); - } else { - nfProcessGroup.reload(processGroupId); - } + updateVersionControlInformation(processGroupId, undefined); nfDialog.showOkDialog({ headerText: 'Disconnect', diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js index 0e5a2d796d..d3289c7646 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js @@ -82,8 +82,7 @@ reportingTaskTypes: '../nifi-api/flow/reporting-task-types', createReportingTask: '../nifi-api/controller/reporting-tasks', reportingTasks: '../nifi-api/flow/reporting-tasks', - createRegistry: '../nifi-api/controller/registries', - registries: '../nifi-api/flow/registries' + registries: '../nifi-api/controller/registry-clients' } }; @@ -486,7 +485,7 @@ // add the new registry var addRegistry = $.ajax({ type: 'POST', - url: config.urls.createRegistry, + url: config.urls.registries, data: JSON.stringify(registryEntity), dataType: 'json', contentType: 'application/json' @@ -877,7 +876,6 @@ // initialize the registry configuration dialog $('#registry-configuration-dialog').modal({ scrollableContentStyle: 'scrollable', - headerText: 'Add Registry', handler: { close: function () { $('#registry-id').text(''); @@ -1351,7 +1349,7 @@ $('#registry-description').val(registryEntity.component.description); // show the dialog - $('#registry-configuration-dialog').modal('setButtonModel', [{ + $('#registry-configuration-dialog').modal('setHeaderText', 'Edit Registry Client').modal('setButtonModel', [{ buttonText: 'Update', color: { base: '#728E9B', @@ -1597,7 +1595,7 @@ name: 'Reporting Tasks', tabContentId: 'reporting-tasks-tab-content' }, { - name: 'Registries', + name: 'Registry Clients', tabContentId: 'registries-tab-content' }], select: function () { @@ -1625,9 +1623,9 @@ } else if (tab === 'Reporting Tasks') { $('#settings-save').hide(); return 'Create a new reporting task'; - } else if (tab === 'Registries') { + } else if (tab === 'Registry Clients') { $('#settings-save').hide(); - return 'Register a new registry'; + return 'Register a new registry client'; } }); } else { @@ -1637,7 +1635,7 @@ if (tab === 'Reporting Task Controller Services') { $('#controller-cs-availability').show(); - } else if (tab === 'Reporting Tasks' || tab === 'Registries') { + } else if (tab === 'Reporting Tasks' || tab === 'Registry Clients') { $('#controller-cs-availability').hide(); } @@ -1676,8 +1674,8 @@ // set the initial focus $('#reporting-task-type-filter').focus(); - } else if (selectedTab === 'Registries') { - $('#registry-configuration-dialog').modal('setButtonModel', [{ + } else if (selectedTab === 'Registry Clients') { + $('#registry-configuration-dialog').modal('setHeaderText', 'Add Registry Client').modal('setButtonModel', [{ buttonText: 'Add', color: { base: '#728E9B', @@ -1702,6 +1700,9 @@ } } }]).modal('show'); + + // set the initial focus + $('#registry-name').focus(); } }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 0d55b27162..5619bc3c61 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -556,6 +556,17 @@ return null; }, + /** + * Determines whether the current user can version flows. + */ + canVersionFlows: function () { + if (nfCommon.isDefinedAndNotNull(nfCommon.currentUser)) { + return nfCommon.currentUser.canVersionFlows === true; + } else { + return false; + } + }, + /** * Determines whether the current user can access provenance. *