mirror of https://github.com/apache/nifi.git
NIFI-1908 Added encoding-version attribute to TemplateDTO
added scaling of templates upon instantiation for placement on the canvas added template-0.7.0.xml for live-testing the import of templates Fixing issue with potentially uninitialized RemoteGroupPorts in copySnippet. This closes #471
This commit is contained in:
parent
b075f238a5
commit
893daf567d
|
@ -17,16 +17,19 @@
|
||||||
package org.apache.nifi.web.api.dto;
|
package org.apache.nifi.web.api.dto;
|
||||||
|
|
||||||
import com.wordnik.swagger.annotations.ApiModelProperty;
|
import com.wordnik.swagger.annotations.ApiModelProperty;
|
||||||
import java.util.Date;
|
import org.apache.nifi.web.api.dto.util.DateTimeAdapter;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
import org.apache.nifi.web.api.dto.util.DateTimeAdapter;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a template.
|
* Defines a template.
|
||||||
*/
|
*/
|
||||||
@XmlRootElement(name = "template")
|
@XmlRootElement(name = "template")
|
||||||
public class TemplateDTO {
|
public class TemplateDTO {
|
||||||
|
public static final String MAX_ENCODING_VERSION = "1.0";
|
||||||
|
|
||||||
private String uri;
|
private String uri;
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ public class TemplateDTO {
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private Date timestamp;
|
private Date timestamp;
|
||||||
|
private String encodingVersion;
|
||||||
|
|
||||||
private FlowSnippetDTO snippet;
|
private FlowSnippetDTO snippet;
|
||||||
|
|
||||||
|
@ -117,6 +121,22 @@ public class TemplateDTO {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encodingVersion needs to be updated if the {@link TemplateDTO} changes.
|
||||||
|
* @return encoding version of this template.
|
||||||
|
*/
|
||||||
|
@XmlAttribute(name= "encoding-version")
|
||||||
|
@ApiModelProperty(
|
||||||
|
value = "The encoding version of this template."
|
||||||
|
)
|
||||||
|
public String getEncodingVersion() {
|
||||||
|
return encodingVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncodingVersion(String encodingVersion) {
|
||||||
|
this.encodingVersion = encodingVersion;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return snippet in this template
|
* @return snippet in this template
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,41 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.nifi.controller;
|
package org.apache.nifi.controller;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import com.sun.jersey.api.client.ClientHandlerException;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.LockSupport;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.nifi.action.Action;
|
import org.apache.nifi.action.Action;
|
||||||
import org.apache.nifi.admin.service.AuditService;
|
import org.apache.nifi.admin.service.AuditService;
|
||||||
|
@ -227,7 +193,39 @@ import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.sun.jersey.api.client.ClientHandlerException;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
public class FlowController implements EventAccess, ControllerServiceProvider, ReportingTaskProvider, QueueProvider, Authorizable {
|
public class FlowController implements EventAccess, ControllerServiceProvider, ReportingTaskProvider, QueueProvider, Authorizable {
|
||||||
|
|
||||||
|
@ -247,6 +245,10 @@ public class FlowController implements EventAccess, ControllerServiceProvider, R
|
||||||
public static final String DEFAULT_ROOT_GROUP_NAME = "NiFi Flow";
|
public static final String DEFAULT_ROOT_GROUP_NAME = "NiFi Flow";
|
||||||
public static final String PRIMARY_NODE_ROLE_NAME = "primary-node";
|
public static final String PRIMARY_NODE_ROLE_NAME = "primary-node";
|
||||||
|
|
||||||
|
// default properties for scaling the positions of components from pre-1.0 flow encoding versions.
|
||||||
|
public static final double DEFAULT_POSITION_SCALE_FACTOR_X = 1.5;
|
||||||
|
public static final double DEFAULT_POSITION_SCALE_FACTOR_Y = 1.34;
|
||||||
|
|
||||||
private final AtomicInteger maxTimerDrivenThreads;
|
private final AtomicInteger maxTimerDrivenThreads;
|
||||||
private final AtomicInteger maxEventDrivenThreads;
|
private final AtomicInteger maxEventDrivenThreads;
|
||||||
private final AtomicReference<FlowEngine> timerDrivenEngineRef;
|
private final AtomicReference<FlowEngine> timerDrivenEngineRef;
|
||||||
|
@ -1335,7 +1337,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, R
|
||||||
/**
|
/**
|
||||||
* Synchronizes this controller with the proposed flow.
|
* Synchronizes this controller with the proposed flow.
|
||||||
*
|
*
|
||||||
* For more details, see {@link FlowSynchronizer#sync(FlowController, DataFlow)}.
|
* For more details, see {@link FlowSynchronizer#sync(FlowController, DataFlow, StringEncryptor)}.
|
||||||
*
|
*
|
||||||
* @param synchronizer synchronizer
|
* @param synchronizer synchronizer
|
||||||
* @param dataFlow the flow to load the controller with. If the flow is null or zero length, then the controller must not have a flow or else an UninheritableFlowException will be thrown.
|
* @param dataFlow the flow to load the controller with. If the flow is null or zero length, then the controller must not have a flow or else an UninheritableFlowException will be thrown.
|
||||||
|
|
|
@ -74,7 +74,6 @@ import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@ -2149,7 +2148,6 @@ public final class StandardProcessGroup implements ProcessGroup {
|
||||||
positionables.addAll(findAllConnectables(this, true));
|
positionables.addAll(findAllConnectables(this, true));
|
||||||
List<ProcessGroup> allProcessGroups = findAllProcessGroups();
|
List<ProcessGroup> allProcessGroups = findAllProcessGroups();
|
||||||
positionables.addAll(allProcessGroups);
|
positionables.addAll(allProcessGroups);
|
||||||
positionables.addAll(allProcessGroups.stream().flatMap(processGroup -> processGroup.findAllPositionables().stream()).collect(Collectors.toSet()));
|
|
||||||
positionables.addAll(findAllRemoteProcessGroups());
|
positionables.addAll(findAllRemoteProcessGroups());
|
||||||
positionables.addAll(findAllLabels());
|
positionables.addAll(findAllLabels());
|
||||||
return positionables;
|
return positionables;
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.nifi.util;
|
package org.apache.nifi.util;
|
||||||
|
|
||||||
|
import org.apache.nifi.web.api.dto.ComponentDTO;
|
||||||
|
import org.apache.nifi.web.api.dto.ConnectionDTO;
|
||||||
|
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
|
||||||
|
import org.apache.nifi.web.api.dto.PositionDTO;
|
||||||
|
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -24,24 +30,34 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.nifi.web.api.dto.ConnectionDTO;
|
|
||||||
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
|
|
||||||
import org.apache.nifi.web.api.dto.ComponentDTO;
|
|
||||||
import org.apache.nifi.web.api.dto.PositionDTO;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for moving Snippets.
|
* Utility class for moving Snippets.
|
||||||
*/
|
*/
|
||||||
public final class SnippetUtils {
|
public final class SnippetUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the content of the specified template around the specified location.
|
* Moves the content of the specified snippet around the specified location. Does not scale components in child process groups.
|
||||||
*
|
*
|
||||||
* @param snippet snippet
|
* @param snippet snippet
|
||||||
* @param x x location
|
* @param x x location
|
||||||
* @param y y location
|
* @param y y location
|
||||||
*/
|
*/
|
||||||
public static void moveSnippet(FlowSnippetDTO snippet, Double x, Double y) {
|
public static void moveSnippet(FlowSnippetDTO snippet, Double x, Double y) {
|
||||||
|
moveAndScaleSnippet(snippet, x, y, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the content of the specified snippet around the specified location
|
||||||
|
* and scales the placement of individual components of the template by the
|
||||||
|
* given factorX and factorY. Does not scale components in child process groups.
|
||||||
|
*
|
||||||
|
* @param snippet snippet
|
||||||
|
* @param x x location
|
||||||
|
* @param y y location
|
||||||
|
* @param factorX x location scaling factor
|
||||||
|
* @param factorY y location scaling factor
|
||||||
|
*/
|
||||||
|
public static void moveAndScaleSnippet(FlowSnippetDTO snippet, Double x, Double y, double factorX, double factorY) {
|
||||||
// ensure the point is specified
|
// ensure the point is specified
|
||||||
if (x != null && y != null) {
|
if (x != null && y != null) {
|
||||||
final PositionDTO origin = new PositionDTO(x, y);
|
final PositionDTO origin = new PositionDTO(x, y);
|
||||||
|
@ -64,15 +80,15 @@ public final class SnippetUtils {
|
||||||
|
|
||||||
// adjust all component positions
|
// adjust all component positions
|
||||||
for (final PositionDTO position : componentPositionLookup.values()) {
|
for (final PositionDTO position : componentPositionLookup.values()) {
|
||||||
position.setX(origin.getX() + (position.getX() - currentOrigin.getX()));
|
position.setX(origin.getX() + ((position.getX() - currentOrigin.getX()) * factorX));
|
||||||
position.setY(origin.getY() + (position.getY() - currentOrigin.getY()));
|
position.setY(origin.getY() + ((position.getY() - currentOrigin.getY()) * factorY));
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust all connection positions
|
// adjust all connection positions
|
||||||
for (final List<PositionDTO> bends : connectionPositionLookup.values()) {
|
for (final List<PositionDTO> bends : connectionPositionLookup.values()) {
|
||||||
for (final PositionDTO bend : bends) {
|
for (final PositionDTO bend : bends) {
|
||||||
bend.setX(origin.getX() + (bend.getX() - currentOrigin.getX()));
|
bend.setX(origin.getX() + ((bend.getX() - currentOrigin.getX()) * factorX));
|
||||||
bend.setY(origin.getY() + (bend.getY() - currentOrigin.getY()));
|
bend.setY(origin.getY() + ((bend.getY() - currentOrigin.getY()) * factorY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +97,61 @@ public final class SnippetUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the placement of individual components of the snippet by the
|
||||||
|
* given factorX and factorY. Does not scale components in child process groups.
|
||||||
|
*
|
||||||
|
* @param snippet snippet
|
||||||
|
* @param factorX x location scaling factor
|
||||||
|
* @param factorY y location scaling factor
|
||||||
|
*/
|
||||||
|
public static void scaleSnippet(FlowSnippetDTO snippet, double factorX, double factorY) {
|
||||||
|
// get the connections
|
||||||
|
final Collection<ConnectionDTO> connections = getConnections(snippet);
|
||||||
|
|
||||||
|
// get the components and their positions from the template contents
|
||||||
|
final Collection<ComponentDTO> components = getComponents(snippet);
|
||||||
|
|
||||||
|
// only perform the operation if there are components in this snippet
|
||||||
|
if (connections.isEmpty() && components.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the component positions from the snippet contents
|
||||||
|
final Map<ComponentDTO, PositionDTO> componentPositionLookup = getPositionLookup(components);
|
||||||
|
final Map<ConnectionDTO, List<PositionDTO>> connectionPositionLookup = getConnectionPositionLookup(connections);
|
||||||
|
|
||||||
|
// adjust all component positions
|
||||||
|
for (final PositionDTO position : componentPositionLookup.values()) {
|
||||||
|
position.setX(position.getX() * factorX);
|
||||||
|
position.setY(position.getY() * factorY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust all connection positions
|
||||||
|
for (final List<PositionDTO> bends : connectionPositionLookup.values()) {
|
||||||
|
for (final PositionDTO bend : bends) {
|
||||||
|
bend.setX(bend.getX() * factorX);
|
||||||
|
bend.setY(bend.getY() * factorY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the updated positions
|
||||||
|
applyUpdatedPositions(componentPositionLookup, connectionPositionLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all {@link ProcessGroupDTO}s in the given {@link FlowSnippetDTO}.
|
||||||
|
* @param snippet containing the child {@link ProcessGroupDTO}s to be returned
|
||||||
|
* @return List of child {@link ProcessGroupDTO}s found in the given {@link FlowSnippetDTO}.
|
||||||
|
*/
|
||||||
|
public static List<ProcessGroupDTO> findAllProcessGroups(FlowSnippetDTO snippet) {
|
||||||
|
final List<ProcessGroupDTO> allProcessGroups = new ArrayList<>(snippet.getProcessGroups());
|
||||||
|
for (final ProcessGroupDTO childGroup : snippet.getProcessGroups()) {
|
||||||
|
allProcessGroups.addAll(findAllProcessGroups(childGroup.getContents()));
|
||||||
|
}
|
||||||
|
return allProcessGroups;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all connections that are part of the specified template.
|
* Gets all connections that are part of the specified template.
|
||||||
*
|
*
|
||||||
|
@ -176,7 +247,7 @@ public final class SnippetUtils {
|
||||||
/**
|
/**
|
||||||
* Gets the origin of the bounding box of all specified component positions
|
* Gets the origin of the bounding box of all specified component positions
|
||||||
*
|
*
|
||||||
* @param componentPositions position list for components
|
* @param componentPositions position list for components
|
||||||
* @param connectionPositions position list for connections
|
* @param connectionPositions position list for connections
|
||||||
* @return position
|
* @return position
|
||||||
*/
|
*/
|
||||||
|
@ -220,7 +291,7 @@ public final class SnippetUtils {
|
||||||
/**
|
/**
|
||||||
* Applies the updated positions to the corresponding components.
|
* Applies the updated positions to the corresponding components.
|
||||||
*
|
*
|
||||||
* @param componentPositionLookup lookup
|
* @param componentPositionLookup lookup
|
||||||
* @param connectionPositionLookup lookup
|
* @param connectionPositionLookup lookup
|
||||||
*/
|
*/
|
||||||
private static void applyUpdatedPositions(final Map<ComponentDTO, PositionDTO> componentPositionLookup, final Map<ConnectionDTO, List<PositionDTO>> connectionPositionLookup) {
|
private static void applyUpdatedPositions(final Map<ComponentDTO, PositionDTO> componentPositionLookup, final Map<ConnectionDTO, List<PositionDTO>> connectionPositionLookup) {
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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.util
|
||||||
|
|
||||||
|
import org.apache.nifi.web.api.dto.*
|
||||||
|
import spock.lang.Specification
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class SnippetUtilsSpec extends Specification {
|
||||||
|
@Unroll
|
||||||
|
def "test moveAndScaleSnippet"() {
|
||||||
|
given:
|
||||||
|
def Map<ComponentDTO, PositionDTO> positions = (snippet.connections + snippet.inputPorts + snippet.outputPorts + snippet.labels + snippet.processGroups + snippet.processGroups +
|
||||||
|
snippet.processors + snippet.funnels + snippet.remoteProcessGroups).collectEntries { ComponentDTO component ->
|
||||||
|
[(component): new PositionDTO(component.position.x, component.position.y)]
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
SnippetUtils.moveAndScaleSnippet(snippet, nX, nY, fX, fY)
|
||||||
|
|
||||||
|
then:
|
||||||
|
positions.entrySet().forEach {
|
||||||
|
def expectedPosition = calculateMoveAndScalePosition(it.value, oX, oY, nX, nY, fX, fY)
|
||||||
|
def positionScaledBySnippetUtils = it.key.getPosition()
|
||||||
|
assert positionScaledBySnippetUtils.x == expectedPosition.x
|
||||||
|
assert positionScaledBySnippetUtils.y == expectedPosition.y
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
nX | nY | oX | oY | fX | fY | snippet
|
||||||
|
500 | 500 | 0 | 0 | 1.5 | 1.34 | new FlowSnippetDTO()
|
||||||
|
500 | 500 | 10 | 10 | 1.5 | 1.34 | new FlowSnippetDTO(processors: [new ProcessorDTO(position: new PositionDTO(x: 10, y: 10))])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "test scaleSnippet"() {
|
||||||
|
given:
|
||||||
|
def Map<ComponentDTO, PositionDTO> positions = (snippet.connections + snippet.inputPorts + snippet.outputPorts + snippet.labels + snippet.processGroups + snippet.processGroups +
|
||||||
|
snippet.processors + snippet.funnels + snippet.remoteProcessGroups).collectEntries { ComponentDTO component ->
|
||||||
|
[(component): new PositionDTO(component.position.x, component.position.y)]
|
||||||
|
}
|
||||||
|
|
||||||
|
when:
|
||||||
|
SnippetUtils.scaleSnippet(snippet, fX, fY)
|
||||||
|
|
||||||
|
then:
|
||||||
|
positions.entrySet().forEach {
|
||||||
|
def expectedPosition = calculateScalePosition(it.value, fX, fY)
|
||||||
|
def positionScaledBySnippetUtils = it.key.getPosition()
|
||||||
|
assert positionScaledBySnippetUtils.x == expectedPosition.x
|
||||||
|
assert positionScaledBySnippetUtils.y == expectedPosition.y
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
fX | fY | snippet
|
||||||
|
1.5 | 1.34 | new FlowSnippetDTO()
|
||||||
|
1.5 | 1.34 | new FlowSnippetDTO(
|
||||||
|
processors: [new ProcessorDTO(position: new PositionDTO(x: 10, y: 10))],
|
||||||
|
processGroups: [
|
||||||
|
new ProcessGroupDTO(position: new PositionDTO(x: 105, y: -10), name: 'pg2',
|
||||||
|
contents: new FlowSnippetDTO(processors: [new ProcessorDTO(name: 'proc1', position: new PositionDTO(x: 50, y: 60))]))])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def PositionDTO calculateMoveAndScalePosition(position, oldOriginX, oldOriginY, newOriginX, newOriginY, factorX, factorY) {
|
||||||
|
new PositionDTO(
|
||||||
|
x: newOriginX + (position.x - oldOriginX) * factorX,
|
||||||
|
y: newOriginY + (position.y - oldOriginY) * factorY)
|
||||||
|
}
|
||||||
|
|
||||||
|
def PositionDTO calculateScalePosition(position, factorX, factorY) {
|
||||||
|
new PositionDTO(
|
||||||
|
x: position.x * factorX,
|
||||||
|
y: position.y * factorY)
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -350,5 +350,15 @@
|
||||||
<version>1.0.1.RELEASE</version>
|
<version>1.0.1.RELEASE</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spockframework</groupId>
|
||||||
|
<artifactId>spock-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cglib</groupId>
|
||||||
|
<artifactId>cglib-nodep</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,22 +16,25 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.nifi.web.dao.impl;
|
package org.apache.nifi.web.dao.impl;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.nifi.controller.FlowController;
|
import org.apache.nifi.controller.FlowController;
|
||||||
import org.apache.nifi.controller.Template;
|
import org.apache.nifi.controller.Template;
|
||||||
import org.apache.nifi.controller.TemplateUtils;
|
import org.apache.nifi.controller.TemplateUtils;
|
||||||
import org.apache.nifi.controller.exception.ProcessorInstantiationException;
|
import org.apache.nifi.controller.exception.ProcessorInstantiationException;
|
||||||
|
import org.apache.nifi.controller.serialization.FlowEncodingVersion;
|
||||||
import org.apache.nifi.groups.ProcessGroup;
|
import org.apache.nifi.groups.ProcessGroup;
|
||||||
import org.apache.nifi.web.NiFiCoreException;
|
import org.apache.nifi.web.NiFiCoreException;
|
||||||
import org.apache.nifi.web.ResourceNotFoundException;
|
import org.apache.nifi.web.ResourceNotFoundException;
|
||||||
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
|
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
|
||||||
|
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
|
||||||
import org.apache.nifi.web.api.dto.TemplateDTO;
|
import org.apache.nifi.web.api.dto.TemplateDTO;
|
||||||
import org.apache.nifi.web.dao.TemplateDAO;
|
import org.apache.nifi.web.dao.TemplateDAO;
|
||||||
import org.apache.nifi.web.util.SnippetUtils;
|
import org.apache.nifi.web.util.SnippetUtils;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -88,8 +91,23 @@ public class StandardTemplateDAO extends ComponentDAO implements TemplateDAO {
|
||||||
TemplateDTO templateDetails = template.getDetails();
|
TemplateDTO templateDetails = template.getDetails();
|
||||||
FlowSnippetDTO snippet = snippetUtils.copy(templateDetails.getSnippet(), group, idGenerationSeed);
|
FlowSnippetDTO snippet = snippetUtils.copy(templateDetails.getSnippet(), group, idGenerationSeed);
|
||||||
|
|
||||||
// reposition the template contents
|
// calculate scaling factors based on the template encoding version
|
||||||
org.apache.nifi.util.SnippetUtils.moveSnippet(snippet, originX, originY);
|
// attempt to parse the encoding version
|
||||||
|
final FlowEncodingVersion templateEncodingVersion = FlowEncodingVersion.parse(templateDetails.getEncodingVersion());
|
||||||
|
// get the major version, or 0 if no version could be parsed
|
||||||
|
int templateEncodingMajorVersion = templateEncodingVersion != null ? templateEncodingVersion.getMajorVersion() : 0;
|
||||||
|
// based on the major version < 1, use the default scaling factors. Otherwise, don't scale (use factor of 1.0)
|
||||||
|
double factorX = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_X : 1.0;
|
||||||
|
double factorY = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_Y : 1.0;
|
||||||
|
|
||||||
|
// reposition and scale the template contents
|
||||||
|
org.apache.nifi.util.SnippetUtils.moveAndScaleSnippet(snippet, originX, originY, factorX, factorY);
|
||||||
|
|
||||||
|
|
||||||
|
// find all the child process groups in each process group in the top level of this snippet
|
||||||
|
final List<ProcessGroupDTO> childProcessGroups = org.apache.nifi.util.SnippetUtils.findAllProcessGroups(snippet);
|
||||||
|
// scale (but don't reposition) child process groups
|
||||||
|
childProcessGroups.stream().forEach(processGroup -> org.apache.nifi.util.SnippetUtils.scaleSnippet(processGroup.getContents(), factorX, factorY));
|
||||||
|
|
||||||
// instantiate the template into this group
|
// instantiate the template into this group
|
||||||
flowController.instantiateSnippet(group, snippet);
|
flowController.instantiateSnippet(group, snippet);
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* 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.dao.impl
|
||||||
|
|
||||||
|
import org.apache.nifi.authorization.Authorizer
|
||||||
|
import org.apache.nifi.controller.FlowController
|
||||||
|
import org.apache.nifi.controller.Template
|
||||||
|
import org.apache.nifi.controller.serialization.FlowEncodingVersion
|
||||||
|
import org.apache.nifi.controller.service.ControllerServiceProvider
|
||||||
|
import org.apache.nifi.groups.ProcessGroup
|
||||||
|
import org.apache.nifi.web.api.dto.*
|
||||||
|
import org.apache.nifi.web.util.SnippetUtils
|
||||||
|
import spock.lang.Specification
|
||||||
|
import spock.lang.Unroll
|
||||||
|
|
||||||
|
class StandardTemplateDAOSpec extends Specification {
|
||||||
|
|
||||||
|
@Unroll
|
||||||
|
def "test InstantiateTemplate moves and scales templates"() {
|
||||||
|
given:
|
||||||
|
def flowController = Mock FlowController
|
||||||
|
def snippetUtils = new SnippetUtils()
|
||||||
|
snippetUtils.flowController = flowController
|
||||||
|
def dtoFactory = new DtoFactory()
|
||||||
|
dtoFactory.authorizer = Mock Authorizer
|
||||||
|
dtoFactory.controllerServiceProvider = Mock ControllerServiceProvider
|
||||||
|
snippetUtils.dtoFactory = dtoFactory
|
||||||
|
def standardTemplateDAO = new StandardTemplateDAO()
|
||||||
|
standardTemplateDAO.flowController = flowController
|
||||||
|
standardTemplateDAO.snippetUtils = snippetUtils
|
||||||
|
def templateEncodingVersion = FlowEncodingVersion.parse(encodingVersion);
|
||||||
|
// get the major version, or 0 if no version could be parsed
|
||||||
|
int templateEncodingMajorVersion = templateEncodingVersion != null ? templateEncodingVersion.getMajorVersion() : 0;
|
||||||
|
double factorX = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_X : 1.0;
|
||||||
|
double factorY = templateEncodingMajorVersion < 1 ? FlowController.DEFAULT_POSITION_SCALE_FACTOR_Y : 1.0;
|
||||||
|
// get all top-level component starting positions
|
||||||
|
def List<ComponentDTO> components = [snippet.connections + snippet.inputPorts + snippet.outputPorts + snippet.labels + snippet.processGroups + snippet.processGroups +
|
||||||
|
snippet.processors + snippet.funnels + snippet.remoteProcessGroups].flatten()
|
||||||
|
|
||||||
|
// get all starting subcomponent starting positions
|
||||||
|
def List<ComponentDTO> subComponents = org.apache.nifi.util.SnippetUtils.findAllProcessGroups(snippet).collect { ProcessGroupDTO processGroup ->
|
||||||
|
def childSnippet = processGroup.contents
|
||||||
|
childSnippet.connections + childSnippet.inputPorts + childSnippet.outputPorts + childSnippet.labels + childSnippet.processGroups + childSnippet.processGroups +
|
||||||
|
childSnippet.processors + childSnippet.funnels + childSnippet.remoteProcessGroups
|
||||||
|
}.flatten()
|
||||||
|
|
||||||
|
when:
|
||||||
|
def instantiatedTemplate = standardTemplateDAO.instantiateTemplate(rootGroupId, newOriginX, newOriginY, templateId, idGenerationSeed)
|
||||||
|
|
||||||
|
then:
|
||||||
|
flowController.getGroup(_) >> { String gId ->
|
||||||
|
def pg = Mock ProcessGroup
|
||||||
|
pg.identifier >> gId
|
||||||
|
pg.findTemplate(templateId) >> { tId ->
|
||||||
|
def t = Mock Template
|
||||||
|
t.getDetails() >> { tDetails ->
|
||||||
|
def td = Mock TemplateDTO
|
||||||
|
td.snippet >> snippet
|
||||||
|
td.encodingVersion >> encodingVersion
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
pg.inputPorts >> []
|
||||||
|
pg.outputPorts >> []
|
||||||
|
pg.processGroups >> []
|
||||||
|
return pg
|
||||||
|
}
|
||||||
|
flowController.rootGroupId >> rootGroupId
|
||||||
|
flowController.instantiateSnippet(*_) >> {}
|
||||||
|
0 * _
|
||||||
|
|
||||||
|
def instantiatedComponents = [instantiatedTemplate.connections + instantiatedTemplate.inputPorts + instantiatedTemplate.outputPorts + instantiatedTemplate.labels +
|
||||||
|
instantiatedTemplate.processGroups + instantiatedTemplate.processGroups + instantiatedTemplate.processors + instantiatedTemplate.funnels +
|
||||||
|
instantiatedTemplate.remoteProcessGroups].flatten()
|
||||||
|
components.forEach { component ->
|
||||||
|
def correspondingScaledPosition = instantiatedComponents.find { scaledComponent ->
|
||||||
|
scaledComponent.name.equals(component.name)
|
||||||
|
}.position
|
||||||
|
assert correspondingScaledPosition != null
|
||||||
|
def expectedPosition = calculateMoveAndScalePosition(component.position, oldOriginX, oldOriginY, newOriginX, newOriginY, factorX, factorY)
|
||||||
|
assert correspondingScaledPosition.x == expectedPosition.x
|
||||||
|
assert correspondingScaledPosition.y == expectedPosition.y
|
||||||
|
|
||||||
|
}
|
||||||
|
def instantiatedSubComponents = org.apache.nifi.util.SnippetUtils.findAllProcessGroups(instantiatedTemplate).collect { ProcessGroupDTO processGroup ->
|
||||||
|
def childSnippet = processGroup.contents
|
||||||
|
childSnippet.connections + childSnippet.inputPorts + childSnippet.outputPorts + childSnippet.labels + childSnippet.processGroups + childSnippet.processGroups +
|
||||||
|
childSnippet.processors + childSnippet.funnels + childSnippet.remoteProcessGroups
|
||||||
|
}.flatten()
|
||||||
|
subComponents.forEach { subComponent ->
|
||||||
|
def correspondingScaledPosition = instantiatedSubComponents.find { scaledComponent ->
|
||||||
|
scaledComponent.name.equals(subComponent.name)
|
||||||
|
}.position
|
||||||
|
assert correspondingScaledPosition != null
|
||||||
|
def expectedPosition = calculateScalePosition(subComponent.position, factorX, factorY)
|
||||||
|
assert correspondingScaledPosition.x == expectedPosition.x
|
||||||
|
assert correspondingScaledPosition.y == expectedPosition.y
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
where:
|
||||||
|
rootGroupId | oldOriginX | oldOriginY | newOriginX | newOriginY | templateId | idGenerationSeed | encodingVersion | snippet
|
||||||
|
'g1' | 0.0 | 0.0 | 5.0 | 5.0 | 't1' | 'AAAA' | null | new FlowSnippetDTO()
|
||||||
|
'g1' | 10.0 | 10.0 | 5.0 | 5.0 | 't1' | 'AAAA' | '0.7' | new FlowSnippetDTO(
|
||||||
|
processors: [new ProcessorDTO(name: 'proc1', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))])
|
||||||
|
'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 't1' | 'AAAA' | null | new FlowSnippetDTO(
|
||||||
|
processors: [new ProcessorDTO(name: 'proc2', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))],
|
||||||
|
processGroups: [
|
||||||
|
new ProcessGroupDTO(
|
||||||
|
name: 'g2',
|
||||||
|
position: new PositionDTO(x: 105, y: -10),
|
||||||
|
contents: new FlowSnippetDTO(processors: [new ProcessorDTO(name: 'proc3', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))])
|
||||||
|
'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 't1' | 'AAAA' | '0.7' | new FlowSnippetDTO(
|
||||||
|
processors: [new ProcessorDTO(name: 'proc2', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))],
|
||||||
|
processGroups: [
|
||||||
|
new ProcessGroupDTO(
|
||||||
|
name: 'g2',
|
||||||
|
position: new PositionDTO(x: 105, y: -10),
|
||||||
|
contents: new FlowSnippetDTO(processors: [new ProcessorDTO(name: 'proc3', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))])
|
||||||
|
'g1' | 10.0 | -10.0 | 5.0 | 5.0 | 't1' | 'AAAA' | '1.0' | new FlowSnippetDTO(
|
||||||
|
processors: [new ProcessorDTO(name: 'proc2', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 10, y: 10))],
|
||||||
|
processGroups: [
|
||||||
|
new ProcessGroupDTO(
|
||||||
|
name: 'g2',
|
||||||
|
position: new PositionDTO(x: 105, y: -10),
|
||||||
|
contents: new FlowSnippetDTO(processors: [new ProcessorDTO(name: 'proc3', config: new ProcessorConfigDTO(), position: new PositionDTO(x: 50, y: 60))]))])
|
||||||
|
}
|
||||||
|
|
||||||
|
def PositionDTO calculateMoveAndScalePosition(position, oldOriginX, oldOriginY, newOriginX, newOriginY, factorX, factorY) {
|
||||||
|
new PositionDTO(
|
||||||
|
x: newOriginX + (position.x - oldOriginX) * factorX,
|
||||||
|
y: newOriginY + (position.y - oldOriginY) * factorY)
|
||||||
|
}
|
||||||
|
|
||||||
|
def PositionDTO calculateScalePosition(position, factorX, factorY) {
|
||||||
|
new PositionDTO(
|
||||||
|
x: position.x * factorX,
|
||||||
|
y: position.y * factorY)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue