You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2018/01/18 17:44:32 UTC
nifi-registry git commit: NIFIREG-77 - Add a 'diff' endpoint to the
API/Client for comparing 2 versions of a flow.
Repository: nifi-registry
Updated Branches:
refs/heads/master 4cfa8c958 -> 915aa3955
NIFIREG-77 - Add a 'diff' endpoint to the API/Client for comparing 2 versions of a flow.
NIFIREG-77 - Add client code for diff operation
This closes #88.
Signed-off-by: Bryan Bende <bb...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/915aa395
Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/915aa395
Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/915aa395
Branch: refs/heads/master
Commit: 915aa395576a30d352e76db6d8d0240da63c01b4
Parents: 4cfa8c9
Author: Danny Lane <da...@gmail.com>
Authored: Mon Jan 8 23:51:27 2018 +0000
Committer: Bryan Bende <bb...@apache.org>
Committed: Thu Jan 18 12:44:04 2018 -0500
----------------------------------------------------------------------
.../apache/nifi/registry/client/FlowClient.java | 13 +++
.../registry/client/impl/JerseyFlowClient.java | 22 ++++
.../nifi/registry/diff/ComponentDifference.java | 77 ++++++++++++++
.../registry/diff/ComponentDifferenceGroup.java | 96 +++++++++++++++++
.../registry/diff/VersionedFlowDifference.java | 79 ++++++++++++++
nifi-registry-framework/pom.xml | 5 +
.../nifi/registry/service/DataModelMapper.java | 35 +++++++
.../nifi/registry/service/RegistryService.java | 105 +++++++++++++++++++
.../registry/service/TestRegistryService.java | 89 +++++++++++++++-
.../registry/web/api/BucketFlowResource.java | 27 +++++
.../web/api/UnsecuredNiFiRegistryClientIT.java | 27 ++++-
11 files changed, 572 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
index 46526f7..150927a 100644
--- a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
+++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.registry.client;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
@@ -106,4 +107,16 @@ public interface FlowClient {
*/
List<VersionedFlow> getByBucket(String bucketId) throws NiFiRegistryException, IOException;
+ /**
+ *
+ * @param bucketId a bucket id
+ * @param flowId the flow that is under inspection
+ * @param versionA the first version to use in the comparison
+ * @param versionB the second flow to use in the comparison
+ * @return the list of differences between the 2 flow versions grouped by component
+ * @throws NiFiRegistryException if an error is encountered other than IOException
+ * @throws IOException if an I/O error is encountered
+ */
+ VersionedFlowDifference diff(final String bucketId, final String flowId,
+ final Integer versionA, final Integer versionB) throws NiFiRegistryException, IOException;
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
----------------------------------------------------------------------
diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
index 95e1fb4..08e8cbb 100644
--- a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
+++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
@@ -19,6 +19,7 @@ package org.apache.nifi.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.FlowClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
@@ -184,5 +185,26 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClien
});
}
+ @Override
+ public VersionedFlowDifference diff(final String bucketId, final String flowId,
+ final Integer versionA, final Integer versionB) throws NiFiRegistryException, IOException {
+ if (StringUtils.isBlank(bucketId)) {
+ throw new IllegalArgumentException("Bucket Identifier cannot be blank");
+ }
+
+ if (StringUtils.isBlank(flowId)) {
+ throw new IllegalArgumentException("Flow Identifier cannot be blank");
+ }
+ return executeAction("Error retrieving flow", () -> {
+ final WebTarget target = bucketFlowsTarget
+ .path("/{flowId}/diff/{versionA}/{versionB}")
+ .resolveTemplate("bucketId", bucketId)
+ .resolveTemplate("flowId", flowId)
+ .resolveTemplate("versionA", versionA)
+ .resolveTemplate("versionB", versionB);
+
+ return getRequestBuilder(target).get(VersionedFlowDifference.class);
+ });
+ }
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifference.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifference.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifference.java
new file mode 100644
index 0000000..d4fb5d0
--- /dev/null
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifference.java
@@ -0,0 +1,77 @@
+/*
+ * 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.registry.diff;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Represents a specific, individual difference that has changed between 2 versions.
+ * The change data and textual descriptions of the change are included for client consumption.
+ */
+public class ComponentDifference {
+ private String valueA;
+ private String valueB;
+ private String changeDescription;
+ private String differenceType;
+ private String differenceTypeDescription;
+
+ @ApiModelProperty("The earlier value from the difference.")
+ public String getValueA() {
+ return valueA;
+ }
+
+ public void setValueA(String valueA) {
+ this.valueA = valueA;
+ }
+
+ @ApiModelProperty("The newer value from the difference.")
+ public String getValueB() {
+ return valueB;
+ }
+
+ public void setValueB(String valueB) {
+ this.valueB = valueB;
+ }
+
+ @ApiModelProperty("The description of the change.")
+ public String getChangeDescription() {
+ return changeDescription;
+ }
+
+ public void setChangeDescription(String changeDescription) {
+ this.changeDescription = changeDescription;
+ }
+
+ @ApiModelProperty("The key to the difference.")
+ public String getDifferenceType() {
+ return differenceType;
+ }
+
+ public void setDifferenceType(String differenceType) {
+ this.differenceType = differenceType;
+ }
+
+ @ApiModelProperty("The description of the change type.")
+ public String getDifferenceTypeDescription() {
+ return differenceTypeDescription;
+ }
+
+ public void setDifferenceTypeDescription(String differenceTypeDescription) {
+ this.differenceTypeDescription = differenceTypeDescription;
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifferenceGroup.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifferenceGroup.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifferenceGroup.java
new file mode 100644
index 0000000..a7b1d58
--- /dev/null
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/ComponentDifferenceGroup.java
@@ -0,0 +1,96 @@
+/*
+ * 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.registry.diff;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a group of differences related to a specific component in a flow.
+ */
+public class ComponentDifferenceGroup {
+ private String componentId;
+ private String componentName;
+ private String componentType;
+ private String processGroupId;
+ private Set<ComponentDifference> differences = new HashSet<>();
+
+ @ApiModelProperty("The id of the component whose changes are grouped together.")
+ public String getComponentId() {
+ return componentId;
+ }
+
+ public void setComponentId(String componentId) {
+ this.componentId = componentId;
+ }
+
+ @ApiModelProperty("The name of the component whose changes are grouped together.")
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public void setComponentName(String componentName) {
+ this.componentName = componentName;
+ }
+
+ @ApiModelProperty("The type of component these changes relate to.")
+ public String getComponentType() {
+ return componentType;
+ }
+
+ public void setComponentType(String componentType) {
+ this.componentType = componentType;
+ }
+
+ @ApiModelProperty("The process group id for this component.")
+ public String getProcessGroupId() {
+ return processGroupId;
+ }
+
+ public void setProcessGroupId(String processGroupId) {
+ this.processGroupId = processGroupId;
+ }
+
+ @ApiModelProperty("The list of changes related to this component between the 2 versions.")
+ public Set<ComponentDifference> getDifferences() {
+ return differences;
+ }
+
+ public void setDifferences(Set<ComponentDifference> differences) {
+ this.differences = differences;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ComponentDifferenceGroup that = (ComponentDifferenceGroup) o;
+ return Objects.equals(componentId, that.componentId)
+ && Objects.equals(componentName, that.componentName)
+ && Objects.equals(componentType, that.componentType)
+ && Objects.equals(processGroupId, that.processGroupId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(componentId, componentName, componentType, processGroupId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/VersionedFlowDifference.java
----------------------------------------------------------------------
diff --git a/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/VersionedFlowDifference.java b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/VersionedFlowDifference.java
new file mode 100644
index 0000000..ccc6a05
--- /dev/null
+++ b/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/diff/VersionedFlowDifference.java
@@ -0,0 +1,79 @@
+/*
+ * 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.registry.diff;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Set;
+
+/**
+ * Represents the result of a diff between 2 versions of the same flow.
+ * A subset of the model classes in registry.flow.diff for exposing on the API
+ * The differences are grouped by component
+ */
+public class VersionedFlowDifference {
+ private String bucketId;
+ private String flowId;
+ private int versionA;
+ private int versionB;
+ private Set<ComponentDifferenceGroup> componentDifferenceGroups;
+
+ public Set<ComponentDifferenceGroup> getComponentDifferenceGroups() {
+ return componentDifferenceGroups;
+ }
+
+ public void setComponentDifferenceGroups(Set<ComponentDifferenceGroup> componentDifferenceGroups) {
+ this.componentDifferenceGroups = componentDifferenceGroups;
+ }
+
+ @ApiModelProperty("The id of the bucket that the flow is stored in.")
+ public String getBucketId() {
+ return bucketId;
+ }
+
+ public void setBucketId(String bucketId) {
+ this.bucketId = bucketId;
+ }
+
+ @ApiModelProperty("The id of the flow that is being examined.")
+ public String getFlowId() {
+ return flowId;
+ }
+
+ public void setFlowId(String flowId) {
+ this.flowId = flowId;
+ }
+
+ @ApiModelProperty("The earlier version from the diff operation.")
+ public int getVersionA() {
+ return versionA;
+ }
+
+ public void setVersionA(int versionA) {
+ this.versionA = versionA;
+ }
+
+ @ApiModelProperty("The latter version from the diff operation.")
+ public int getVersionB() {
+ return versionB;
+ }
+
+ public void setVersionB(int versionB) {
+ this.versionB = versionB;
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-framework/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/pom.xml b/nifi-registry-framework/pom.xml
index 423fbd5..16fde75 100644
--- a/nifi-registry-framework/pom.xml
+++ b/nifi-registry-framework/pom.xml
@@ -313,6 +313,11 @@
<artifactId>apacheds-all</artifactId>
<version>2.0.0-M24</version>
<scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-flow-diff</artifactId>
+ <version>0.1.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java
index 1dbf49a..3436662 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java
@@ -22,8 +22,12 @@ import org.apache.nifi.registry.db.entity.BucketItemEntityType;
import org.apache.nifi.registry.db.entity.FlowEntity;
import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
import org.apache.nifi.registry.db.entity.KeyEntity;
+import org.apache.nifi.registry.diff.ComponentDifference;
+import org.apache.nifi.registry.diff.ComponentDifferenceGroup;
+import org.apache.nifi.registry.flow.VersionedComponent;
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
+import org.apache.nifi.registry.flow.diff.FlowDifference;
import org.apache.nifi.registry.security.key.Key;
import java.util.Date;
@@ -112,6 +116,35 @@ public class DataModelMapper {
return metadata;
}
+ public static ComponentDifference map(final FlowDifference flowDifference){
+ ComponentDifference diff = new ComponentDifference();
+ diff.setChangeDescription(flowDifference.getDescription());
+ diff.setDifferenceType(flowDifference.getDifferenceType().toString());
+ diff.setDifferenceTypeDescription(flowDifference.getDifferenceType().getDescription());
+ diff.setValueA(getValueDescription(flowDifference.getValueA()));
+ diff.setValueB(getValueDescription(flowDifference.getValueB()));
+ return diff;
+ }
+
+ public static ComponentDifferenceGroup map(VersionedComponent versionedComponent){
+ ComponentDifferenceGroup grouping = new ComponentDifferenceGroup();
+ grouping.setComponentId(versionedComponent.getIdentifier());
+ grouping.setComponentName(versionedComponent.getName());
+ grouping.setProcessGroupId(versionedComponent.getGroupIdentifier());
+ grouping.setComponentType(versionedComponent.getComponentType().getTypeName());
+ return grouping;
+ }
+
+ private static String getValueDescription(Object valueA){
+ if(valueA instanceof VersionedComponent){
+ return ((VersionedComponent) valueA).getIdentifier();
+ }
+ if(valueA!= null){
+ return valueA.toString();
+ }
+ return null;
+ }
+
// --- Map keys
public static Key map(final KeyEntity keyEntity) {
@@ -130,4 +163,6 @@ public class DataModelMapper {
return keyEntity;
}
+ // map
+
}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
index e805520..fad517f 100644
--- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
+++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.registry.service;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.nifi.registry.bucket.Bucket;
@@ -24,13 +25,23 @@ import org.apache.nifi.registry.db.entity.BucketEntity;
import org.apache.nifi.registry.db.entity.BucketItemEntity;
import org.apache.nifi.registry.db.entity.FlowEntity;
import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
+import org.apache.nifi.registry.diff.ComponentDifferenceGroup;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.flow.FlowPersistenceProvider;
import org.apache.nifi.registry.flow.FlowSnapshotContext;
+import org.apache.nifi.registry.flow.VersionedComponent;
import org.apache.nifi.registry.flow.VersionedFlow;
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.ConciseEvolvingDifferenceDescriptor;
+import org.apache.nifi.registry.flow.diff.FlowComparator;
+import org.apache.nifi.registry.flow.diff.FlowComparison;
+import org.apache.nifi.registry.flow.diff.FlowDifference;
+import org.apache.nifi.registry.flow.diff.StandardComparableDataFlow;
+import org.apache.nifi.registry.flow.diff.StandardFlowComparator;
import org.apache.nifi.registry.provider.flow.StandardFlowSnapshotContext;
import org.apache.nifi.registry.serialization.Serializer;
import org.slf4j.Logger;
@@ -47,7 +58,9 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -781,6 +794,98 @@ public class RegistryService {
}
}
+ /**
+ * Returns the differences between two specified versions of a flow.
+ *
+ * @param bucketIdentifier the id of the bucket the flow exists in
+ * @param flowIdentifier the flow to be examined
+ * @param versionA the first version of the comparison
+ * @param versionB the second version of the comparison
+ * @return The differences between two specified versions, grouped by component.
+ */
+ public VersionedFlowDifference getFlowDiff(final String bucketIdentifier, final String flowIdentifier,
+ final Integer versionA, final Integer versionB) {
+ if (StringUtils.isBlank(bucketIdentifier)) {
+ throw new IllegalArgumentException("Bucket identifier cannot be null or blank");
+ }
+
+ if (StringUtils.isBlank(flowIdentifier)) {
+ throw new IllegalArgumentException("Flow identifier cannot be null or blank");
+ }
+
+ if (versionA == null || versionB == null) {
+ throw new IllegalArgumentException("Version cannot be null or blank");
+ }
+ // older version is always the lower, regardless of the order supplied
+ final Integer older = Math.min(versionA, versionB);
+ final Integer newer = Math.max(versionA, versionB);
+
+ readLock.lock();
+ try {
+ // Get the content for both versions of the flow
+ final byte[] serializedSnapshotA = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, older);
+ if (serializedSnapshotA == null || serializedSnapshotA.length == 0) {
+ throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
+ + flowIdentifier + " and version " + older);
+ }
+
+ final byte[] serializedSnapshotB = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, newer);
+ if (serializedSnapshotB == null || serializedSnapshotB.length == 0) {
+ throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
+ + flowIdentifier + " and version " + newer);
+ }
+
+ // deserialize the contents
+ final InputStream inputA = new ByteArrayInputStream(serializedSnapshotA);
+ final VersionedProcessGroup flowContentsA = processGroupSerializer.deserialize(inputA);
+ final InputStream inputB = new ByteArrayInputStream(serializedSnapshotB);
+ final VersionedProcessGroup flowContentsB = processGroupSerializer.deserialize(inputB);
+
+ final ComparableDataFlow comparableFlowA = new StandardComparableDataFlow(String.format("Version %d", older), flowContentsA);
+ final ComparableDataFlow comparableFlowB = new StandardComparableDataFlow(String.format("Version %d", newer), flowContentsB);
+
+ // Compare the two versions of the flow
+ final FlowComparator flowComparator = new StandardFlowComparator(comparableFlowA, comparableFlowB,
+ null, new ConciseEvolvingDifferenceDescriptor());
+ final FlowComparison flowComparison = flowComparator.compare();
+
+ VersionedFlowDifference result = new VersionedFlowDifference();
+ result.setBucketId(bucketIdentifier);
+ result.setFlowId(flowIdentifier);
+ result.setVersionA(older);
+ result.setVersionB(newer);
+
+ Set<ComponentDifferenceGroup> differenceGroups = getStringComponentDifferenceGroupMap(flowComparison.getDifferences());
+ result.setComponentDifferenceGroups(differenceGroups);
+
+ return result;
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Group the differences in the comparison by component
+ * @param flowDifferences The differences to group together by component
+ * @return A set of componentDifferenceGroups where each entry contains a set of differences specific to that group
+ */
+ private Set<ComponentDifferenceGroup> getStringComponentDifferenceGroupMap(Set<FlowDifference> flowDifferences) {
+ Map<String, ComponentDifferenceGroup> differenceGroups = new HashMap<>();
+ for (FlowDifference diff : flowDifferences) {
+ ComponentDifferenceGroup group;
+ // A component may only exist on only one version for new/removed components
+ VersionedComponent component = ObjectUtils.firstNonNull(diff.getComponentA(), diff.getComponentB());
+ if(differenceGroups.containsKey(component.getIdentifier())){
+ group = differenceGroups.get(component.getIdentifier());
+ }else{
+ group = DataModelMapper.map(component);
+ differenceGroups.put(component.getIdentifier(), group);
+ }
+ group.getDifferences().add(DataModelMapper.map(diff));
+ }
+ return differenceGroups.values().stream().collect(Collectors.toSet());
+ }
+
// ---------------------- Field methods ---------------------------------------------
public Set<String> getBucketFields() {
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
index 26467ca..f593518 100644
--- a/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
+++ b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
@@ -20,12 +20,16 @@ import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.db.entity.BucketEntity;
import org.apache.nifi.registry.db.entity.FlowEntity;
import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
+import org.apache.nifi.registry.diff.ComponentDifference;
+import org.apache.nifi.registry.diff.ComponentDifferenceGroup;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.flow.FlowPersistenceProvider;
import org.apache.nifi.registry.flow.VersionedFlow;
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.VersionedProcessor;
import org.apache.nifi.registry.serialization.Serializer;
import org.apache.nifi.registry.serialization.VersionedProcessGroupSerializer;
import org.junit.Before;
@@ -43,16 +47,20 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -1006,6 +1014,85 @@ public class TestRegistryService {
return existingBucket;
}
+ // -----------------Test Flow Diff Service Method---------------------
+ @Test
+ public void testGetDiffReturnsRemovedComponentChanges() {
+ when(flowPersistenceProvider.getFlowContent(
+ anyString(), anyString(), anyInt()
+ )).thenReturn(new byte[10], new byte[10]);
+
+ final VersionedProcessGroup pgA = createVersionedProcessGroupA();
+ final VersionedProcessGroup pgB = createVersionedProcessGroupB();
+ when(snapshotSerializer.deserialize(any())).thenReturn(pgA, pgB);
+
+ final VersionedFlowDifference diff = registryService.getFlowDiff(
+ "bucketIdentifier", "flowIdentifier", 1, 2);
+
+ assertNotNull(diff);
+ Optional<ComponentDifferenceGroup> removedComponent = diff.getComponentDifferenceGroups().stream()
+ .filter(p->p.getComponentId().equals("ID-pg1")).findFirst();
+
+ assertTrue(removedComponent.isPresent());
+ assertTrue(removedComponent.get().getDifferences().iterator().next().getDifferenceType().equals("COMPONENT_REMOVED"));
+ }
+
+ @Test
+ public void testGetDiffReturnsChangesInChronologicalOrder() {
+ when(flowPersistenceProvider.getFlowContent(
+ anyString(), anyString(), anyInt()
+ )).thenReturn(new byte[10], new byte[10]);
+
+ final VersionedProcessGroup pgA = createVersionedProcessGroupA();
+ final VersionedProcessGroup pgB = createVersionedProcessGroupB();
+ when(snapshotSerializer.deserialize(any())).thenReturn(pgA, pgB);
+
+ // getFlowDiff orders the changes in ascending order of version number regardless of param order
+ final VersionedFlowDifference diff = registryService.getFlowDiff(
+ "bucketIdentifier", "flowIdentifier", 2,1);
+
+ assertNotNull(diff);
+ Optional<ComponentDifferenceGroup> nameChangedComponent = diff.getComponentDifferenceGroups().stream()
+ .filter(p->p.getComponentId().equals("ProcessorFirstV1")).findFirst();
+
+ assertTrue(nameChangedComponent.isPresent());
+
+ ComponentDifference nameChangeDifference = nameChangedComponent.get().getDifferences().stream()
+ .filter(d-> d.getDifferenceType().equals("NAME_CHANGED")).findFirst().get();
+
+ assertEquals("ProcessorFirstV1", nameChangeDifference.getValueA());
+ assertEquals("ProcessorFirstV2", nameChangeDifference.getValueB());
+ }
+
+ private VersionedProcessGroup createVersionedProcessGroupA() {
+ VersionedProcessGroup root = new VersionedProcessGroup();
+ root.setProcessGroups(new HashSet<>(Arrays.asList(createProcessGroup("ID-pg1"), createProcessGroup("ID-pg2"))));
+ // Add processors
+ root.setProcessors(new HashSet<>(Arrays.asList(createVersionedProcessor("ProcessorFirstV1"), createVersionedProcessor("ProcessorSecondV1"))));
+ return root;
+ }
+
+ private VersionedProcessGroup createProcessGroup(String identifier){
+ VersionedProcessGroup processGroup = new VersionedProcessGroup();
+ processGroup.setIdentifier(identifier);
+ return processGroup;
+ }
+ private VersionedProcessGroup createVersionedProcessGroupB() {
+ VersionedProcessGroup updated = createVersionedProcessGroupA();
+ // remove a process group
+ updated.getProcessGroups().removeIf(pg->pg.getIdentifier().equals("ID-pg1"));
+ // change the name of a processor
+ updated.getProcessors().stream().forEach(p->p.setPenaltyDuration(p.getName().equals("ProcessorFirstV1") ? "1" : "2"));
+ updated.getProcessors().stream().forEach(p->p.setName(p.getName().equals("ProcessorFirstV1") ? "ProcessorFirstV2" : p.getName()));
+ return updated;
+ }
+
+ private VersionedProcessor createVersionedProcessor(String name){
+ VersionedProcessor processor = new VersionedProcessor();
+ processor.setName(name);
+ processor.setIdentifier(name);
+ processor.setProperties(new HashMap<>());
+ return processor;
+ }
// -------------------------------------------------------------------
private Answer<BucketEntity> createBucketAnswer() {
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
index 8d03a03..2e58723 100644
--- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
+++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
@@ -25,6 +25,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.security.authorization.Authorizer;
@@ -402,6 +403,32 @@ public class BucketFlowResource extends AuthorizableApplicationResource {
return Response.status(Response.Status.OK).entity(snapshot).build();
}
+ @GET
+ @Path("{flowId}/diff/{versionA: \\d+}/{versionB: \\d+}")
+ @Consumes(MediaType.WILDCARD)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(
+ value = "Returns a list of differences between 2 versions of a flow",
+ response = VersionedFlowDifference.class
+ )
+ @ApiResponses({
+ @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400),
+ @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
+ @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403),
+ @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
+ @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409)})
+ public Response getFlowDiff(@PathParam("bucketId")
+ @ApiParam("The bucket identifier") final String bucketId,
+ @PathParam("flowId")
+ @ApiParam("The flow identifier") final String flowId,
+ @PathParam("versionA")
+ @ApiParam("The first version number") final Integer versionNumberA,
+ @PathParam("versionB")
+ @ApiParam("The second version number") final Integer versionNumberB) {
+ VersionedFlowDifference result = registryService.getFlowDiff(bucketId, flowId, versionNumberA, versionNumberB);
+ return Response.status(Response.Status.OK).entity(result).build();
+ }
+
private void populateLinksAndPermissions(VersionedFlowSnapshot snapshot) {
if (snapshot.getSnapshotMetadata() != null) {
linkService.populateSnapshotLinks(snapshot.getSnapshotMetadata());
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/915aa395/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
----------------------------------------------------------------------
diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
index a12056b..c733f26 100644
--- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
+++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
@@ -29,12 +29,14 @@ import org.apache.nifi.registry.client.NiFiRegistryClientConfig;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.UserClient;
import org.apache.nifi.registry.client.impl.JerseyNiFiRegistryClient;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
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.VersionedProcessor;
+import org.apache.nifi.registry.flow.VersionedPropertyDescriptor;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -265,6 +267,19 @@ public class UnsecuredNiFiRegistryClientIT extends UnsecuredITBase {
allItems.stream().forEach(i -> Assert.assertNotNull(i.getBucketName()));
bucketItems.stream().forEach(i -> LOGGER.info("Items in bucket, item " + i.getIdentifier()));
+ // ----------------------- TEST DIFF ---------------------------//
+
+ final VersionedFlowSnapshot snapshot3 = buildSnapshot(snapshotFlow, 3);
+ final VersionedProcessGroup newlyAddedPG = new VersionedProcessGroup();
+ newlyAddedPG.setIdentifier("new-pg");
+ newlyAddedPG.setName("NEW Process Group");
+ snapshot3.getFlowContents().getProcessGroups().add(newlyAddedPG);
+ snapshotClient.create(snapshot3);
+
+ VersionedFlowDifference diff = flowClient.diff(snapshotFlow.getBucketIdentifier(), snapshotFlow.getIdentifier(), 3, 2);
+ Assert.assertNotNull(diff);
+ Assert.assertEquals(1, diff.getComponentDifferenceGroups().size());
+
// ---------------------- DELETE DATA --------------------------//
final VersionedFlow deletedFlow1 = flowClient.delete(flowsBucket.getIdentifier(), flow1.getIdentifier());
@@ -302,7 +317,7 @@ public class UnsecuredNiFiRegistryClientIT extends UnsecuredITBase {
return client.create(versionedFlow);
}
- private static VersionedFlowSnapshot createSnapshot(FlowSnapshotClient client, VersionedFlow flow, int num) throws IOException, NiFiRegistryException {
+ private static VersionedFlowSnapshot buildSnapshot(VersionedFlow flow, int num) {
final VersionedFlowSnapshotMetadata snapshotMetadata = new VersionedFlowSnapshotMetadata();
snapshotMetadata.setBucketIdentifier(flow.getBucketIdentifier());
snapshotMetadata.setFlowIdentifier(flow.getIdentifier());
@@ -322,15 +337,19 @@ public class UnsecuredNiFiRegistryClientIT extends UnsecuredITBase {
processorProperties.put("Prop 1", "Val 1");
processorProperties.put("Prop 2", "Val 2");
+ final Map<String, VersionedPropertyDescriptor> propertyDescriptors = new HashMap<>();
+
final VersionedProcessor processor1 = new VersionedProcessor();
processor1.setIdentifier("p1");
processor1.setName("Processor 1");
processor1.setProperties(processorProperties);
+ processor1.setPropertyDescriptors(propertyDescriptors);
final VersionedProcessor processor2 = new VersionedProcessor();
processor2.setIdentifier("p2");
processor2.setName("Processor 2");
processor2.setProperties(processorProperties);
+ processor2.setPropertyDescriptors(propertyDescriptors);
subProcessGroup.getProcessors().add(processor1);
subProcessGroup.getProcessors().add(processor2);
@@ -338,8 +357,12 @@ public class UnsecuredNiFiRegistryClientIT extends UnsecuredITBase {
final VersionedFlowSnapshot snapshot = new VersionedFlowSnapshot();
snapshot.setSnapshotMetadata(snapshotMetadata);
snapshot.setFlowContents(rootProcessGroup);
+ return snapshot;
+ }
+
+ private static VersionedFlowSnapshot createSnapshot(FlowSnapshotClient client, VersionedFlow flow, int num) throws IOException, NiFiRegistryException {
+ final VersionedFlowSnapshot snapshot = buildSnapshot(flow, num);
return client.create(snapshot);
}
-
}