NIFI-1800:

- UI style updates to make the components stand out better.
- Reusing controller service table in different contexts (controller, process group, etc).
- This closes #469
This commit is contained in:
Matt Gilman 2016-05-26 10:20:52 -04:00
parent ae3012a992
commit 9152a9fdbb
35 changed files with 2050 additions and 1477 deletions

View File

@ -17,10 +17,11 @@
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.TimeAdapter;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.apache.nifi.web.api.dto.util.TimeAdapter; import java.util.Date;
/** /**
* Details for the controller configuration. * Details for the controller configuration.
@ -28,13 +29,10 @@ import org.apache.nifi.web.api.dto.util.TimeAdapter;
@XmlType(name = "config") @XmlType(name = "config")
public class ControllerConfigurationDTO { public class ControllerConfigurationDTO {
private String name;
private String comments;
private Integer maxTimerDrivenThreadCount; private Integer maxTimerDrivenThreadCount;
private Integer maxEventDrivenThreadCount; private Integer maxEventDrivenThreadCount;
private Long autoRefreshIntervalSeconds; private Long autoRefreshIntervalSeconds;
private Boolean siteToSiteSecure;
private Date currentTime; private Date currentTime;
private Integer timeOffset; private Integer timeOffset;
@ -67,34 +65,6 @@ public class ControllerConfigurationDTO {
this.maxEventDrivenThreadCount = maxEventDrivenThreadCount; this.maxEventDrivenThreadCount = maxEventDrivenThreadCount;
} }
/**
* @return name of this NiFi
*/
@ApiModelProperty(
value = "The name of this NiFi."
)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return comments for this NiFi
*/
@ApiModelProperty(
value = "The comments for this NiFi."
)
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
/** /**
* @return interval in seconds between the automatic NiFi refresh requests. This value is read only * @return interval in seconds between the automatic NiFi refresh requests. This value is read only
*/ */
@ -110,21 +80,6 @@ public class ControllerConfigurationDTO {
this.autoRefreshIntervalSeconds = autoRefreshIntervalSeconds; this.autoRefreshIntervalSeconds = autoRefreshIntervalSeconds;
} }
/**
* @return Indicates whether or not Site-to-Site communications with this instance is secure (2-way authentication). This value is read only
*/
@ApiModelProperty(
value = "Indicates whether site to site communication with the NiFi is secure (requires 2-way authenticiation).",
readOnly = true
)
public Boolean isSiteToSiteSecure() {
return siteToSiteSecure;
}
public void setSiteToSiteSecure(Boolean siteToSiteSecure) {
this.siteToSiteSecure = siteToSiteSecure;
}
/** /**
* @return current time on the server * @return current time on the server
*/ */

View File

@ -17,6 +17,7 @@
package org.apache.nifi.web.api.entity; package org.apache.nifi.web.api.entity;
import com.wordnik.swagger.annotations.ApiModelProperty; import com.wordnik.swagger.annotations.ApiModelProperty;
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
import org.apache.nifi.web.api.dto.ControllerConfigurationDTO; import org.apache.nifi.web.api.dto.ControllerConfigurationDTO;
import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.RevisionDTO;
@ -26,10 +27,11 @@ import javax.xml.bind.annotation.XmlRootElement;
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a ControllerConfigurationDTO. * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a ControllerConfigurationDTO.
*/ */
@XmlRootElement(name = "controllerConfigurationEntity") @XmlRootElement(name = "controllerConfigurationEntity")
public class ControllerConfigurationEntity extends ComponentEntity { public class ControllerConfigurationEntity extends Entity {
private ControllerConfigurationDTO config; private ControllerConfigurationDTO config;
private RevisionDTO revision; private RevisionDTO revision;
private AccessPolicyDTO accessPolicy;
/** /**
* @return revision for this request/response * @return revision for this request/response
@ -65,4 +67,19 @@ public class ControllerConfigurationEntity extends ComponentEntity {
this.config = config; this.config = config;
} }
/**
* The access policy for this component.
*
* @return The access policy
*/
@ApiModelProperty(
value = "The access policy for the controller."
)
public AccessPolicyDTO getAccessPolicy() {
return accessPolicy;
}
public void setAccessPolicy(AccessPolicyDTO accessPolicy) {
this.accessPolicy = accessPolicy;
}
} }

View File

@ -16,9 +16,6 @@
*/ */
package org.apache.nifi.audit; package org.apache.nifi.audit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import org.apache.nifi.action.Action; import org.apache.nifi.action.Action;
import org.apache.nifi.action.Component; import org.apache.nifi.action.Component;
import org.apache.nifi.action.FlowChangeAction; import org.apache.nifi.action.FlowChangeAction;
@ -33,6 +30,10 @@ import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
/** /**
* Audits configuration changes to the controller. * Audits configuration changes to the controller.
*/ */
@ -41,112 +42,6 @@ public class ControllerAuditor extends NiFiAuditor {
private static final Logger logger = LoggerFactory.getLogger(ControllerAuditor.class); private static final Logger logger = LoggerFactory.getLogger(ControllerAuditor.class);
/**
* Audits updating the name of the controller.
*
* @param proceedingJoinPoint join point
* @param name name
* @param controllerFacade facade
* @throws java.lang.Throwable ex
*/
@Around("within(org.apache.nifi.web.controller.ControllerFacade) && "
+ "execution(void setName(java.lang.String)) && "
+ "args(name) && "
+ "target(controllerFacade)")
public void updateControllerNameAdvice(ProceedingJoinPoint proceedingJoinPoint, String name, ControllerFacade controllerFacade) throws Throwable {
// get the previous name
String previousName = controllerFacade.getName();
// update the configuraion
proceedingJoinPoint.proceed();
// if no exception were thrown, add the configuration action...
// ensure the name changed
if (!name.equals(previousName)) {
// get the current user
NiFiUser user = NiFiUserUtils.getNiFiUser();
// ensure the user was found
if (user != null) {
Collection<Action> actions = new ArrayList<>();
// create the configuration details
FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails();
configDetails.setName("Controller Name");
configDetails.setValue(name);
configDetails.setPreviousValue(previousName);
// create the config action
FlowChangeAction configAction = new FlowChangeAction();
configAction.setUserIdentity(user.getIdentity());
configAction.setUserName(user.getUserName());
configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName());
configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails);
actions.add(configAction);
// record the action
saveActions(actions, logger);
}
}
}
/**
* Audits updating the comments of the controller.
*
* @param proceedingJoinPoint join point
* @param comments comments
* @param controllerFacade facade
* @throws java.lang.Throwable ex
*/
@Around("within(org.apache.nifi.web.controller.ControllerFacade) && "
+ "execution(void setComments(java.lang.String)) && "
+ "args(comments) && "
+ "target(controllerFacade)")
public void updateControllerCommentsAdvice(ProceedingJoinPoint proceedingJoinPoint, String comments, ControllerFacade controllerFacade) throws Throwable {
// get the previous name
String previousComments = controllerFacade.getComments();
// update the configuraion
proceedingJoinPoint.proceed();
// if no exception were thrown, add the configuration action...
// ensure the name changed
if (!comments.equals(previousComments)) {
// get the current user
NiFiUser user = NiFiUserUtils.getNiFiUser();
// ensure the user was found
if (user != null) {
Collection<Action> actions = new ArrayList<>();
// create the configuration details
FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails();
configDetails.setName("Controller Comments");
configDetails.setValue(comments);
configDetails.setPreviousValue(previousComments);
// create the config action
FlowChangeAction configAction = new FlowChangeAction();
configAction.setUserIdentity(user.getIdentity());
configAction.setUserName(user.getUserName());
configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName());
configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails);
actions.add(configAction);
// record the action
saveActions(actions, logger);
}
}
}
/** /**
* Audits updating the max number of timer driven threads for the controller. * Audits updating the max number of timer driven threads for the controller.
* *
@ -189,7 +84,7 @@ public class ControllerAuditor extends NiFiAuditor {
configAction.setOperation(Operation.Configure); configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date()); configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller"); configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName()); configAction.setSourceName("Flow Controller");
configAction.setSourceType(Component.Controller); configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails); configAction.setActionDetails(configDetails);
actions.add(configAction); actions.add(configAction);
@ -242,7 +137,7 @@ public class ControllerAuditor extends NiFiAuditor {
configAction.setOperation(Operation.Configure); configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date()); configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller"); configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName()); configAction.setSourceName("Flow Controller");
configAction.setSourceType(Component.Controller); configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails); configAction.setActionDetails(configDetails);
actions.add(configAction); actions.add(configAction);

View File

@ -66,6 +66,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity;
import org.apache.nifi.web.api.entity.FlowEntity; import org.apache.nifi.web.api.entity.FlowEntity;
@ -262,7 +263,7 @@ public interface NiFiServiceFacade {
* *
* @return Controller configuration transfer object * @return Controller configuration transfer object
*/ */
ControllerConfigurationDTO getControllerConfiguration(); ControllerConfigurationEntity getControllerConfiguration();
/** /**
* Updates the configuration for this controller. * Updates the configuration for this controller.
@ -271,7 +272,7 @@ public interface NiFiServiceFacade {
* @param controllerConfigurationDTO Controller configuration DTO * @param controllerConfigurationDTO Controller configuration DTO
* @return Controller configuration DTO * @return Controller configuration DTO
*/ */
ConfigurationSnapshot<ControllerConfigurationDTO> updateControllerConfiguration(Revision revision, ControllerConfigurationDTO controllerConfigurationDTO); ControllerConfigurationEntity updateControllerConfiguration(Revision revision, ControllerConfigurationDTO controllerConfigurationDTO);
/** /**
* Creates a new archive of the flow configuration. * Creates a new archive of the flow configuration.

View File

@ -45,6 +45,7 @@ import org.apache.nifi.connectable.Funnel;
import org.apache.nifi.connectable.Port; import org.apache.nifi.connectable.Port;
import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.ConfiguredComponent;
import org.apache.nifi.controller.Counter; import org.apache.nifi.controller.Counter;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ProcessorNode;
import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.ReportingTaskNode;
import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.ScheduledState;
@ -67,7 +68,6 @@ import org.apache.nifi.remote.RootGroupPort;
import org.apache.nifi.reporting.Bulletin; import org.apache.nifi.reporting.Bulletin;
import org.apache.nifi.reporting.BulletinQuery; import org.apache.nifi.reporting.BulletinQuery;
import org.apache.nifi.reporting.BulletinRepository; import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.AccessPolicyDTO;
import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO;
@ -127,6 +127,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity;
@ -183,9 +184,7 @@ import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -886,15 +885,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
} }
@Override @Override
public ConfigurationSnapshot<ControllerConfigurationDTO> updateControllerConfiguration(final Revision revision, final ControllerConfigurationDTO controllerConfigurationDTO) { public ControllerConfigurationEntity updateControllerConfiguration(final Revision revision, final ControllerConfigurationDTO controllerConfigurationDTO) {
final Supplier<ControllerConfigurationDTO> daoUpdate = () -> { final RevisionUpdate<ControllerConfigurationDTO> updatedComponent = updateComponent(
// update the controller configuration through the proxy revision,
if (controllerConfigurationDTO.getName() != null) { controllerFacade,
controllerFacade.setName(controllerConfigurationDTO.getName()); () -> {
}
if (controllerConfigurationDTO.getComments() != null) {
controllerFacade.setComments(controllerConfigurationDTO.getComments());
}
if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) { if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) {
controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount()); controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount());
} }
@ -903,15 +898,12 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
} }
return controllerConfigurationDTO; return controllerConfigurationDTO;
}; },
controller -> dtoFactory.createControllerConfigurationDto(controllerFacade, properties.getAutoRefreshInterval()));
final RevisionUpdate<ControllerConfigurationDTO> updatedComponent = updateComponent( final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade);
revision, final RevisionDTO updateRevision = dtoFactory.createRevisionDTO(updatedComponent.getLastModification());
controllerFacade, return entityFactory.createControllerConfigurationEntity(updatedComponent.getComponent(), updateRevision, accessPolicy);
daoUpdate,
controller -> getControllerConfiguration());
return new ConfigurationSnapshot<>(updatedComponent.getLastModification().getRevision().getVersion());
} }
@Override @Override
@ -1266,9 +1258,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
* *
* @return a RevisionUpdate that represents the updated configuration * @return a RevisionUpdate that represents the updated configuration
*/ */
private <D, C> RevisionUpdate<D> createComponent(final ComponentDTO componentDto, private <D, C> RevisionUpdate<D> createComponent(final ComponentDTO componentDto, final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
final String modifier = NiFiUserUtils.getNiFiUserName(); final String modifier = NiFiUserUtils.getNiFiUserName();
// ensure id is set // ensure id is set
@ -1638,6 +1628,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override @Override
public ControllerServiceEntity createControllerService(final String groupId, final ControllerServiceDTO controllerServiceDTO) { public ControllerServiceEntity createControllerService(final String groupId, final ControllerServiceDTO controllerServiceDTO) {
final String normalizedGroupId = groupId == null ? controllerFacade.getRootGroupId() : groupId;
controllerServiceDTO.setParentGroupId(normalizedGroupId);
final RevisionUpdate<ControllerServiceDTO> snapshot = createComponent( final RevisionUpdate<ControllerServiceDTO> snapshot = createComponent(
controllerServiceDTO, controllerServiceDTO,
() -> { () -> {
@ -1645,7 +1638,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerServiceNode controllerService = controllerServiceDAO.createControllerService(controllerServiceDTO); final ControllerServiceNode controllerService = controllerServiceDAO.createControllerService(controllerServiceDTO);
// TODO - this logic should be part of the controllerServiceDAO // TODO - this logic should be part of the controllerServiceDAO
final ProcessGroup group = processGroupDAO.getProcessGroup(groupId); final ProcessGroup group = processGroupDAO.getProcessGroup(normalizedGroupId);
group.addControllerService(controllerService); group.addControllerService(controllerService);
return controllerService; return controllerService;
}, },
@ -2350,8 +2343,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerDTO controllerDTO = new ControllerDTO(); final ControllerDTO controllerDTO = new ControllerDTO();
controllerDTO.setId(controllerFacade.getRootGroupId()); controllerDTO.setId(controllerFacade.getRootGroupId());
controllerDTO.setInstanceId(controllerFacade.getInstanceId()); controllerDTO.setInstanceId(controllerFacade.getInstanceId());
controllerDTO.setName(controllerFacade.getName());
controllerDTO.setComments(controllerFacade.getComments());
controllerDTO.setInputPorts(inputPortDtos); controllerDTO.setInputPorts(inputPortDtos);
controllerDTO.setOutputPorts(outputPortDtos); controllerDTO.setOutputPorts(outputPortDtos);
controllerDTO.setInputPortCount(inputPorts.size()); controllerDTO.setInputPortCount(inputPorts.size());
@ -2374,29 +2365,13 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
} }
@Override @Override
public ControllerConfigurationDTO getControllerConfiguration() { public ControllerConfigurationEntity getControllerConfiguration() {
ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO(); return revisionManager.get(FlowController.class.getSimpleName(), rev -> {
controllerConfig.setName(controllerFacade.getName()); final ControllerConfigurationDTO dto = dtoFactory.createControllerConfigurationDto(controllerFacade, properties.getAutoRefreshInterval());
controllerConfig.setComments(controllerFacade.getComments()); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade);
controllerConfig.setMaxTimerDrivenThreadCount(controllerFacade.getMaxTimerDrivenThreadCount()); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
controllerConfig.setMaxEventDrivenThreadCount(controllerFacade.getMaxEventDrivenThreadCount()); return entityFactory.createControllerConfigurationEntity(dto, revision, accessPolicy);
});
// get the refresh interval
final long refreshInterval = FormatUtils.getTimeDuration(properties.getAutoRefreshInterval(), TimeUnit.SECONDS);
controllerConfig.setAutoRefreshIntervalSeconds(refreshInterval);
final Date now = new Date();
controllerConfig.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime()));
controllerConfig.setCurrentTime(now);
// determine the site to site configuration
if (isClustered()) {
controllerConfig.setSiteToSiteSecure(controllerFacade.isClusterManagerRemoteSiteCommsSecure());
} else {
controllerConfig.setSiteToSiteSecure(controllerFacade.isRemoteSiteCommsSecure());
}
return controllerConfig;
} }
@Override @Override
@ -2656,9 +2631,18 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override @Override
public Set<ControllerServiceEntity> getControllerServices(String groupId) { public Set<ControllerServiceEntity> getControllerServices(String groupId) {
// TODO - move this logic into the ControllerServiceDAO // TODO - move this logic into the ControllerServiceDAO
final Set<ControllerServiceNode> serviceNodes;
final Set<String> serviceIds;
if (groupId == null) {
// TODO - controller services scoped by the controller
serviceNodes = controllerServiceDAO.getControllerServices();
serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet());
} else {
final ProcessGroup group = processGroupDAO.getProcessGroup(groupId); final ProcessGroup group = processGroupDAO.getProcessGroup(groupId);
final Set<ControllerServiceNode> serviceNodes = group.getControllerServices(true); serviceNodes = group.getControllerServices(true);
final Set<String> serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet()); serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet());
}
return revisionManager.get(serviceIds, () -> { return revisionManager.get(serviceIds, () -> {
return serviceNodes.stream() return serviceNodes.stream()

View File

@ -16,10 +16,43 @@
*/ */
package org.apache.nifi.web.api; package org.apache.nifi.web.api;
import java.net.URI; import com.sun.jersey.api.core.ResourceContext;
import java.util.Arrays; import com.wordnik.swagger.annotations.Api;
import java.util.HashSet; import com.wordnik.swagger.annotations.ApiOperation;
import java.util.Set; import com.wordnik.swagger.annotations.ApiParam;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AccessDeniedException;
import org.apache.nifi.authorization.AuthorizationRequest;
import org.apache.nifi.authorization.AuthorizationResult;
import org.apache.nifi.authorization.AuthorizationResult.Result;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.ResourceFactory;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.cluster.manager.exception.UnknownNodeException;
import org.apache.nifi.cluster.manager.impl.WebClusterManager;
import org.apache.nifi.cluster.node.Node;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.CounterDTO;
import org.apache.nifi.web.api.dto.CountersDTO;
import org.apache.nifi.web.api.entity.AuthorityEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity;
import org.apache.nifi.web.api.entity.CounterEntity;
import org.apache.nifi.web.api.entity.CountersEntity;
import org.apache.nifi.web.api.entity.Entity;
import org.apache.nifi.web.api.entity.ProcessGroupEntity;
import org.apache.nifi.web.api.entity.ReportingTaskEntity;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -36,41 +69,10 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.net.URI;
import org.apache.commons.lang3.StringUtils; import java.util.Arrays;
import org.apache.nifi.authorization.user.NiFiUser; import java.util.HashSet;
import org.apache.nifi.authorization.user.NiFiUserUtils; import java.util.Set;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.cluster.manager.exception.UnknownNodeException;
import org.apache.nifi.cluster.manager.impl.WebClusterManager;
import org.apache.nifi.cluster.node.Node;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.ConfigurationSnapshot;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.ControllerConfigurationDTO;
import org.apache.nifi.web.api.dto.CounterDTO;
import org.apache.nifi.web.api.dto.CountersDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.entity.AuthorityEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.CounterEntity;
import org.apache.nifi.web.api.entity.CountersEntity;
import org.apache.nifi.web.api.entity.Entity;
import org.apache.nifi.web.api.entity.ProcessGroupEntity;
import org.apache.nifi.web.api.entity.ReportingTaskEntity;
import org.apache.nifi.web.api.entity.ReportingTasksEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import com.sun.jersey.api.core.ResourceContext;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization;
/** /**
* RESTful endpoint for managing a Flow Controller. * RESTful endpoint for managing a Flow Controller.
@ -85,12 +87,35 @@ public class ControllerResource extends ApplicationResource {
private NiFiServiceFacade serviceFacade; private NiFiServiceFacade serviceFacade;
private WebClusterManager clusterManager; private WebClusterManager clusterManager;
private NiFiProperties properties; private NiFiProperties properties;
private Authorizer authorizer;
private ReportingTaskResource reportingTaskResource; private ReportingTaskResource reportingTaskResource;
private ControllerServiceResource controllerServiceResource;
@Context @Context
private ResourceContext resourceContext; private ResourceContext resourceContext;
/**
* Authorizes access to the flow.
*/
private void authorizeController(final RequestAction action) {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.resource(ResourceFactory.getControllerResource())
.identity(user.getIdentity())
.anonymous(user.isAnonymous())
.accessAttempt(true)
.action(action)
.build();
final AuthorizationResult result = authorizer.authorize(request);
if (!Result.Approved.equals(result.getResult())) {
final String message = StringUtils.isNotBlank(result.getExplanation()) ? result.getExplanation() : "Access is denied";
throw new AccessDeniedException(message);
}
}
/** /**
* Creates a new archive of this flow controller. Note, this is a POST operation that returns a URI that is not representative of the thing that was actually created. The archive that is created * Creates a new archive of this flow controller. Note, this is a POST operation that returns a URI that is not representative of the thing that was actually created. The archive that is created
* cannot be referenced at a later time, therefore there is no corresponding URI. Instead the request URI is returned. * cannot be referenced at a later time, therefore there is no corresponding URI. Instead the request URI is returned.
@ -311,19 +336,15 @@ public class ControllerResource extends ApplicationResource {
} }
) )
public Response getControllerConfig() { public Response getControllerConfig() {
// TODO
// authorizeController(RequestAction.READ);
// replicate if cluster manager // replicate if cluster manager
if (properties.isClusterManager()) { if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
} }
final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration(); final ControllerConfigurationEntity entity = serviceFacade.getControllerConfiguration();
// create the response entity
final ControllerConfigurationEntity entity = new ControllerConfigurationEntity();
entity.setConfig(controllerConfig);
// generate the response
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
@ -374,32 +395,20 @@ public class ControllerResource extends ApplicationResource {
return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), configEntity, getHeaders()).getResponse(); return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), configEntity, getHeaders()).getResponse();
} }
final RevisionDTO revisionDto = configEntity.getRevision(); final Revision revision = getRevision(configEntity.getRevision(), FlowController.class.getSimpleName());
final Revision revision = new Revision(revisionDto.getVersion(), revisionDto.getClientId(), "controller"); return withWriteLock(
serviceFacade,
// handle expects request (usually from the cluster manager) revision,
final String expects = httpServletRequest.getHeader(RequestReplicator.REQUEST_VALIDATION_HTTP_HEADER); lookup -> {
if (expects != null) { authorizeController(RequestAction.WRITE);
return generateContinueResponse().build(); },
} null,
() -> {
final ConfigurationSnapshot<ControllerConfigurationDTO> controllerResponse final ControllerConfigurationEntity entity = serviceFacade.updateControllerConfiguration(revision, configEntity.getConfig());
= serviceFacade.updateControllerConfiguration(revision, configEntity.getConfig());
final ControllerConfigurationDTO controllerConfig = controllerResponse.getConfiguration();
// get the updated revision
final RevisionDTO updatedRevision = new RevisionDTO();
updatedRevision.setClientId(revision.getClientId());
updatedRevision.setVersion(controllerResponse.getVersion());
// create the response entity
final ControllerConfigurationEntity entity = new ControllerConfigurationEntity();
entity.setRevision(updatedRevision);
entity.setConfig(controllerConfig);
// generate the response
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
);
}
/**x /**x
* Retrieves the user details, including the authorities, about the user making the request. * Retrieves the user details, including the authorities, about the user making the request.
@ -519,26 +528,27 @@ public class ControllerResource extends ApplicationResource {
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build(); return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
} }
// -------------------
// controller services
// -------------------
/** /**
* Retrieves all the of reporting tasks in this NiFi. * Creates a new Controller Service.
* *
* @param clientId Optional client id. If the client id is not specified, a * @param httpServletRequest request
* new one will be generated. This value (whether specified or generated) is * @param controllerServiceEntity A controllerServiceEntity.
* included in the response. * @return A controllerServiceEntity.
* @return A reportingTasksEntity.
*/ */
@GET @POST
@Consumes(MediaType.WILDCARD) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("reporting-tasks") @Path("controller-services")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") // TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation( @ApiOperation(
value = "Gets all reporting tasks", value = "Creates a new controller service",
response = ReportingTasksEntity.class, response = ControllerServiceEntity.class,
authorizations = { authorizations = {
@Authorization(value = "Read Only", type = "ROLE_MONITOR"), @Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
@Authorization(value = "Administrator", type = "ROLE_ADMIN")
} }
) )
@ApiResponses( @ApiResponses(
@ -549,28 +559,50 @@ 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.") @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 getReportingTasks( public Response createControllerService(
@Context final HttpServletRequest httpServletRequest,
@ApiParam( @ApiParam(
value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.", value = "The controller service configuration details.",
required = false required = true
) ) final ControllerServiceEntity controllerServiceEntity) {
@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
// replicate if cluster manager if (controllerServiceEntity == null || controllerServiceEntity.getComponent() == null) {
if (properties.isClusterManager()) { throw new IllegalArgumentException("Controller service details must be specified.");
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
} }
// get all the reporting tasks if (controllerServiceEntity.getComponent().getId() != null) {
final Set<ReportingTaskEntity> reportingTasks = serviceFacade.getReportingTasks(); throw new IllegalArgumentException("Controller service ID cannot be specified.");
reportingTaskResource.populateRemainingReportingTaskEntitiesContent(reportingTasks); }
// create the response entity if (StringUtils.isBlank(controllerServiceEntity.getComponent().getType())) {
final ReportingTasksEntity entity = new ReportingTasksEntity(); throw new IllegalArgumentException("The type of controller service to create must be specified.");
entity.setReportingTasks(reportingTasks); }
// generate the response if (properties.isClusterManager()) {
return clusterContext(generateOkResponse(entity)).build(); return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), controllerServiceEntity, getHeaders()).getResponse();
}
// handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
// TODO - authorize controller access
});
}
if (validationPhase) {
return generateContinueResponse().build();
}
// set the processor id as appropriate
controllerServiceEntity.getComponent().setId(generateUuid());
// create the controller service and generate the json
final ControllerServiceEntity entity = serviceFacade.createControllerService(null, controllerServiceEntity.getComponent());
controllerServiceResource.populateRemainingControllerServiceContent(entity.getComponent());
// build the response
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
} }
// setters // setters
@ -586,8 +618,15 @@ public class ControllerResource extends ApplicationResource {
this.reportingTaskResource = reportingTaskResource; this.reportingTaskResource = reportingTaskResource;
} }
public void setControllerServiceResource(ControllerServiceResource controllerServiceResource) {
this.controllerServiceResource = controllerServiceResource;
}
public void setProperties(NiFiProperties properties) { public void setProperties(NiFiProperties properties) {
this.properties = properties; this.properties = properties;
} }
public void setAuthorizer(Authorizer authorizer) {
this.authorizer = authorizer;
}
} }

View File

@ -48,7 +48,6 @@ import org.apache.nifi.web.api.dto.AboutDTO;
import org.apache.nifi.web.api.dto.BannerDTO; import org.apache.nifi.web.api.dto.BannerDTO;
import org.apache.nifi.web.api.dto.BulletinBoardDTO; import org.apache.nifi.web.api.dto.BulletinBoardDTO;
import org.apache.nifi.web.api.dto.BulletinQueryDTO; import org.apache.nifi.web.api.dto.BulletinQueryDTO;
import org.apache.nifi.web.api.dto.ControllerConfigurationDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.flow.FlowDTO; import org.apache.nifi.web.api.dto.flow.FlowDTO;
@ -81,7 +80,9 @@ import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
import org.apache.nifi.web.api.entity.ProcessorStatusEntity; import org.apache.nifi.web.api.entity.ProcessorStatusEntity;
import org.apache.nifi.web.api.entity.ProcessorTypesEntity; import org.apache.nifi.web.api.entity.ProcessorTypesEntity;
import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusEntity;
import org.apache.nifi.web.api.entity.ReportingTaskEntity;
import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity; import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
import org.apache.nifi.web.api.entity.ReportingTasksEntity;
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
import org.apache.nifi.web.api.entity.SearchResultsEntity; import org.apache.nifi.web.api.entity.SearchResultsEntity;
import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.entity.StatusHistoryEntity;
@ -141,6 +142,7 @@ public class FlowResource extends ApplicationResource {
private TemplateResource templateResource; private TemplateResource templateResource;
private ProcessGroupResource processGroupResource; private ProcessGroupResource processGroupResource;
private ControllerServiceResource controllerServiceResource; private ControllerServiceResource controllerServiceResource;
private ReportingTaskResource reportingTaskResource;
/** /**
* Populates the remaining fields in the specified process group. * Populates the remaining fields in the specified process group.
@ -186,6 +188,9 @@ public class FlowResource extends ApplicationResource {
return flowStructure; return flowStructure;
} }
/**
* Authorizes access to the flow.
*/
private void authorizeFlow() { private void authorizeFlow() {
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();
@ -269,6 +274,56 @@ public class FlowResource extends ApplicationResource {
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
// -------------------
// controller services
// -------------------
/**
* Retrieves all the of controller services in this NiFi.
*
* @return A controllerServicesEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("controller/controller-services")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets all controller services",
response = ControllerServicesEntity.class,
authorizations = {
@Authorization(value = "Read Only", type = "ROLE_MONITOR"),
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
@Authorization(value = "Administrator", type = "ROLE_ADMIN")
}
)
@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 = 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 getControllerServicesFromController() {
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
}
// get all the controller services
final Set<ControllerServiceEntity> controllerServices = serviceFacade.getControllerServices(null);
controllerServiceResource.populateRemainingControllerServiceEntitiesContent(controllerServices);
// create the response entity
final ControllerServicesEntity entity = new ControllerServicesEntity();
entity.setControllerServices(controllerServices);
// generate the response
return clusterContext(generateOkResponse(entity)).build();
}
/** /**
* Retrieves all the of controller services in this NiFi. * Retrieves all the of controller services in this NiFi.
* *
@ -296,7 +351,7 @@ public class FlowResource 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.") @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 getControllerServices( public Response getControllerServicesFromGroup(
@ApiParam( @ApiParam(
value = "The process group id.", value = "The process group id.",
required = true required = true
@ -320,6 +375,64 @@ public class FlowResource extends ApplicationResource {
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
// ---------------
// reporting-tasks
// ---------------
/**
* Retrieves all the of reporting tasks in this NiFi.
*
* @param clientId Optional client id. If the client id is not specified, a
* new one will be generated. This value (whether specified or generated) is
* included in the response.
* @return A reportingTasksEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("reporting-tasks")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets all reporting tasks",
response = ReportingTasksEntity.class,
authorizations = {
@Authorization(value = "Read Only", type = "ROLE_MONITOR"),
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
@Authorization(value = "Administrator", type = "ROLE_ADMIN")
}
)
@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 = 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 getReportingTasks(
@ApiParam(
value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
required = false
)
@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
}
// get all the reporting tasks
final Set<ReportingTaskEntity> reportingTasks = serviceFacade.getReportingTasks();
reportingTaskResource.populateRemainingReportingTaskEntitiesContent(reportingTasks);
// create the response entity
final ReportingTasksEntity entity = new ReportingTasksEntity();
entity.setReportingTasks(reportingTasks);
// generate the response
return clusterContext(generateOkResponse(entity)).build();
}
/** /**
* Updates the specified process group. * Updates the specified process group.
* *
@ -358,8 +471,6 @@ public class FlowResource extends ApplicationResource {
@PathParam("id") String id, @PathParam("id") String id,
ScheduleComponentsEntity scheduleComponentsEntity) { ScheduleComponentsEntity scheduleComponentsEntity) {
authorizeFlow();
// ensure the same id is being used // ensure the same id is being used
if (!id.equals(scheduleComponentsEntity.getId())) { if (!id.equals(scheduleComponentsEntity.getId())) {
throw new IllegalArgumentException(String.format("The process group id (%s) in the request body does " throw new IllegalArgumentException(String.format("The process group id (%s) in the request body does "
@ -441,6 +552,9 @@ public class FlowResource extends ApplicationResource {
serviceFacade, serviceFacade,
revisions, revisions,
lookup -> { lookup -> {
// ensure access to the flow
authorizeFlow();
// ensure access to every component being scheduled // ensure access to every component being scheduled
componentsToSchedule.keySet().forEach(componentId -> { componentsToSchedule.keySet().forEach(componentId -> {
final Authorizable connectable = lookup.getConnectable(componentId); final Authorizable connectable = lookup.getConnectable(componentId);
@ -875,11 +989,9 @@ public class FlowResource extends ApplicationResource {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
} }
final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration();
// create the about dto // create the about dto
final AboutDTO aboutDTO = new AboutDTO(); final AboutDTO aboutDTO = new AboutDTO();
aboutDTO.setTitle(controllerConfig.getName()); aboutDTO.setTitle("NiFi"); // TODO - where to load title from
aboutDTO.setVersion(properties.getUiTitle()); aboutDTO.setVersion(properties.getUiTitle());
aboutDTO.setUri(generateResourceUri()); aboutDTO.setUri(generateResourceUri());
@ -1853,6 +1965,10 @@ public class FlowResource extends ApplicationResource {
this.controllerServiceResource = controllerServiceResource; this.controllerServiceResource = controllerServiceResource;
} }
public void setReportingTaskResource(ReportingTaskResource reportingTaskResource) {
this.reportingTaskResource = reportingTaskResource;
}
public void setAuthorizer(Authorizer authorizer) { public void setAuthorizer(Authorizer authorizer) {
this.authorizer = authorizer; this.authorizer = authorizer;
} }

View File

@ -134,6 +134,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusSnapshotDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusSnapshotDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusSnapshotDTO;
import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity; import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity;
import org.apache.nifi.web.controller.ControllerFacade;
import org.apache.nifi.web.revision.RevisionManager; import org.apache.nifi.web.revision.RevisionManager;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
@ -154,6 +155,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -174,6 +176,22 @@ public final class DtoFactory {
private EntityFactory entityFactory; private EntityFactory entityFactory;
private Authorizer authorizer; private Authorizer authorizer;
public ControllerConfigurationDTO createControllerConfigurationDto(final ControllerFacade controllerFacade, final String autoRefreshInterval) {
final ControllerConfigurationDTO dto = new ControllerConfigurationDTO();
dto.setMaxTimerDrivenThreadCount(controllerFacade.getMaxTimerDrivenThreadCount());
dto.setMaxEventDrivenThreadCount(controllerFacade.getMaxEventDrivenThreadCount());
// get the refresh interval
final long refreshInterval = FormatUtils.getTimeDuration(autoRefreshInterval, TimeUnit.SECONDS);
dto.setAutoRefreshIntervalSeconds(refreshInterval);
final Date now = new Date();
dto.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime()));
dto.setCurrentTime(now);
return dto;
}
/** /**
* Creates an ActionDTO for the specified Action. * Creates an ActionDTO for the specified Action.
* *
@ -1143,6 +1161,7 @@ public final class DtoFactory {
public ControllerServiceDTO createControllerServiceDto(final ControllerServiceNode controllerServiceNode) { public ControllerServiceDTO createControllerServiceDto(final ControllerServiceNode controllerServiceNode) {
final ControllerServiceDTO dto = new ControllerServiceDTO(); final ControllerServiceDTO dto = new ControllerServiceDTO();
dto.setId(controllerServiceNode.getIdentifier()); dto.setId(controllerServiceNode.getIdentifier());
dto.setParentGroupId(controllerServiceNode.getProcessGroup() == null ? null : controllerServiceNode.getProcessGroup().getIdentifier());
dto.setName(controllerServiceNode.getName()); dto.setName(controllerServiceNode.getName());
dto.setType(controllerServiceNode.getControllerServiceImplementation().getClass().getName()); dto.setType(controllerServiceNode.getControllerServiceImplementation().getClass().getName());
dto.setState(controllerServiceNode.getState().name()); dto.setState(controllerServiceNode.getState().name());

View File

@ -24,6 +24,7 @@ import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO; import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentEntity;
import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity; import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity;
@ -40,6 +41,19 @@ import org.apache.nifi.web.api.entity.SnippetEntity;
public final class EntityFactory { public final class EntityFactory {
public ControllerConfigurationEntity createControllerConfigurationEntity(final ControllerConfigurationDTO dto, final RevisionDTO revision, final AccessPolicyDTO accessPolicy) {
final ControllerConfigurationEntity entity = new ControllerConfigurationEntity();
entity.setRevision(revision);
if (dto != null) {
entity.setAccessPolicy(accessPolicy);
// TODO - remove this once contents of ControllerConfigurationEntity is updated
// if (accessPolicy != null && accessPolicy.getCanRead()) {
entity.setConfig(dto);
// }
}
return entity;
}
public ProcessGroupFlowEntity createProcessGroupFlowEntity(final ProcessGroupFlowDTO dto, final AccessPolicyDTO accessPolicy) { public ProcessGroupFlowEntity createProcessGroupFlowEntity(final ProcessGroupFlowDTO dto, final AccessPolicyDTO accessPolicy) {
final ProcessGroupFlowEntity entity = new ProcessGroupFlowEntity(); final ProcessGroupFlowEntity entity = new ProcessGroupFlowEntity();
entity.setProcessGroupFlow(dto); entity.setProcessGroupFlow(dto);

View File

@ -177,6 +177,7 @@
<property name="connectionResource" ref="connectionResource"/> <property name="connectionResource" ref="connectionResource"/>
<property name="templateResource" ref="templateResource"/> <property name="templateResource" ref="templateResource"/>
<property name="controllerServiceResource" ref="controllerServiceResource"/> <property name="controllerServiceResource" ref="controllerServiceResource"/>
<property name="reportingTaskResource" ref="reportingTaskResource"/>
<property name="processGroupResource" ref="processGroupResource"/> <property name="processGroupResource" ref="processGroupResource"/>
</bean> </bean>
<bean id="resourceResource" class="org.apache.nifi.web.api.ResourceResource" scope="singleton"> <bean id="resourceResource" class="org.apache.nifi.web.api.ResourceResource" scope="singleton">
@ -190,6 +191,8 @@
<property name="properties" ref="nifiProperties"/> <property name="properties" ref="nifiProperties"/>
<property name="clusterManager" ref="clusterManager"/> <property name="clusterManager" ref="clusterManager"/>
<property name="reportingTaskResource" ref="reportingTaskResource"/> <property name="reportingTaskResource" ref="reportingTaskResource"/>
<property name="controllerServiceResource" ref="controllerServiceResource"/>
<property name="authorizer" ref="authorizer"/>
</bean> </bean>
<bean id="siteToSiteResource" class="org.apache.nifi.web.api.SiteToSiteResource" scope="singleton"> <bean id="siteToSiteResource" class="org.apache.nifi.web.api.SiteToSiteResource" scope="singleton">
<property name="serviceFacade" ref="serviceFacade"/> <property name="serviceFacade" ref="serviceFacade"/>

View File

@ -184,7 +184,6 @@ public class AdminAccessControlTest {
ControllerConfigurationEntity entity = response.getEntity(ControllerConfigurationEntity.class); ControllerConfigurationEntity entity = response.getEntity(ControllerConfigurationEntity.class);
Assert.assertNotNull(entity); Assert.assertNotNull(entity);
Assert.assertNotNull(entity.getConfig()); Assert.assertNotNull(entity.getConfig());
Assert.assertEquals("NiFi Flow", entity.getConfig().getName());
Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue()); Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue());
Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue()); Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue());
Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue()); Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue());

View File

@ -207,7 +207,6 @@ public class DfmAccessControlTest {
// create the controller configuration // create the controller configuration
ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO(); ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO();
controllerConfig.setName("new name");
// create the revision // create the revision
final RevisionDTO revision = new RevisionDTO(); final RevisionDTO revision = new RevisionDTO();
@ -229,7 +228,6 @@ public class DfmAccessControlTest {
entity = response.getEntity(ControllerConfigurationEntity.class); entity = response.getEntity(ControllerConfigurationEntity.class);
Assert.assertNotNull(entity); Assert.assertNotNull(entity);
Assert.assertNotNull(entity.getConfig()); Assert.assertNotNull(entity.getConfig());
Assert.assertEquals("new name", entity.getConfig().getName());
Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue()); Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue());
Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue()); Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue());
Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue()); Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue());

View File

@ -180,7 +180,6 @@ public class ReadOnlyAccessControlTest {
ControllerConfigurationEntity entity = response.getEntity(ControllerConfigurationEntity.class); ControllerConfigurationEntity entity = response.getEntity(ControllerConfigurationEntity.class);
Assert.assertNotNull(entity); Assert.assertNotNull(entity);
Assert.assertNotNull(entity.getConfig()); Assert.assertNotNull(entity.getConfig());
Assert.assertEquals("NiFi Flow", entity.getConfig().getName());
Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue()); Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue());
Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue()); Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().intValue());
Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue()); Assert.assertEquals(30, entity.getConfig().getAutoRefreshIntervalSeconds().intValue());

View File

@ -339,6 +339,7 @@
<include>${staging.dir}/js/nf/canvas/nf-queue-listing.js</include> <include>${staging.dir}/js/nf/canvas/nf-queue-listing.js</include>
<include>${staging.dir}/js/nf/canvas/nf-component-state.js</include> <include>${staging.dir}/js/nf/canvas/nf-component-state.js</include>
<include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include> <include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include>
<include>${staging.dir}/js/nf/canvas/nf-controller-services.js</include>
<include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include> <include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include>
<include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include> <include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include>
<include>${staging.dir}/js/nf/nf-processor-details.js</include> <include>${staging.dir}/js/nf/nf-processor-details.js</include>

View File

@ -27,6 +27,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
<script type="text/javascript" src="js/nf/canvas/nf-component-state.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/canvas/nf-component-state.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-controller-services.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-reporting-task.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/canvas/nf-reporting-task.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-processor-configuration.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/canvas/nf-processor-configuration.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\ <script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\

View File

@ -83,6 +83,12 @@
<div class="graph-control-content hidden"> <div class="graph-control-content hidden">
<div id="operation-buttons"> <div id="operation-buttons">
<div> <div>
<div id="operate-configure" class="action-button" title="Configuration">
<button ng-click="appCtrl.nf.Actions['showConfiguration'](appCtrl.nf.CanvasUtils.getSelection());"
ng-disabled="false">
<i class="graph-control-action-icon fa fa-gear"></i></button>
</div>
<div class="button-spacer-large">&nbsp;</div>
<div id="operate-enable" class="action-button" title="Enable"> <div id="operate-enable" class="action-button" title="Enable">
<button ng-click="appCtrl.nf.Actions['enable'](appCtrl.nf.CanvasUtils.getSelection());" <button ng-click="appCtrl.nf.Actions['enable'](appCtrl.nf.CanvasUtils.getSelection());"
ng-disabled="!appCtrl.nf.CanvasUtils.canEnable(appCtrl.nf.CanvasUtils.getSelection());"> ng-disabled="!appCtrl.nf.CanvasUtils.canEnable(appCtrl.nf.CanvasUtils.getSelection());">
@ -112,7 +118,9 @@
ng-disabled="!(appCtrl.nf.Canvas.canWrite() && (appCtrl.nf.CanvasUtils.getSelection().empty() || appCtrl.nf.CanvasUtils.canRead(appCtrl.nf.CanvasUtils.getSelection())));"> ng-disabled="!(appCtrl.nf.Canvas.canWrite() && (appCtrl.nf.CanvasUtils.getSelection().empty() || appCtrl.nf.CanvasUtils.canRead(appCtrl.nf.CanvasUtils.getSelection())));">
<i class="graph-control-action-icon icon icon-template"></i></button> <i class="graph-control-action-icon icon icon-template"></i></button>
</div> </div>
<div class="button-spacer-large">&nbsp;</div> <div class="clear"></div>
</div>
<div style="margin-top: 5px;">
<div id="operate-copy" class="action-button" title="Copy"> <div id="operate-copy" class="action-button" title="Copy">
<button ng-click="appCtrl.nf.Actions['copy'](appCtrl.nf.CanvasUtils.getSelection());" <button ng-click="appCtrl.nf.Actions['copy'](appCtrl.nf.CanvasUtils.getSelection());"
ng-disabled="!appCtrl.nf.CanvasUtils.isCopyable(appCtrl.nf.CanvasUtils.getSelection()) || !appCtrl.nf.CanvasUtils.canRead(appCtrl.nf.CanvasUtils.getSelection());"> ng-disabled="!appCtrl.nf.CanvasUtils.isCopyable(appCtrl.nf.CanvasUtils.getSelection()) || !appCtrl.nf.CanvasUtils.canRead(appCtrl.nf.CanvasUtils.getSelection());">
@ -124,9 +132,7 @@
ng-disabled="!appCtrl.nf.Clipboard.isCopied()"> ng-disabled="!appCtrl.nf.Clipboard.isCopied()">
<i class="graph-control-action-icon fa fa-paste"></i></button> <i class="graph-control-action-icon fa fa-paste"></i></button>
</div> </div>
<div class="clear"></div> <div class="button-spacer-large">&nbsp;</div>
</div>
<div style="margin-top: 5px;">
<div id="operate-group" class="action-button" title="Group"> <div id="operate-group" class="action-button" title="Group">
<button ng-click="appCtrl.nf.Actions['group'](appCtrl.nf.CanvasUtils.getSelection());" <button ng-click="appCtrl.nf.Actions['group'](appCtrl.nf.CanvasUtils.getSelection());"
ng-disabled="!appCtrl.nf.CanvasUtils.isDisconnected(appCtrl.nf.CanvasUtils.getSelection()) || !appCtrl.nf.CanvasUtils.canModify(appCtrl.nf.CanvasUtils.getSelection());"> ng-disabled="!appCtrl.nf.CanvasUtils.isDisconnected(appCtrl.nf.CanvasUtils.getSelection()) || !appCtrl.nf.CanvasUtils.canModify(appCtrl.nf.CanvasUtils.getSelection());">

View File

@ -15,24 +15,49 @@
limitations under the License. limitations under the License.
--%> --%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="process-group-configuration" nf-draggable="{containment: 'parent', handle: '.dialog-header'}"> <div id="process-group-configuration">
<div class="dialog-content"> <div id="process-group-configuration-header-text" class="settings-header-text">Process Group Configuration</div>
<div class="settings-container">
<div class="settings-tabs-container">
<div id="process-group-configuration-tabs" class="settings-tabs"></div>
<div id="process-group-configuration-refresh-button" class="pointer settings-refresh-button" title="Refresh"></div>
<div class="settings-last-refreshed-container">
Last updated:&nbsp;<span id="process-group-configuration-last-refreshed"></span>
</div>
<div id="process-group-configuration-loading-container" class="loading-container"></div>
<div id="add-process-group-configuration-controller-service" class="add-icon-bg"></div>
<div class="clear"></div>
</div>
<div class="settings-tab-background"></div>
<div>
<div id="general-process-group-configuration-tab-content" class="configuration-tab">
<div id="general-process-group-configuration">
<div class="setting"> <div class="setting">
<div class="setting-name">Name</div> <div class="setting-name">Process group name</div>
<div class="setting-field"> <div class="editable setting-field">
<input type="text" id="process-group-name" name="process-group-name" class="process-group-field"/> <input type="text" id="process-group-name" class="setting-input"/>
</div>
<div class="read-only setting-field">
<span id="read-only-process-group-name"></span>
</div> </div>
</div> </div>
<div class="setting"> <div class="setting">
<div class="setting-name">Id</div> <div class="setting-name">Process group comments</div>
<div class="setting-field"> <div class="editable setting-field">
<span id="process-group-id"></span> <textarea id="process-group-comments" class="setting-input"></textarea>
</div>
<div class="read-only setting-field">
<span id="read-only-process-group-comments"></span>
</div> </div>
</div> </div>
<div class="setting"> <div class="editable settings-buttons">
<div class="setting-name">Comments</div> <div id="process-group-configuration-save" class="button">Apply</div>
<div class="setting-field"> <div class="clear"></div>
<textarea cols="30" rows="4" id="process-group-comments" name="process-group-comments" class="process-group-field"></textarea> </div>
</div>
</div>
<div id="process-group-controller-services-tab-content" class="configuration-tab">
<div id="process-group-controller-services-table" class="settings-table"></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -16,43 +16,22 @@
--%> --%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="settings"> <div id="settings">
<div id="settings-header-text">NiFi Settings</div> <div id="settings-header-text" class="settings-header-text">NiFi Settings</div>
<div id="settings-container"> <div class="settings-container">
<div id="settings-tabs-container"> <div class="settings-tabs-container">
<div id="settings-tabs"></div> <div id="settings-tabs" class="settings-tabs"></div>
<div id="settings-refresh-button" class="pointer" title="Refresh"></div> <div id="settings-refresh-button" class="pointer settings-refresh-button" title="Refresh"></div>
<div id="settings-last-refreshed-container"> <div class="settings-last-refreshed-container">
Last updated:&nbsp;<span id="settings-last-refreshed"></span> Last updated:&nbsp;<span id="settings-last-refreshed"></span>
</div> </div>
<div id="settings-refresh-required-icon" class="hidden"></div>
<div id="settings-loading-container" class="loading-container"></div> <div id="settings-loading-container" class="loading-container"></div>
<div id="new-service-or-task" class="add-icon-bg"></div> <div id="new-service-or-task" class="add-icon-bg"></div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<div id="settings-tab-background"></div> <div class="settings-tab-background"></div>
<div id="settings-tabs-content"> <div>
<div id="general-settings-tab-content" class="configuration-tab"> <div id="general-settings-tab-content" class="configuration-tab">
<div id="general-settings"> <div id="general-settings">
<div class="setting">
<div class="setting-name">Data flow name</div>
<div class="editable setting-field">
<input type="text" id="data-flow-title-field" name="data-flow-title" class="setting-input"/>
<span id="archive-flow-link" class="link">Back-up flow</span>
<img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Archives the flow configuration."/>
</div>
<div class="read-only setting-field">
<span id="read-only-data-flow-title-field"></span>
</div>
</div>
<div class="setting">
<div class="setting-name">Data flow comments</div>
<div class="editable setting-field">
<textarea id="data-flow-comments-field" name="data-flow-comments" class="setting-input"></textarea>
</div>
<div class="read-only setting-field">
<span id="read-only-data-flow-comments-field"></span>
</div>
</div>
<div class="setting"> <div class="setting">
<div class="setting-name"> <div class="setting-name">
Maximum timer driven thread count Maximum timer driven thread count
@ -60,6 +39,8 @@
</div> </div>
<div class="editable setting-field"> <div class="editable setting-field">
<input type="text" id="maximum-timer-driven-thread-count-field" class="setting-input"/> <input type="text" id="maximum-timer-driven-thread-count-field" class="setting-input"/>
<span id="archive-flow-link" class="link">Back-up flow</span>
<img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Archives the flow configuration."/>
</div> </div>
<div class="read-only setting-field"> <div class="read-only setting-field">
<span id="read-only-maximum-timer-driven-thread-count-field"></span> <span id="read-only-maximum-timer-driven-thread-count-field"></span>
@ -77,7 +58,7 @@
<span id="read-only-maximum-event-driven-thread-count-field"></span> <span id="read-only-maximum-event-driven-thread-count-field"></span>
</div> </div>
</div> </div>
<div id="settings-buttons" class="editable"> <div class="editable settings-buttons">
<div id="settings-save" class="button">Apply</div> <div id="settings-save" class="button">Apply</div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>

View File

@ -14,11 +14,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* flow status styles positioned at the top of the graph */ /* flow status styles positioned at the top of the graph */
#flow-status { #flow-status {
height: 32px; height: 33px;
background-color: #E3E8EB; /*tint base-color 60%*/ background-color: #E3E8EB; /*tint base-color 60%*/
border-bottom: 1px solid #aabbc3;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
z-index: 3;
position: relative;
} }
#flow-status .icon { #flow-status .icon {

View File

@ -24,6 +24,7 @@
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
background-color: #f9fafb;
background-size: 14px 14px; background-size: 14px 14px;
background-image: linear-gradient(to right, rgba(229, 235, 237, 1) 1px, transparent 1px), linear-gradient(to bottom, rgba(229, 235, 237, 1) 1px, transparent 1px); background-image: linear-gradient(to right, rgba(229, 235, 237, 1) 1px, transparent 1px), linear-gradient(to bottom, rgba(229, 235, 237, 1) 1px, transparent 1px);
z-index: 1; z-index: 1;
@ -64,6 +65,11 @@ g.component rect.body.unauthorized {
fill: #f4f6f7; fill: #f4f6f7;
} }
g.component rect.border {
stroke: rgba(0,0,0,0.25);
stroke-width: 1;
}
g.component rect.border.unauthorized { g.component rect.border.unauthorized {
stroke-width: 1.5; stroke-width: 1.5;
stroke: #ba554a; stroke: #ba554a;

View File

@ -23,11 +23,15 @@ md-toolbar.md-small {
height: 56px; height: 56px;
background-color:#AABBC3; /*tint base-color 40%*/ background-color:#AABBC3; /*tint base-color 40%*/
min-height: 56px; min-height: 56px;
max-height: 56px; } max-height: 56px;
position: relative;
z-index: 4;
}
md-toolbar.md-small .md-toolbar-tools { md-toolbar.md-small .md-toolbar-tools {
height: 56px; height: 56px;
min-height: 56px; min-height: 56px;
max-height: 56px; } max-height: 56px;
}
#header .icon { #header .icon {
font-size:30px; font-size:30px;

View File

@ -55,12 +55,11 @@
} }
div.graph-control { div.graph-control {
background-color: rgba(249, 250, 251, 0.97); /*tint base-color 96%*/ box-shadow: 0 1px 6px rgba(0,0,0,0.25);
border-top: 1px solid #C7D2D7; /*tint base-color 60%*/ background-color: rgba(249, 250, 251, 0.9);
border-right: 1px solid #C7D2D7; /*tint base-color 60%*/ border-top: 1px solid #aabbc3;
border-bottom: 1px solid #C7D2D7; /*tint base-color 60%*/ border-right: 1px solid #aabbc3;
border-top-right-radius: 2px; border-bottom: 1px solid #aabbc3;
border-bottom-right-radius: 2px;
padding: 10px; padding: 10px;
margin-bottom: 2px; margin-bottom: 2px;
} }
@ -75,9 +74,8 @@ div.graph-control button {
height: 24px; height: 24px;
width: 24px; width: 24px;
line-height: 19px; line-height: 19px;
border-radius: 2px;
border: 1px solid #CCDADB; /*tint link-color 80%*/ border: 1px solid #CCDADB; /*tint link-color 80%*/
background-color: rgba(249, 250, 251, 0.97); background-color: rgba(249,250,251,1);
color: #004849; color: #004849;
} }
@ -186,8 +184,9 @@ rect.birdseye-brush {
#breadcrumbs { #breadcrumbs {
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
background-color: rgba(249, 250, 251, 0.97); /*tint base-color 96%*/ box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
border-top: 1px solid #C7D2D7; /*tint base-color 60%*/ background-color: rgba(249, 250, 251, 0.9);
border-top: 1px solid #aabbc3;
color: #598599; color: #598599;
z-index: 3; z-index: 3;
height: 31px; height: 31px;

View File

@ -14,17 +14,64 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#process-group-configuration { #process-group-configuration {
z-index: 1301; position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
display: none; display: none;
width: 400px; padding: 20px;
height: 320px;
} }
.process-group-field { #add-process-group-configuration-controller-service {
width: 369px; float: right;
width: 19px;
height: 19px;
margin-top: 4px;
cursor: pointer;
} }
textarea.process-group-field { /* settings tabs */
#process-group-configuration-tabs {
float: left;
}
#process-group-configuration-refresh-button {
height: 24px;
width: 26px;
float: left;
}
#process-group-configuration-loading-container {
float: left;
width: 16px;
height: 16px;
background-color: transparent;
margin-top: 4px;
margin-left: 3px;
}
#process-group-configuration-last-refreshed {
font-weight: bold;
}
/* general */
#general-process-group-configuration {
margin-top: -190px;
margin-left: 10px;
}
#general-process-group-configuration input, #general-process-group-configuration textarea {
font-size: 11px !important;
font-family: Verdana;
width: 350px;
vertical-align: middle;
}
#process-group-comments {
height: 100px; height: 100px;
} }

View File

@ -25,13 +25,13 @@
padding: 20px; padding: 20px;
} }
#settings-header-text { div.settings-header-text {
height: 35px; height: 35px;
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
} }
#settings-container { div.settings-container {
margin-top: 18px; margin-top: 18px;
} }
@ -45,11 +45,11 @@
/* settings tabs */ /* settings tabs */
#settings-tabs-container { div.settings-tabs-container {
border-bottom: 2px solid #666; border-bottom: 2px solid #666;
} }
#settings-tabs { div.settings-tabs {
float: left; float: left;
} }
@ -78,7 +78,7 @@
border-right: 1px solid #666; border-right: 1px solid #666;
} }
#settings-tab-background { div.settings-tab-background {
height: 200px; height: 200px;
margin-top: 1px; margin-top: 1px;
background-color: transparent; background-color: transparent;
@ -86,7 +86,7 @@
filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr=#dddddd, endColorstr=#ffffff); filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr=#dddddd, endColorstr=#ffffff);
} }
#settings div.configuration-tab { #settings div.configuration-tab, #process-group-configuration div.configuration-tab {
display: none; display: none;
} }
@ -105,13 +105,13 @@ span.sorted {
text-decoration: underline; text-decoration: underline;
} }
#settings-refresh-button { div.settings-refresh-button {
height: 24px; height: 24px;
width: 26px; width: 26px;
float: left; float: left;
} }
#settings-last-refreshed-container { div.settings-last-refreshed-container {
float: left; float: left;
color: #666; color: #666;
font-weight: normal; font-weight: normal;
@ -134,15 +134,6 @@ span.sorted {
font-weight: bold; font-weight: bold;
} }
#settings-refresh-required-icon {
float: left;
margin-top: 4px;
margin-left: 3px;
width: 18px;
height: 16px;
background-image: url(../images/iconAlert.png);
}
/* general */ /* general */
#general-settings { #general-settings {
@ -150,23 +141,19 @@ span.sorted {
margin-left: 10px; margin-left: 10px;
} }
#general-settings input, #general-settings textarea { #general-settings input {
font-size: 11px !important; font-size: 11px !important;
font-family: Verdana; font-family: Verdana;
width: 350px; width: 350px;
vertical-align: middle; vertical-align: middle;
} }
#data-flow-comments-field { div.settings-buttons {
height: 100px; margin-left: 310px;
}
#settings-buttons {
margin-left: 304px;
margin-top: 10px; margin-top: 10px;
} }
#settings-save { div.settings-buttons div.button {
float: left; float: left;
} }

View File

@ -1108,6 +1108,11 @@
}; };
var goToControllerService = function (property) { var goToControllerService = function (property) {
$.ajax({
type: 'GET',
url: '../nifi-api/controller-services/' + encodeURIComponent(property.value),
dataType: 'json'
}).done(function (controllerServiceEntity) {
// close the dialog // close the dialog
var dialog = table.closest('.dialog'); var dialog = table.closest('.dialog');
if (dialog.hasClass('modal')) { if (dialog.hasClass('modal')) {
@ -1116,28 +1121,30 @@
dialog.hide(); dialog.hide();
} }
var controllerService = controllerServiceEntity.component;
$.Deferred(function (deferred) { $.Deferred(function (deferred) {
if (nf.Common.isDefinedAndNotNull(controllerService.parentGroupId)) {
nf.ProcessGroupConfiguration.showConfiguration(controllerService.parentGroupId).done(function () {
deferred.resolve();
});
} else {
if ($('#settings').is(':visible')) { if ($('#settings').is(':visible')) {
deferred.resolve(); deferred.resolve();
} else { } else {
// reload the settings and show // reload the settings and show
nf.Settings.loadSettings().done(function () { nf.Settings.showSettings().done(function () {
nf.Settings.showSettings();
deferred.resolve(); deferred.resolve();
}); });
} }
}
}).done(function () { }).done(function () {
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); if (nf.Common.isDefinedAndNotNull(controllerService.parentGroupId)) {
var controllerServiceData = controllerServiceGrid.getData(); nf.ProcessGroupConfiguration.selectControllerService(property.value);
} else {
// select the desired service nf.Settings.selectControllerService(property.value);
var row = controllerServiceData.getRowById(property.value); }
controllerServiceGrid.setSelectedRows([row]);
controllerServiceGrid.scrollRowIntoView(row);
// select the controller services tab
$('#settings-tabs').find('li:eq(1)').click();
}); });
}).fail(nf.Common.handleAjaxError);
}; };
// initialize the grid // initialize the grid
@ -1580,6 +1587,7 @@
var propertyTableContainer = $(this); var propertyTableContainer = $(this);
var options = propertyTableContainer.data('options'); var options = propertyTableContainer.data('options');
if (nf.Common.isDefinedAndNotNull(options)) {
// clear the property table container // clear the property table container
clear(propertyTableContainer); clear(propertyTableContainer);
@ -1588,6 +1596,7 @@
$(options.dialogContainer).children('div.new-property-dialog').remove(); $(options.dialogContainer).children('div.new-property-dialog').remove();
$(options.dialogContainer).children('div.new-inline-controller-service-dialog').remove(); $(options.dialogContainer).children('div.new-inline-controller-service-dialog').remove();
} }
}
}); });
}, },

View File

@ -128,9 +128,7 @@ nf.ng.Canvas.GlobalMenuCtrl = function (serviceProvider) {
* Launch the settings shell. * Launch the settings shell.
*/ */
launch: function () { launch: function () {
nf.Settings.loadSettings().done(function () {
nf.Settings.showSettings(); nf.Settings.showSettings();
});
} }
} }
}; };

View File

@ -118,32 +118,6 @@ nf.ng.Canvas.HeaderCtrl = function (serviceProvider, toolboxCtrl, globalMenuCtrl
this.globalMenuCtrl.init(); this.globalMenuCtrl.init();
this.flowStatusCtrl.init(); this.flowStatusCtrl.init();
this.loginCtrl.init(); this.loginCtrl.init();
},
/**
* Reloads and clears any warnings.
*/
reloadAndClearWarnings: function () {
nf.Canvas.reload().done(function () {
// update component visibility
nf.Canvas.View.updateVisibility();
// refresh the birdseye
nf.Birdseye.refresh();
// hide the refresh link on the canvas
$('#stats-last-refreshed').removeClass('alert');
$('#refresh-required-container').hide();
// hide the refresh link on the settings
$('#settings-last-refreshed').removeClass('alert');
$('#settings-refresh-required-icon').hide();
}).fail(function () {
nf.Dialog.showOkDialog({
dialogContent: 'Unable to refresh the current group.',
overlayBackground: true
});
});
} }
} }

View File

@ -691,13 +691,16 @@ nf.Actions = (function () {
* @param {selection} selection Selection of the component to be configured * @param {selection} selection Selection of the component to be configured
*/ */
showConfiguration: function (selection) { showConfiguration: function (selection) {
if (selection.size() === 1) { if (selection.empty()) {
nf.ProcessGroupConfiguration.showConfiguration(nf.Canvas.getGroupId());
} else if (selection.size() === 1) {
var selectionData = selection.datum();
if (nf.CanvasUtils.isProcessor(selection)) { if (nf.CanvasUtils.isProcessor(selection)) {
nf.ProcessorConfiguration.showConfiguration(selection); nf.ProcessorConfiguration.showConfiguration(selection);
} else if (nf.CanvasUtils.isLabel(selection)) { } else if (nf.CanvasUtils.isLabel(selection)) {
nf.LabelConfiguration.showConfiguration(selection); nf.LabelConfiguration.showConfiguration(selection);
} else if (nf.CanvasUtils.isProcessGroup(selection)) { } else if (nf.CanvasUtils.isProcessGroup(selection)) {
nf.ProcessGroupConfiguration.showConfiguration(selection); nf.ProcessGroupConfiguration.showConfiguration(selectionData.id);
} else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) { } else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) {
nf.RemoteProcessGroupConfiguration.showConfiguration(selection); nf.RemoteProcessGroupConfiguration.showConfiguration(selection);
} else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) { } else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) {
@ -710,12 +713,14 @@ nf.Actions = (function () {
// Defines an action for showing component details (like configuration but read only). // Defines an action for showing component details (like configuration but read only).
showDetails: function (selection) { showDetails: function (selection) {
if (selection.size() === 1) { if (selection.empty()) {
nf.ProcessGroupDetails.showConfiguration(nf.Canvas.getGroupId());
} else if (selection.size() === 1) {
var selectionData = selection.datum(); var selectionData = selection.datum();
if (nf.CanvasUtils.isProcessor(selection)) { if (nf.CanvasUtils.isProcessor(selection)) {
nf.ProcessorDetails.showDetails(nf.Canvas.getGroupId(), selectionData.id); nf.ProcessorDetails.showDetails(nf.Canvas.getGroupId(), selectionData.id);
} else if (nf.CanvasUtils.isProcessGroup(selection)) { } else if (nf.CanvasUtils.isProcessGroup(selection)) {
nf.ProcessGroupDetails.showDetails(selection); nf.ProcessGroupDetails.showConfiguration(selectionData.id);
} else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) { } else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) {
nf.RemoteProcessGroupDetails.showDetails(selection); nf.RemoteProcessGroupDetails.showDetails(selection);
} else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) { } else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) {

View File

@ -113,7 +113,6 @@ nf.Canvas = (function () {
var groupName = null; var groupName = null;
var accessPolicy = null; var accessPolicy = null;
var parentGroupId = null; var parentGroupId = null;
var secureSiteToSite = false;
var clustered = false; var clustered = false;
var svg = null; var svg = null;
var canvas = null; var canvas = null;
@ -267,33 +266,45 @@ nf.Canvas = (function () {
// filter for drop shadow // filter for drop shadow
var filter = defs.append('filter') var filter = defs.append('filter')
.attr('id', 'component-drop-shadow'); .attr({
'id': 'component-drop-shadow',
'height': '140%',
'y': '-20%'
});
// blur // blur
filter.append('feGaussianBlur') filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha') .attr({
.attr('stdDeviation', 2) 'in': 'SourceAlpha',
.attr('result', 'blur'); 'stdDeviation': 3,
'result': 'blur'
});
// offset // offset
filter.append('feOffset') filter.append('feOffset')
.attr('in', 'blur') .attr({
.attr('dx', 0) 'in': 'blur',
.attr('dy', 1) 'dx': 0,
.attr('result', 'offsetBlur'); 'dy': 1,
'result': 'offsetBlur'
});
// color/opacity // color/opacity
filter.append('feFlood') filter.append('feFlood')
.attr('flood-color', '#000000') .attr({
.attr('flood-opacity', 0.25) 'flood-color': '#000000',
.attr('result', 'offsetColor'); 'flood-opacity': 0.4,
'result': 'offsetColor'
});
// combine // combine
filter.append('feComposite') filter.append('feComposite')
.attr('in', 'offsetColor') .attr({
.attr('in2', 'offsetBlur') 'in': 'offsetColor',
.attr('operator', 'in') 'in2': 'offsetBlur',
.attr('result', 'offsetColorBlur'); 'operator': 'in',
'result': 'offsetColorBlur'
});
// stack the effect under the source graph // stack the effect under the source graph
var feMerge = filter.append('feMerge'); var feMerge = filter.append('feMerge');
@ -512,7 +523,6 @@ nf.Canvas = (function () {
if (e.target === window) { if (e.target === window) {
updateGraphSize(); updateGraphSize();
updateFlowStatusContainerSize(); updateFlowStatusContainerSize();
nf.Settings.resetTableSize();
} }
}).on('keydown', function (evt) { }).on('keydown', function (evt) {
// if a dialog is open, disable canvas shortcuts // if a dialog is open, disable canvas shortcuts
@ -819,9 +829,6 @@ nf.Canvas = (function () {
// get the auto refresh interval // get the auto refresh interval
var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10); var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
// initialize whether site to site is secure
secureSiteToSite = configDetails.siteToSiteSecure;
// init storage // init storage
nf.Storage.init(); nf.Storage.init();
@ -890,13 +897,6 @@ nf.Canvas = (function () {
return clustered === true; return clustered === true;
}, },
/**
* Returns whether site to site communications is secure.
*/
isSecureSiteToSite: function () {
return secureSiteToSite;
},
/** /**
* Set the group id. * Set the group id.
* *

View File

@ -113,11 +113,12 @@ nf.ControllerService = (function () {
* Reloads the specified controller service. It's referencing and referenced * Reloads the specified controller service. It's referencing and referenced
* components are NOT reloaded. * components are NOT reloaded.
* *
* @param {jQuery} serviceTable
* @param {string} id * @param {string} id
*/ */
var reloadControllerService = function (id) { var reloadControllerService = function (serviceTable, id) {
// get the table and update the row accordingly // get the table and update the row accordingly
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(id); var controllerServiceEntity = controllerServiceData.getItemById(id);
@ -135,18 +136,19 @@ nf.ControllerService = (function () {
url: controllerServiceEntity.component.uri, url: controllerServiceEntity.component.uri,
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done(function (response) {
renderControllerService(response); renderControllerService(serviceTable, response);
}).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError);
}; };
/** /**
* Renders the specified controller service. * Renders the specified controller service.
* *
* @param {object} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
*/ */
var renderControllerService = function (controllerServiceEntity) { var renderControllerService = function (serviceTable, controllerServiceEntity) {
// get the table and update the row accordingly // get the table and update the row accordingly
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
var currentControllerServiceEntity = controllerServiceData.getItemById(controllerServiceEntity.id); var currentControllerServiceEntity = controllerServiceData.getItemById(controllerServiceEntity.id);
controllerServiceData.updateItem(controllerServiceEntity.id, $.extend({ controllerServiceData.updateItem(controllerServiceEntity.id, $.extend({
@ -158,11 +160,12 @@ nf.ControllerService = (function () {
* Reloads the specified controller services and all of its referencing * Reloads the specified controller services and all of its referencing
* and referenced components. * and referenced components.
* *
* @param {jQuery} serviceTable
* @param {type} controllerService * @param {type} controllerService
*/ */
var reloadControllerServiceAndReferencingComponents = function (controllerService) { var reloadControllerServiceAndReferencingComponents = function (serviceTable, controllerService) {
reloadControllerService(controllerService.id).done(function(response) { reloadControllerService(serviceTable, controllerService.id).done(function(response) {
reloadControllerServiceReferences(response.component); reloadControllerServiceReferences(serviceTable, response.component);
}); });
}; };
@ -170,9 +173,10 @@ nf.ControllerService = (function () {
* Reloads components that reference this controller service as well as * Reloads components that reference this controller service as well as
* other services that this controller service references. * other services that this controller service references.
* *
* @param {jQuery} serviceTable
* @param {object} controllerService * @param {object} controllerService
*/ */
var reloadControllerServiceReferences = function (controllerService) { var reloadControllerServiceReferences = function (serviceTable, controllerService) {
// reload all dependent processors if they are currently visible // reload all dependent processors if they are currently visible
$.each(controllerService.referencingComponents, function (_, referencingComponentEntity) { $.each(controllerService.referencingComponents, function (_, referencingComponentEntity) {
// ensure we can read the referencing component prior to reloading // ensure we can read the referencing component prior to reloading
@ -210,7 +214,7 @@ nf.ControllerService = (function () {
} }
} else { } else {
// reload the referencing services // reload the referencing services
reloadControllerService(reference.id); reloadControllerService(serviceTable, reference.id);
// update the current state of this service // update the current state of this service
var referencingComponentState = $('div.' + reference.id + '-state'); var referencingComponentState = $('div.' + reference.id + '-state');
@ -220,14 +224,14 @@ nf.ControllerService = (function () {
// consider it's referencing components if appropriate // consider it's referencing components if appropriate
if (reference.referenceCycle === false) { if (reference.referenceCycle === false) {
reloadControllerServiceReferences(reference); reloadControllerServiceReferences(serviceTable, reference);
} }
} }
}); });
// see if this controller service references another controller service // see if this controller service references another controller service
// in order to update the referenced service referencing components // in order to update the referenced service referencing components
nf.ControllerService.reloadReferencedServices(controllerService); nf.ControllerService.reloadReferencedServices(serviceTable, controllerService);
}; };
/** /**
@ -362,10 +366,11 @@ nf.ControllerService = (function () {
/** /**
* Adds the specified reference for this controller service. * Adds the specified reference for this controller service.
* *
* @param {jQuery} serviceTable
* @param {jQuery} referenceContainer * @param {jQuery} referenceContainer
* @param {array} referencingComponents * @param {array} referencingComponents
*/ */
var createReferencingComponents = function (referenceContainer, referencingComponents) { var createReferencingComponents = function (serviceTable, referenceContainer, referencingComponents) {
if (nf.Common.isEmpty(referencingComponents)) { if (nf.Common.isEmpty(referencingComponents)) {
referenceContainer.append('<div class="unset">No referencing components.</div>'); referenceContainer.append('<div class="unset">No referencing components.</div>');
return; return;
@ -427,7 +432,7 @@ nf.ControllerService = (function () {
processors.append(processorItem); processors.append(processorItem);
} else if (referencingComponent.referenceType === 'ControllerService') { } else if (referencingComponent.referenceType === 'ControllerService') {
var serviceLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () { var serviceLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
// select the selected row // select the selected row
@ -443,12 +448,12 @@ nf.ControllerService = (function () {
var referencingServiceReferencesContainer = $('<div class="referencing-component-references hidden"></div>'); var referencingServiceReferencesContainer = $('<div class="referencing-component-references hidden"></div>');
var serviceTwist = $('<div class="service expansion-button collapsed pointer"></div>').on('click', function() { var serviceTwist = $('<div class="service expansion-button collapsed pointer"></div>').on('click', function() {
if (serviceTwist.hasClass('collapsed')) { if (serviceTwist.hasClass('collapsed')) {
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
var referencingService = controllerServiceData.getItemById(referencingComponent.id); var referencingService = controllerServiceData.getItemById(referencingComponent.id);
// create the markup for the references // create the markup for the references
createReferencingComponents(referencingServiceReferencesContainer, referencingService.referencingComponents); createReferencingComponents(serviceTable, referencingServiceReferencesContainer, referencingService.referencingComponents);
} else { } else {
referencingServiceReferencesContainer.empty(); referencingServiceReferencesContainer.empty();
} }
@ -573,11 +578,12 @@ nf.ControllerService = (function () {
/** /**
* Sets whether the specified controller service is enabled. * Sets whether the specified controller service is enabled.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
* @param {boolean} enabled * @param {boolean} enabled
* @param {function} pollCondition * @param {function} pollCondition
*/ */
var setEnabled = function (controllerServiceEntity, enabled, pollCondition) { var setEnabled = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
// build the request entity // build the request entity
var updateControllerServiceEntity = { var updateControllerServiceEntity = {
'revision': nf.Client.getRevision(controllerServiceEntity), 'revision': nf.Client.getRevision(controllerServiceEntity),
@ -594,7 +600,7 @@ nf.ControllerService = (function () {
dataType: 'json', dataType: 'json',
contentType: 'application/json' contentType: 'application/json'
}).done(function (response) { }).done(function (response) {
renderControllerService(response); renderControllerService(serviceTable, response);
}).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError);
// wait until the polling of each service finished // wait until the polling of each service finished
@ -664,11 +670,12 @@ nf.ControllerService = (function () {
* Updates the scheduled state of the processors/reporting tasks referencing * Updates the scheduled state of the processors/reporting tasks referencing
* the specified controller service. * the specified controller service.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
* @param {boolean} running * @param {boolean} running
* @param {function} pollCondition * @param {function} pollCondition
*/ */
var updateReferencingSchedulableComponents = function (controllerServiceEntity, running, pollCondition) { var updateReferencingSchedulableComponents = function (serviceTable, controllerServiceEntity, running, pollCondition) {
var referenceEntity = { var referenceEntity = {
'id': controllerServiceEntity.id, 'id': controllerServiceEntity.id,
'state': running ? 'RUNNING' : 'STOPPED', 'state': running ? 'RUNNING' : 'STOPPED',
@ -705,7 +712,7 @@ nf.ControllerService = (function () {
var services = getReferencingControllerServiceIds(controllerServiceEntity.component); var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid // get the controller service grid
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service // start polling for each controller service
@ -934,11 +941,12 @@ nf.ControllerService = (function () {
/** /**
* Updates the referencing services with the specified state. * Updates the referencing services with the specified state.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
* @param {boolean} enabled * @param {boolean} enabled
* @param {function} pollCondition * @param {function} pollCondition
*/ */
var updateReferencingServices = function (controllerServiceEntity, enabled, pollCondition) { var updateReferencingServices = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
// build the reference entity // build the reference entity
var referenceEntity = { var referenceEntity = {
'id': controllerServiceEntity.id, 'id': controllerServiceEntity.id,
@ -972,7 +980,7 @@ nf.ControllerService = (function () {
var services = getReferencingControllerServiceIds(controllerServiceEntity.component); var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid // get the controller service grid
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service // start polling for each controller service
@ -1003,14 +1011,14 @@ nf.ControllerService = (function () {
* *
* @argument {object} controllerService The controller service to disable * @argument {object} controllerService The controller service to disable
*/ */
var showDisableControllerServiceDialog = function (controllerService) { var showDisableControllerServiceDialog = function (serviceTable, controllerService) {
// populate the disable controller service dialog // populate the disable controller service dialog
$('#disable-controller-service-id').text(controllerService.id); $('#disable-controller-service-id').text(controllerService.id);
$('#disable-controller-service-name').text(controllerService.name); $('#disable-controller-service-name').text(controllerService.name);
// load the controller referencing components list // load the controller referencing components list
var referencingComponentsContainer = $('#disable-controller-service-referencing-components'); var referencingComponentsContainer = $('#disable-controller-service-referencing-components');
createReferencingComponents(referencingComponentsContainer, controllerService.referencingComponents); createReferencingComponents(serviceTable, referencingComponentsContainer, controllerService.referencingComponents);
var hasUnauthorized = false; var hasUnauthorized = false;
$.each(controllerService.referencingComponents, function (_, referencingComponent) { $.each(controllerService.referencingComponents, function (_, referencingComponent) {
@ -1026,7 +1034,9 @@ nf.ControllerService = (function () {
buttons.push({ buttons.push({
buttonText: 'Disable', buttonText: 'Disable',
handler: { handler: {
click: disableHandler click: function () {
disableHandler(serviceTable);
}
} }
}); });
} }
@ -1052,19 +1062,35 @@ nf.ControllerService = (function () {
/** /**
* Shows the dialog for enabling a controller service. * Shows the dialog for enabling a controller service.
* *
* @param {object} serviceTable
* @param {object} controllerService * @param {object} controllerService
*/ */
var showEnableControllerServiceDialog = function (controllerService) { var showEnableControllerServiceDialog = function (serviceTable, controllerService) {
// populate the disable controller service dialog // populate the disable controller service dialog
$('#enable-controller-service-id').text(controllerService.id); $('#enable-controller-service-id').text(controllerService.id);
$('#enable-controller-service-name').text(controllerService.name); $('#enable-controller-service-name').text(controllerService.name);
// load the controller referencing components list // load the controller referencing components list
var referencingComponentsContainer = $('#enable-controller-service-referencing-components'); var referencingComponentsContainer = $('#enable-controller-service-referencing-components');
createReferencingComponents(referencingComponentsContainer, controllerService.referencingComponents); createReferencingComponents(serviceTable, referencingComponentsContainer, controllerService.referencingComponents);
// build the button model
var buttons = [{
buttonText: 'Enable',
handler: {
click: function () {
enableHandler(serviceTable);
}
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}];
// show the dialog // show the dialog
$('#enable-controller-service-dialog').modal('show'); $('#enable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins // load the bulletins
queryBulletins([controllerService.id]).done(function(response) { queryBulletins([controllerService.id]).done(function(response) {
@ -1084,9 +1110,11 @@ nf.ControllerService = (function () {
/** /**
* Handles the disable action of the disable controller service dialog. * Handles the disable action of the disable controller service dialog.
*
* @param {jQuery} serviceTable
*/ */
var disableHandler = function() { var disableHandler = function(serviceTable) {
var disableDialog = $(this); var disableDialog = $('#disable-controller-service-dialog');
var canceled = false; var canceled = false;
// only provide a cancel option // only provide a cancel option
@ -1107,7 +1135,7 @@ nf.ControllerService = (function () {
// get the controller service // get the controller service
var controllerServiceId = $('#disable-controller-service-id').text(); var controllerServiceId = $('#disable-controller-service-id').text();
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId); var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component; var controllerService = controllerServiceEntity.component;
@ -1133,7 +1161,7 @@ nf.ControllerService = (function () {
$.Deferred(function (deferred) { $.Deferred(function (deferred) {
// stop all referencing schedulable components // stop all referencing schedulable components
var stopped = updateReferencingSchedulableComponents(controllerServiceEntity, false, continuePolling); var stopped = updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, false, continuePolling);
// once everything has stopped // once everything has stopped
stopped.done(function () { stopped.done(function () {
@ -1141,7 +1169,7 @@ nf.ControllerService = (function () {
var disableReferencingServices = $('#disable-referencing-services').addClass('ajax-loading'); var disableReferencingServices = $('#disable-referencing-services').addClass('ajax-loading');
// disable all referencing services // disable all referencing services
var disabled = updateReferencingServices(controllerServiceEntity, false, continuePolling); var disabled = updateReferencingServices(serviceTable, controllerServiceEntity, false, continuePolling);
// everything is disabled // everything is disabled
disabled.done(function () { disabled.done(function () {
@ -1149,7 +1177,7 @@ nf.ControllerService = (function () {
var disableControllerService = $('#disable-controller-service').addClass('ajax-loading'); var disableControllerService = $('#disable-controller-service').addClass('ajax-loading');
// disable this service // disable this service
setEnabled(controllerServiceEntity, false, continuePolling).done(function () { setEnabled(serviceTable, controllerServiceEntity, false, continuePolling).done(function () {
deferred.resolve(); deferred.resolve();
disableControllerService.removeClass('ajax-loading').addClass('ajax-complete'); disableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () { }).fail(function () {
@ -1165,7 +1193,7 @@ nf.ControllerService = (function () {
disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error'); disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
}); });
}).always(function () { }).always(function () {
reloadControllerServiceAndReferencingComponents(controllerService); reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
setCloseButton(); setCloseButton();
// inform the user if the action was canceled // inform the user if the action was canceled
@ -1181,14 +1209,16 @@ nf.ControllerService = (function () {
/** /**
* Handles the enable action of the enable controller service dialog. * Handles the enable action of the enable controller service dialog.
*
* @param {jQuery} serviceTable
*/ */
var enableHandler = function() { var enableHandler = function(serviceTable) {
var enableDialog = $(this); var enableDialog = $('#enable-controller-service-dialog');
var canceled = false; var canceled = false;
// get the controller service // get the controller service
var controllerServiceId = $('#enable-controller-service-id').text(); var controllerServiceId = $('#enable-controller-service-id').text();
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId); var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component; var controllerService = controllerServiceEntity.component;
@ -1256,7 +1286,7 @@ nf.ControllerService = (function () {
$.Deferred(function (deferred) { $.Deferred(function (deferred) {
// enable this controller service // enable this controller service
var enable = setEnabled(controllerServiceEntity, true, continuePolling); var enable = setEnabled(serviceTable, controllerServiceEntity, true, continuePolling);
if (scope === config.serviceAndReferencingComponents) { if (scope === config.serviceAndReferencingComponents) {
// once the service is enabled, activate all referencing components // once the service is enabled, activate all referencing components
@ -1265,7 +1295,7 @@ nf.ControllerService = (function () {
var enableReferencingServices = $('#enable-referencing-services').addClass('ajax-loading'); var enableReferencingServices = $('#enable-referencing-services').addClass('ajax-loading');
// enable the referencing services // enable the referencing services
var servicesEnabled = updateReferencingServices(controllerServiceEntity, true, continuePolling); var servicesEnabled = updateReferencingServices(serviceTable, controllerServiceEntity, true, continuePolling);
// once all the referencing services are enbled // once all the referencing services are enbled
servicesEnabled.done(function () { servicesEnabled.done(function () {
@ -1273,7 +1303,7 @@ nf.ControllerService = (function () {
var enableReferencingSchedulable = $('#enable-referencing-schedulable').addClass('ajax-loading'); var enableReferencingSchedulable = $('#enable-referencing-schedulable').addClass('ajax-loading');
// start all referencing schedulable components // start all referencing schedulable components
updateReferencingSchedulableComponents(controllerServiceEntity, true, continuePolling).done(function() { updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, true, continuePolling).done(function() {
deferred.resolve(); deferred.resolve();
enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete'); enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () { }).fail(function () {
@ -1298,7 +1328,7 @@ nf.ControllerService = (function () {
}); });
} }
}).always(function () { }).always(function () {
reloadControllerServiceAndReferencingComponents(controllerService); reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
setCloseButton(); setCloseButton();
// inform the user if the action was canceled // inform the user if the action was canceled
@ -1331,8 +1361,10 @@ nf.ControllerService = (function () {
/** /**
* Goes to a service configuration from the property table. * Goes to a service configuration from the property table.
*
* @param {jQuery} serviceTable
*/ */
var goToServiceFromProperty = function () { var goToServiceFromProperty = function (serviceTable) {
return $.Deferred(function (deferred) { return $.Deferred(function (deferred) {
// close all fields currently being edited // close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow'); $('#controller-service-properties').propertytable('saveRow');
@ -1348,7 +1380,7 @@ nf.ControllerService = (function () {
}, },
yesHandler: function () { yesHandler: function () {
var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails'); var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails');
saveControllerService(controllerServiceEntity).done(function () { saveControllerService(serviceTable, controllerServiceEntity).done(function () {
deferred.resolve(); deferred.resolve();
}).fail(function () { }).fail(function () {
deferred.reject(); deferred.reject();
@ -1361,7 +1393,7 @@ nf.ControllerService = (function () {
}).promise(); }).promise();
}; };
var saveControllerService = function (controllerServiceEntity) { var saveControllerService = function (serviceTable, controllerServiceEntity) {
// marshal the settings and properties and update the controller service // marshal the settings and properties and update the controller service
var updatedControllerService = marshalDetails(); var updatedControllerService = marshalDetails();
@ -1391,11 +1423,11 @@ nf.ControllerService = (function () {
contentType: 'application/json' contentType: 'application/json'
}).done(function (response) { }).done(function (response) {
// reload the controller service // reload the controller service
renderControllerService(response); renderControllerService(serviceTable, response);
// reload all previously referenced controller services // reload all previously referenced controller services
$.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) { $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
reloadControllerService(oldServiceReferenceId); reloadControllerService(serviceTable, oldServiceReferenceId);
}); });
}).fail(handleControllerServiceConfigurationError); }).fail(handleControllerServiceConfigurationError);
} else { } else {
@ -1480,7 +1512,7 @@ nf.ControllerService = (function () {
}); });
// initialize the conroller service configuration dialog // initialize the conroller service configuration dialog
$('#controller-service-configuration').data('mode', config.edit).modal({ $('#controller-service-configuration').modal({
headerText: 'Configure Controller Service', headerText: 'Configure Controller Service',
overlayBackground: false, overlayBackground: false,
handler: { handler: {
@ -1506,15 +1538,6 @@ nf.ControllerService = (function () {
} }
}); });
// initialize the property table
$('#controller-service-properties').propertytable({
readOnly: false,
groupId: nf.Canvas.getGroupId(),
dialogContainer: '#new-controller-service-property-container',
descriptorDeferred: getControllerServicePropertyDescriptor,
goToServiceDeferred: goToServiceFromProperty
});
// initialize the disable service dialog // initialize the disable service dialog
$('#disable-controller-service-dialog').modal({ $('#disable-controller-service-dialog').modal({
headerText: 'Disable Controller Service', headerText: 'Disable Controller Service',
@ -1543,19 +1566,6 @@ nf.ControllerService = (function () {
nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state'); nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins'); nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins');
referencingComponents.css('border-width', '0').empty(); referencingComponents.css('border-width', '0').empty();
// reset dialog
disableDialog.modal('setButtonModel', [{
buttonText: 'Disable',
handler: {
click: disableHandler
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}]);
} }
} }
}); });
@ -1577,17 +1587,6 @@ nf.ControllerService = (function () {
$('#enable-controller-service-dialog').modal({ $('#enable-controller-service-dialog').modal({
headerText: 'Enable Controller Service', headerText: 'Enable Controller Service',
overlayBackground: false, overlayBackground: false,
buttons: [{
buttonText: 'Enable',
handler: {
click: enableHandler
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}],
handler: { handler: {
close: function() { close: function() {
var enableDialog = $(this); var enableDialog = $(this);
@ -1613,19 +1612,6 @@ nf.ControllerService = (function () {
nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state'); nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins'); nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-bulletins');
referencingComponents.css('border-width', '0').empty(); referencingComponents.css('border-width', '0').empty();
// reset dialog
enableDialog.modal('setButtonModel', [{
buttonText: 'Enable',
handler: {
click: enableHandler
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}]);
} }
} }
}); });
@ -1634,11 +1620,12 @@ nf.ControllerService = (function () {
/** /**
* Shows the configuration dialog for the specified controller service. * Shows the configuration dialog for the specified controller service.
* *
* @argument {jQuery} serviceTable The controller service table
* @argument {object} controllerServiceEntity The controller service * @argument {object} controllerServiceEntity The controller service
*/ */
showConfiguration: function (controllerServiceEntity) { showConfiguration: function (serviceTable, controllerServiceEntity) {
var controllerServiceDialog = $('#controller-service-configuration'); var controllerServiceDialog = $('#controller-service-configuration');
if (controllerServiceDialog.data('mode') === config.readOnly) { if (controllerServiceDialog.data('mode') !== config.edit) {
// update the visibility // update the visibility
$('#controller-service-configuration .controller-service-read-only').hide(); $('#controller-service-configuration .controller-service-read-only').hide();
$('#controller-service-configuration .controller-service-editable').show(); $('#controller-service-configuration .controller-service-editable').show();
@ -1649,7 +1636,9 @@ nf.ControllerService = (function () {
groupId: nf.Canvas.getGroupId(), groupId: nf.Canvas.getGroupId(),
dialogContainer: '#new-controller-service-property-container', dialogContainer: '#new-controller-service-property-container',
descriptorDeferred: getControllerServicePropertyDescriptor, descriptorDeferred: getControllerServicePropertyDescriptor,
goToServiceDeferred: goToServiceFromProperty goToServiceDeferred: function () {
return goToServiceFromProperty(serviceTable);
}
}); });
// update the mode // update the mode
@ -1692,7 +1681,7 @@ nf.ControllerService = (function () {
var referenceContainer = $('#controller-service-referencing-components'); var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list // load the controller referencing components list
createReferencingComponents(referenceContainer, controllerService.referencingComponents); createReferencingComponents(serviceTable, referenceContainer, controllerService.referencingComponents);
var buttons = [{ var buttons = [{
buttonText: 'Apply', buttonText: 'Apply',
@ -1702,8 +1691,8 @@ nf.ControllerService = (function () {
$('#controller-service-properties').propertytable('saveRow'); $('#controller-service-properties').propertytable('saveRow');
// save the controller service // save the controller service
saveControllerService(controllerServiceEntity).done(function (response) { saveControllerService(serviceTable, controllerServiceEntity).done(function (response) {
reloadControllerServiceReferences(response.component); reloadControllerServiceReferences(serviceTable, response.component);
// close the details panel // close the details panel
controllerServiceDialog.modal('hide'); controllerServiceDialog.modal('hide');
@ -1735,7 +1724,7 @@ nf.ControllerService = (function () {
// show the custom ui // show the custom ui
nf.CustomUi.showCustomUi($('#controller-service-id').text(), controllerService.customUiUrl, true).done(function () { nf.CustomUi.showCustomUi($('#controller-service-id').text(), controllerService.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the controller service // once the custom ui is closed, reload the controller service
reloadControllerServiceAndReferencingComponents(controllerService); reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
// show the settings // show the settings
nf.Settings.showSettings(); nf.Settings.showSettings();
@ -1753,7 +1742,7 @@ nf.ControllerService = (function () {
overlayBackground: false, overlayBackground: false,
noHandler: openCustomUi, noHandler: openCustomUi,
yesHandler: function () { yesHandler: function () {
saveControllerService(controllerServiceEntity).done(function () { saveControllerService(serviceTable, controllerServiceEntity).done(function () {
// open the custom ui // open the custom ui
openCustomUi(); openCustomUi();
}); });
@ -1785,11 +1774,12 @@ nf.ControllerService = (function () {
/** /**
* Shows the controller service details in a read only dialog. * Shows the controller service details in a read only dialog.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
*/ */
showDetails: function(controllerServiceEntity) { showDetails: function(serviceTable, controllerServiceEntity) {
var controllerServiceDialog = $('#controller-service-configuration'); var controllerServiceDialog = $('#controller-service-configuration');
if (controllerServiceDialog.data('mode') === config.edit) { if (controllerServiceDialog.data('mode') !== config.readOnly) {
// update the visibility // update the visibility
$('#controller-service-configuration .controller-service-read-only').show(); $('#controller-service-configuration .controller-service-read-only').show();
$('#controller-service-configuration .controller-service-editable').hide(); $('#controller-service-configuration .controller-service-editable').hide();
@ -1839,7 +1829,7 @@ nf.ControllerService = (function () {
var referenceContainer = $('#controller-service-referencing-components'); var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list // load the controller referencing components list
createReferencingComponents(referenceContainer, controllerService.referencingComponents); createReferencingComponents(serviceTable, referenceContainer, controllerService.referencingComponents);
var buttons = [{ var buttons = [{
buttonText: 'Ok', buttonText: 'Ok',
@ -1889,19 +1879,21 @@ nf.ControllerService = (function () {
/** /**
* Enables the specified controller service. * Enables the specified controller service.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
*/ */
enable: function(controllerServiceEntity) { enable: function(serviceTable, controllerServiceEntity) {
showEnableControllerServiceDialog(controllerServiceEntity.component); showEnableControllerServiceDialog(serviceTable, controllerServiceEntity.component);
}, },
/** /**
* Disables the specified controller service. * Disables the specified controller service.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
*/ */
disable: function(controllerServiceEntity) { disable: function(serviceTable, controllerServiceEntity) {
showDisableControllerServiceDialog(controllerServiceEntity.component); showDisableControllerServiceDialog(serviceTable, controllerServiceEntity.component);
}, },
/** /**
@ -1909,20 +1901,22 @@ nf.ControllerService = (function () {
* necessary because the specified component state is reflected in the * necessary because the specified component state is reflected in the
* referenced service referencing components. * referenced service referencing components.
* *
* @param {jQuery} serviceTable
* @param {object} component * @param {object} component
*/ */
reloadReferencedServices: function(component) { reloadReferencedServices: function(serviceTable, component) {
$.each(getReferencedServices(component), function (_, referencedServiceId) { $.each(getReferencedServices(component), function (_, referencedServiceId) {
reloadControllerService(referencedServiceId); reloadControllerService(serviceTable, referencedServiceId);
}); });
}, },
/** /**
* Deletes the specified controller service. * Deletes the specified controller service.
* *
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity * @param {object} controllerServiceEntity
*/ */
remove: function(controllerServiceEntity) { remove: function(serviceTable, controllerServiceEntity) {
// prompt for removal? // prompt for removal?
var revision = nf.Client.getRevision(controllerServiceEntity); var revision = nf.Client.getRevision(controllerServiceEntity);
@ -1935,12 +1929,12 @@ nf.ControllerService = (function () {
dataType: 'json' dataType: 'json'
}).done(function (response) { }).done(function (response) {
// remove the service // remove the service
var controllerServiceGrid = $('#controller-services-table').data('gridInstance'); var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData(); var controllerServiceData = controllerServiceGrid.getData();
controllerServiceData.deleteItem(controllerServiceEntity.id); controllerServiceData.deleteItem(controllerServiceEntity.id);
// reload the as necessary // reload the as necessary
reloadControllerServiceReferences(controllerServiceEntity.component); reloadControllerServiceReferences(serviceTable, controllerServiceEntity.component);
}).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError);
} }
}; };

View File

@ -0,0 +1,883 @@
/*
* 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.
*/
/* global nf, Slick, d3 */
nf.ControllerServices = (function () {
var dblClick = null;
var initialized = false;
var config = {
filterText: 'Filter',
styles: {
filterList: 'filter-list'
},
urls: {
api: '../nifi-api',
controllerServiceTypes: '../nifi-api/flow/controller-service-types'
}
};
var gridOptions = {
forceFitColumns: true,
enableTextSelectionOnCells: true,
enableCellNavigation: true,
enableColumnReorder: false,
autoEdit: false,
multiSelect: false
};
/**
* Get the text out of the filter field. If the filter field doesn't
* have any text it will contain the text 'filter list' so this method
* accounts for that.
*/
var getControllerServiceTypeFilterText = function () {
var filterText = '';
var filterField = $('#controller-service-type-filter');
if (!filterField.hasClass(config.styles.filterList)) {
filterText = filterField.val();
}
return filterText;
};
/**
* Filters the processor type table.
*/
var applyControllerServiceTypeFilter = function () {
// get the dataview
var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
// ensure the grid has been initialized
if (nf.Common.isDefinedAndNotNull(controllerServiceTypesGrid)) {
var controllerServiceTypesData = controllerServiceTypesGrid.getData();
// update the search criteria
controllerServiceTypesData.setFilterArgs({
searchString: getControllerServiceTypeFilterText()
});
controllerServiceTypesData.refresh();
// update the selection if possible
if (controllerServiceTypesData.getLength() > 0) {
controllerServiceTypesGrid.setSelectedRows([0]);
}
}
};
/**
* Hides the selected controller service.
*/
var clearSelectedControllerService = function () {
$('#controller-service-type-description').text('');
$('#controller-service-type-name').text('');
$('#selected-controller-service-name').text('');
$('#selected-controller-service-type').text('');
$('#controller-service-description-container').hide();
};
/**
* Clears the selected controller service type.
*/
var clearControllerServiceSelection = function () {
// clear the selected row
clearSelectedControllerService();
// clear the active cell the it can be reselected when its included
var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
controllerServiceTypesGrid.resetActiveCell();
};
/**
* Performs the filtering.
*
* @param {object} item The item subject to filtering
* @param {object} args Filter arguments
* @returns {Boolean} Whether or not to include the item
*/
var filterControllerServiceTypes = function (item, args) {
// determine if the item matches the filter
var matchesFilter = matchesRegex(item, args);
// determine if the row matches the selected tags
var matchesTags = true;
if (matchesFilter) {
var tagFilters = $('#controller-service-tag-cloud').tagcloud('getSelectedTags');
var hasSelectedTags = tagFilters.length > 0;
if (hasSelectedTags) {
matchesTags = matchesSelectedTags(tagFilters, item['tags']);
}
}
// determine if this row should be visible
var matches = matchesFilter && matchesTags;
// if this row is currently selected and its being filtered
if (matches === false && $('#selected-controller-service-type').text() === item['type']) {
clearControllerServiceSelection();
}
return matches;
};
/**
* Determines if the item matches the filter.
*
* @param {object} item The item to filter
* @param {object} args The filter criteria
* @returns {boolean} Whether the item matches the filter
*/
var matchesRegex = function (item, args) {
if (args.searchString === '') {
return true;
}
try {
// perform the row filtering
var filterExp = new RegExp(args.searchString, 'i');
} catch (e) {
// invalid regex
return false;
}
// determine if the item matches the filter
var matchesLabel = item['label'].search(filterExp) >= 0;
var matchesTags = item['tags'].search(filterExp) >= 0;
return matchesLabel || matchesTags;
};
/**
* Determines if the specified tags match all the tags selected by the user.
*
* @argument {string[]} tagFilters The tag filters
* @argument {string} tags The tags to test
*/
var matchesSelectedTags = function (tagFilters, tags) {
var selectedTags = [];
$.each(tagFilters, function (_, filter) {
selectedTags.push(filter);
});
// normalize the tags
var normalizedTags = tags.toLowerCase();
var matches = true;
$.each(selectedTags, function (i, selectedTag) {
if (normalizedTags.indexOf(selectedTag) === -1) {
matches = false;
return false;
}
});
return matches;
};
/**
* Adds the currently selected controller service.
*
* @param {string} controllerServicesUri
* @param {jQuery} serviceTable
*/
var addSelectedControllerService = function (controllerServicesUri, serviceTable) {
var selectedServiceType = $('#selected-controller-service-type').text();
// ensure something was selected
if (selectedServiceType === '') {
nf.Dialog.showOkDialog({
dialogContent: 'The type of controller service to create must be selected.',
overlayBackground: false
});
} else {
addControllerService(controllerServicesUri, serviceTable, selectedServiceType);
}
};
/**
* Adds a new controller service of the specified type.
*
* @param {string} controllerServicesUri
* @param {jQuery} serviceTable
* @param {string} controllerServiceType
*/
var addControllerService = function (controllerServicesUri, serviceTable, controllerServiceType) {
// build the controller service entity
var controllerServiceEntity = {
'component': {
'type': controllerServiceType
}
};
// add the new controller service
var addService = $.ajax({
type: 'POST',
url: controllerServicesUri,
data: JSON.stringify(controllerServiceEntity),
dataType: 'json',
contentType: 'application/json'
}).done(function (controllerServiceEntity) {
// add the item
var controllerServicesGrid = serviceTable.data('gridInstance');
var controllerServicesData = controllerServicesGrid.getData();
controllerServicesData.addItem(controllerServiceEntity);
// resort
controllerServicesData.reSort();
controllerServicesGrid.invalidate();
// select the new controller service
var row = controllerServicesData.getRowById(controllerServiceEntity.id);
controllerServicesGrid.setSelectedRows([row]);
controllerServicesGrid.scrollRowIntoView(row);
}).fail(nf.Common.handleAjaxError);
// hide the dialog
$('#new-controller-service-dialog').modal('hide');
return addService;
};
/**
* Initializes the new controller service dialog.
*/
var initNewControllerServiceDialog = function () {
// define the function for filtering the list
$('#controller-service-type-filter').focus(function () {
if ($(this).hasClass(config.styles.filterList)) {
$(this).removeClass(config.styles.filterList).val('');
}
}).blur(function () {
if ($(this).val() === '') {
$(this).addClass(config.styles.filterList).val(config.filterText);
}
}).addClass(config.styles.filterList).val(config.filterText);
// initialize the processor type table
var controllerServiceTypesColumns = [
{id: 'type', name: 'Type', field: 'label', sortable: false, resizable: true},
{id: 'tags', name: 'Tags', field: 'tags', sortable: false, resizable: true}
];
// initialize the dataview
var controllerServiceTypesData = new Slick.Data.DataView({
inlineFilters: false
});
controllerServiceTypesData.setItems([]);
controllerServiceTypesData.setFilterArgs({
searchString: getControllerServiceTypeFilterText()
});
controllerServiceTypesData.setFilter(filterControllerServiceTypes);
// initialize the grid
var controllerServiceTypesGrid = new Slick.Grid('#controller-service-types-table', controllerServiceTypesData, controllerServiceTypesColumns, gridOptions);
controllerServiceTypesGrid.setSelectionModel(new Slick.RowSelectionModel());
controllerServiceTypesGrid.registerPlugin(new Slick.AutoTooltips());
controllerServiceTypesGrid.setSortColumn('type', true);
controllerServiceTypesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
if ($.isArray(args.rows) && args.rows.length === 1) {
var controllerServiceTypeIndex = args.rows[0];
var controllerServiceType = controllerServiceTypesGrid.getDataItem(controllerServiceTypeIndex);
// set the controller service type description
if (nf.Common.isDefinedAndNotNull(controllerServiceType)) {
if (nf.Common.isBlank(controllerServiceType.description)) {
$('#controller-service-type-description').attr('title', '').html('<span class="unset">No description specified</span>');
} else {
$('#controller-service-type-description').html(controllerServiceType.description).ellipsis();
}
// populate the dom
$('#controller-service-type-name').text(controllerServiceType.label).ellipsis();
$('#selected-controller-service-name').text(controllerServiceType.label);
$('#selected-controller-service-type').text(controllerServiceType.type);
// show the selected controller service
$('#controller-service-description-container').show();
}
}
});
// wire up the dataview to the grid
controllerServiceTypesData.onRowCountChanged.subscribe(function (e, args) {
controllerServiceTypesGrid.updateRowCount();
controllerServiceTypesGrid.render();
// update the total number of displayed processors
$('#displayed-controller-service-types').text(args.current);
});
controllerServiceTypesData.onRowsChanged.subscribe(function (e, args) {
controllerServiceTypesGrid.invalidateRows(args.rows);
controllerServiceTypesGrid.render();
});
controllerServiceTypesData.syncGridSelection(controllerServiceTypesGrid, true);
// hold onto an instance of the grid
$('#controller-service-types-table').data('gridInstance', controllerServiceTypesGrid);
// load the available controller services
$.ajax({
type: 'GET',
url: config.urls.controllerServiceTypes,
dataType: 'json'
}).done(function (response) {
var id = 0;
var tags = [];
// begin the update
controllerServiceTypesData.beginUpdate();
// go through each controller service type
$.each(response.controllerServiceTypes, function (i, documentedType) {
// add the documented type
controllerServiceTypesData.addItem({
id: id++,
label: nf.Common.substringAfterLast(documentedType.type, '.'),
type: documentedType.type,
description: nf.Common.escapeHtml(documentedType.description),
tags: documentedType.tags.join(', ')
});
// count the frequency of each tag for this type
$.each(documentedType.tags, function (i, tag) {
tags.push(tag.toLowerCase());
});
});
// end the udpate
controllerServiceTypesData.endUpdate();
// set the total number of processors
$('#total-controller-service-types, #displayed-controller-service-types').text(response.controllerServiceTypes.length);
// create the tag cloud
$('#controller-service-tag-cloud').tagcloud({
tags: tags,
select: applyControllerServiceTypeFilter,
remove: applyControllerServiceTypeFilter
});
}).fail(nf.Common.handleAjaxError);
// initialize the controller service dialog
$('#new-controller-service-dialog').modal({
headerText: 'Add Controller Service',
overlayBackground: false,
handler: {
close: function () {
// clear the selected row
clearSelectedControllerService();
// clear any filter strings
$('#controller-service-type-filter').addClass(config.styles.filterList).val(config.filterText);
// clear the tagcloud
$('#controller-service-tag-cloud').tagcloud('clearSelectedTags');
// reset the filter
applyControllerServiceTypeFilter();
// unselect any current selection
var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
controllerServiceTypesGrid.setSelectedRows([]);
controllerServiceTypesGrid.resetActiveCell();
}
}
});
};
/**
* Formatter for the name column.
*
* @param {type} row
* @param {type} cell
* @param {type} value
* @param {type} columnDef
* @param {type} dataContext
* @returns {String}
*/
var nameFormatter = function (row, cell, value, columnDef, dataContext) {
if (!dataContext.accessPolicy.canRead) {
return '<span class="blank">' + dataContext.id + '</span>';
}
return dataContext.component.name;
};
/**
* Formatter for the type column.
*
* @param {type} row
* @param {type} cell
* @param {type} value
* @param {type} columnDef
* @param {type} dataContext
* @returns {String}
*/
var typeFormatter = function (row, cell, value, columnDef, dataContext) {
if (!dataContext.accessPolicy.canRead) {
return '';
}
return nf.Common.substringAfterLast(dataContext.component.type, '.');
};
/**
* 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) {
if (sortDetails.columnId === 'moreDetails') {
var aBulletins = 0;
if (!nf.Common.isEmpty(a.bulletins)) {
aBulletins = a.bulletins.length;
}
var bBulletins = 0;
if (!nf.Common.isEmpty(b.bulletins)) {
bBulletins = b.bulletins.length;
}
return aBulletins - bBulletins;
} else if (sortDetails.columnId === 'type') {
var aType = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? nf.Common.substringAfterLast(a[sortDetails.columnId], '.') : '';
var bType = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? nf.Common.substringAfterLast(b[sortDetails.columnId], '.') : '';
return aType === bType ? 0 : aType > bType ? 1 : -1;
} else if (sortDetails.columnId === 'state') {
var aState = 'Invalid';
if (nf.Common.isEmpty(a.validationErrors)) {
aState = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
}
var bState = 'Invalid';
if (nf.Common.isEmpty(b.validationErrors)) {
bState = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
}
return aState === bState ? 0 : aState > bState ? 1 : -1;
} else {
var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
return aString === bString ? 0 : aString > bString ? 1 : -1;
}
};
// perform the sort
data.sort(comparer, sortDetails.sortAsc);
};
/**
* Initializes the controller services tab.
*
* @param {jQuery} serviceTable
*/
var initControllerServices = function (serviceTable) {
// more details formatter
var moreControllerServiceDetails = function (row, cell, value, columnDef, dataContext) {
if (!dataContext.accessPolicy.canRead) {
return '';
}
var markup = '<img src="images/iconDetails.png" title="View Details" class="pointer view-controller-service" style="margin-top: 5px; float: left;" />';
// always include a button to view the usage
markup += '<img src="images/iconUsage.png" title="Usage" class="pointer controller-service-usage" style="margin-left: 6px; margin-top: 3px; float: left;" />';
var hasErrors = !nf.Common.isEmpty(dataContext.component.validationErrors);
var hasBulletins = !nf.Common.isEmpty(dataContext.component.bulletins);
if (hasErrors) {
markup += '<img src="images/iconAlert.png" class="has-errors" style="margin-top: 4px; margin-left: 3px; float: left;" />';
}
if (hasBulletins) {
markup += '<img src="images/iconBulletin.png" class="has-bulletins" style="margin-top: 5px; margin-left: 5px; float: left;"/>';
}
if (hasErrors || hasBulletins) {
markup += '<span class="hidden row-id">' + nf.Common.escapeHtml(dataContext.id) + '</span>';
}
return markup;
};
var controllerServiceStateFormatter = function (row, cell, value, columnDef, dataContext) {
if (!dataContext.accessPolicy.canRead) {
return '';
}
// determine the appropriate label
var icon = '', label = '';
if (!nf.Common.isEmpty(dataContext.component.validationErrors)) {
icon = 'invalid';
label = 'Invalid';
} else {
if (dataContext.component.state === 'DISABLED') {
icon = 'disabled';
label = 'Disabled';
} else if (dataContext.component.state === 'DISABLING') {
icon = 'disabled';
label = 'Disabling';
} else if (dataContext.component.state === 'ENABLED') {
icon = 'enabled';
label = 'Enabled';
} else if (dataContext.component.state === 'ENABLING') {
icon = 'enabled';
label = 'Enabling';
}
}
// format the markup
var formattedValue = '<div class="' + icon + '" style="margin-top: 3px;"></div>';
return formattedValue + '<div class="status-text" style="margin-top: 2px; margin-left: 4px; float: left;">' + label + '</div>';
};
var controllerServiceActionFormatter = function (row, cell, value, columnDef, dataContext) {
var markup = '';
if (dataContext.accessPolicy.canRead && dataContext.accessPolicy.canWrite) {
if (dataContext.component.state === 'ENABLED' || dataContext.component.state === 'ENABLING') {
markup += '<img src="images/iconDisable.png" title="Disable" class="pointer disable-controller-service" style="margin-top: 2px;" />';
} else if (dataContext.component.state === 'DISABLED') {
markup += '<img src="images/iconEdit.png" title="Edit" class="pointer edit-controller-service" style="margin-top: 2px;" />';
// if there are no validation errors allow enabling
if (nf.Common.isEmpty(dataContext.component.validationErrors)) {
markup += '<img src="images/iconEnable.png" title="Enable" class="pointer enable-controller-service" style="margin-top: 2px; margin-left: 3px;"/>';
}
markup += '<img src="images/iconDelete.png" title="Remove" class="pointer delete-controller-service" style="margin-top: 2px; margin-left: 3px;" />';
}
if (dataContext.component.persistsState === true) {
markup += '<img src="images/iconViewState.png" title="View State" class="pointer view-state-controller-service" style="margin-top: 2px; margin-left: 3px;" />';
}
}
return markup;
};
// define the column model for the controller services table
var controllerServicesColumns = [
{id: 'moreDetails', name: '&nbsp;', resizable: false, formatter: moreControllerServiceDetails, sortable: true, width: 90, maxWidth: 90, toolTip: 'Sorts based on presence of bulletins'},
{id: 'name', name: 'Name', formatter: nameFormatter, sortable: true, resizable: true},
{id: 'type', name: 'Type', formatter: typeFormatter, sortable: true, resizable: true},
{id: 'state', name: 'State', formatter: controllerServiceStateFormatter, sortable: true, resizeable: true}
];
// action column should always be last
controllerServicesColumns.push({id: 'actions', name: '&nbsp;', resizable: false, formatter: controllerServiceActionFormatter, sortable: false, width: 90, maxWidth: 90});
// initialize the dataview
var controllerServicesData = new Slick.Data.DataView({
inlineFilters: false
});
controllerServicesData.setItems([]);
// initialize the sort
sort({
columnId: 'name',
sortAsc: true
}, controllerServicesData);
// initialize the grid
var controllerServicesGrid = new Slick.Grid(serviceTable, controllerServicesData, controllerServicesColumns, gridOptions);
controllerServicesGrid.setSelectionModel(new Slick.RowSelectionModel());
controllerServicesGrid.registerPlugin(new Slick.AutoTooltips());
controllerServicesGrid.setSortColumn('name', true);
controllerServicesGrid.onSort.subscribe(function (e, args) {
sort({
columnId: args.sortCol.field,
sortAsc: args.sortAsc
}, controllerServicesData);
});
// configure a click listener
controllerServicesGrid.onClick.subscribe(function (e, args) {
var target = $(e.target);
// get the service at this row
var controllerServiceEntity = controllerServicesData.getItem(args.row);
// determine the desired action
if (controllerServicesGrid.getColumns()[args.cell].id === 'actions') {
if (target.hasClass('edit-controller-service')) {
nf.ControllerService.showConfiguration(serviceTable, controllerServiceEntity);
} else if (target.hasClass('enable-controller-service')) {
nf.ControllerService.enable(serviceTable, controllerServiceEntity);
} else if (target.hasClass('disable-controller-service')) {
nf.ControllerService.disable(serviceTable, controllerServiceEntity);
} else if (target.hasClass('delete-controller-service')) {
nf.ControllerService.remove(serviceTable, controllerServiceEntity);
} else if (target.hasClass('view-state-controller-service')) {
nf.ComponentState.showState(controllerServiceEntity.component, controllerServiceEntity.state === 'DISABLED');
}
} else if (controllerServicesGrid.getColumns()[args.cell].id === 'moreDetails') {
if (target.hasClass('view-controller-service')) {
nf.ControllerService.showDetails(serviceTable, controllerServiceEntity);
} else if (target.hasClass('controller-service-usage')) {
// close the settings dialog
$('#shell-close-button').click();
// open the documentation for this controller service
nf.Shell.showPage('../nifi-docs/documentation?' + $.param({
select: nf.Common.substringAfterLast(controllerServiceEntity.component.type, '.')
})).done(function() {
nf.Settings.showSettings();
});
}
}
});
// wire up the dataview to the grid
controllerServicesData.onRowCountChanged.subscribe(function (e, args) {
controllerServicesGrid.updateRowCount();
controllerServicesGrid.render();
});
controllerServicesData.onRowsChanged.subscribe(function (e, args) {
controllerServicesGrid.invalidateRows(args.rows);
controllerServicesGrid.render();
});
controllerServicesData.syncGridSelection(controllerServicesGrid, true);
// hold onto an instance of the grid
serviceTable.data('gridInstance', controllerServicesGrid).on('mouseenter', 'div.slick-cell', function (e) {
var errorIcon = $(this).find('img.has-errors');
if (errorIcon.length && !errorIcon.data('qtip')) {
var serviceId = $(this).find('span.row-id').text();
// get the service item
var controllerServiceEntity = controllerServicesData.getItemById(serviceId);
// format the errors
var tooltip = nf.Common.formatUnorderedList(controllerServiceEntity.component.validationErrors);
// show the tooltip
if (nf.Common.isDefinedAndNotNull(tooltip)) {
errorIcon.qtip($.extend({
content: tooltip,
position: {
target: 'mouse',
viewport: $(window),
adjust: {
x: 8,
y: 8,
method: 'flipinvert flipinvert'
}
}
}, nf.Common.config.tooltipConfig));
}
}
var bulletinIcon = $(this).find('img.has-bulletins');
if (bulletinIcon.length && !bulletinIcon.data('qtip')) {
var taskId = $(this).find('span.row-id').text();
// get the task item
var controllerServiceEntity = controllerServicesData.getItemById(taskId);
// format the tooltip
var bulletins = nf.Common.getFormattedBulletins(controllerServiceEntity.component.bulletins);
var tooltip = nf.Common.formatUnorderedList(bulletins);
// show the tooltip
if (nf.Common.isDefinedAndNotNull(tooltip)) {
bulletinIcon.qtip($.extend({}, nf.Common.config.tooltipConfig, {
content: tooltip,
position: {
target: 'mouse',
viewport: $(window),
adjust: {
x: 8,
y: 8,
method: 'flipinvert flipinvert'
}
}
}));
}
}
});
};
/**
* Loads the controller services.
*
* @param {string} controllerServicesUri
* @param {jQuery} serviceTable
*/
var loadControllerServices = function (controllerServicesUri, serviceTable) {
return $.ajax({
type: 'GET',
url: controllerServicesUri,
dataType: 'json'
}).done(function (response) {
var services = [];
$.each(response.controllerServices, function (_, service) {
services.push($.extend({
bulletins: []
}, service));
});
nf.Common.cleanUpTooltips(serviceTable, 'img.has-errors');
nf.Common.cleanUpTooltips(serviceTable, 'img.has-bulletins');
var controllerServicesGrid = serviceTable.data('gridInstance');
var controllerServicesData = controllerServicesGrid.getData();
// update the controller services
controllerServicesData.setItems(services);
controllerServicesData.reSort();
controllerServicesGrid.invalidate();
});
};
return {
/**
* Initializes the status page.
*
* @param {jQuery} serviceTable
*/
init: function (serviceTable) {
if (!initialized) {
// initialize the new controller service dialog
initNewControllerServiceDialog();
// don't run this again
initialized = true;
}
// initialize the controller service table
initControllerServices(serviceTable);
},
/**
* Prompts for a new controller service.
*
* @param {string} controllerServicesUri
* @param {jQuery} serviceTable
*/
promptNewControllerService: function (controllerServicesUri, serviceTable) {
// update the keyhandler
$('#controller-service-type-filter').off('keyup').on('keyup', function (e) {
var code = e.keyCode ? e.keyCode : e.which;
if (code === $.ui.keyCode.ENTER) {
addSelectedControllerService(controllerServicesUri, serviceTable);
} else {
applyControllerServiceTypeFilter();
}
});
// update the button model and show the dialog
$('#new-controller-service-dialog').modal('setButtonModel', [{
buttonText: 'Add',
handler: {
click: function () {
addSelectedControllerService(controllerServicesUri, serviceTable);
}
}
}, {
buttonText: 'Cancel',
handler: {
click: function () {
$(this).modal('hide');
}
}
}]).modal('show');
var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
// remove previous dbl click handler
if (dblClick !== null) {
controllerServiceTypesGrid.onDblClick.unsubscribe(dblClick);
}
// update the dbl click handler and subsrcibe
dblClick = function(e, args) {
var controllerServiceType = controllerServiceTypesGrid.getDataItem(args.row);
addControllerService(controllerServicesUri, serviceTable, controllerServiceType.type);
};
controllerServiceTypesGrid.onDblClick.subscribe(dblClick);
// reset the canvas size after the dialog is shown
if (nf.Common.isDefinedAndNotNull(controllerServiceTypesGrid)) {
controllerServiceTypesGrid.setSelectedRows([0]);
controllerServiceTypesGrid.resizeCanvas();
}
// set the initial focus
$('#controller-service-type-filter').focus();
},
/**
* Update the size of the grid based on its container's current size.
*
* @param {jQuery} serviceTable
*/
resetTableSize: function (serviceTable) {
var controllerServicesGrid = serviceTable.data('gridInstance');
if (nf.Common.isDefinedAndNotNull(controllerServicesGrid)) {
controllerServicesGrid.resizeCanvas();
}
},
/**
* Loads the settings.
*
* @param {string} controllerServicesUri
* @param {jQuery} serviceTable
*/
loadControllerServices: function (controllerServicesUri, serviceTable) {
return loadControllerServices(controllerServicesUri, serviceTable);
},
/**
* Sets the controller service and reporting task bulletins in their respective tables.
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceBulletins
*/
setBulletins: function(serviceTable, controllerServiceBulletins) {
// controller services
var controllerServicesGrid = serviceTable.data('gridInstance');
var controllerServicesData = controllerServicesGrid.getData();
controllerServicesData.beginUpdate();
// if there are some bulletins process them
if (!nf.Common.isEmpty(controllerServiceBulletins)) {
var controllerServiceBulletinsBySource = d3.nest()
.key(function(d) { return d.sourceId; })
.map(controllerServiceBulletins, d3.map);
controllerServiceBulletinsBySource.forEach(function(sourceId, sourceBulletins) {
var controllerService = controllerServicesData.getItemById(sourceId);
if (nf.Common.isDefinedAndNotNull(controllerService)) {
controllerServicesData.updateItem(sourceId, $.extend(controllerService, {
bulletins: sourceBulletins
}));
}
});
} else {
// if there are no bulletins clear all
var controllerServices = controllerServicesData.getItems();
$.each(controllerServices, function(_, controllerService) {
controllerServicesData.updateItem(controllerService.id, $.extend(controllerService, {
bulletins: []
}));
});
}
controllerServicesData.endUpdate();
}
};
}());

View File

@ -19,24 +19,47 @@
nf.ProcessGroupConfiguration = (function () { nf.ProcessGroupConfiguration = (function () {
return { var config = {
init: function () { filterText: 'Filter',
$('#process-group-configuration').modal({ styles: {
headerText: 'Configure Process Group', filterList: 'filter-list'
overlayBackground: true, },
buttons: [{ urls: {
buttonText: 'Apply', api: '../nifi-api'
handler: { }
click: function () { };
// get the process group data to reference the uri
var processGroupId = $('#process-group-id').text();
var processGroupData = d3.select('#id-' + processGroupId).datum();
/**
* Initializes the general tab.
*/
var initGeneral = function () {
};
/**
* Gets the controller services table.
*
* @returns {*|jQuery|HTMLElement}
*/
var getControllerServicesTable = function () {
return $('#process-group-controller-services-table');
};
/**
* Saves the configuration for the specified group.
*
* @param version
* @param groupId
*/
var saveConfiguration = function (version, groupId) {
// build the entity // build the entity
var entity = { var entity = {
'revision': nf.Client.getRevision(processGroupData), 'revision': nf.Client.getRevision({
'revision': {
'version': version
}
}),
'component': { 'component': {
'id': processGroupId, 'id': groupId,
'name': $('#process-group-name').val(), 'name': $('#process-group-name').val(),
'comments': $('#process-group-comments').val() 'comments': $('#process-group-comments').val()
} }
@ -46,61 +69,221 @@ nf.ProcessGroupConfiguration = (function () {
$.ajax({ $.ajax({
type: 'PUT', type: 'PUT',
data: JSON.stringify(entity), data: JSON.stringify(entity),
url: processGroupData.component.uri, url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId),
dataType: 'json', dataType: 'json',
contentType: 'application/json' contentType: 'application/json'
}).done(function (response) { }).done(function (response) {
// refresh the process group // refresh the process group if necessary
if (response.accessPolicy.canRead && response.component.parentGroupId === nf.Canvas.getGroupId()) {
nf.ProcessGroup.set(response); nf.ProcessGroup.set(response);
}
// close the details panel // show the result dialog
$('#process-group-configuration').modal('hide'); nf.Dialog.showOkDialog({
}).fail(function (xhr, status, error) { dialogContent: 'Process group configuration successfully saved.',
// close the details panel overlayBackground: false
$('#process-group-configuration').modal('hide');
// handle the error
nf.Common.handleAjaxError(xhr, status, error);
}); });
// update the click listener for the updated revision
$('#process-group-configuration-save').off('click').on('click', function () {
saveConfiguration(response.revision.version, groupId);
});
}).fail(nf.Common.handleAjaxError);
};
/**
* Loads the configuration for the specified process group.
*
* @param {string} groupId
*/
var loadConfiguration = function (groupId) {
var setUnauthorizedText = function () {
$('#read-only-process-group-name').addClass('unset').text('Unauthorized');
$('#read-only-process-group-comments').addClass('unset').text('Unauthorized');
};
var setEditable = function (editable) {
if (editable) {
$('#process-group-configuration div.editable').show();
$('#process-group-configuration div.read-only').hide();
$('#process-group-configuration-save').show();
} else {
$('#process-group-configuration div.editable').hide();
$('#process-group-configuration div.read-only').show();
$('#process-group-configuration-save').hide();
} }
};
var processGroup = $.Deferred(function (deferred) {
$.ajax({
type: 'GET',
url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId),
dataType: 'json'
}).done(function (response) {
if (response.accessPolicy.canWrite) {
var processGroup = response.component;
// populate the process group settings
$('#process-group-id').text(processGroup.id);
$('#process-group-name').removeClass('unset').val(processGroup.name);
$('#process-group-comments').removeClass('unset').val(processGroup.comments);
setEditable(true);
// register the click listener for the save button
$('#process-group-configuration-save').off('click').on('click', function () {
saveConfiguration(response.revision.version, processGroupResponse.id);
});
} else {
if (response.accessPolicy.canRead) {
// populate the process group settings
$('#read-only-process-group-name').removeClass('unset').text(response.component.name);
$('#read-only-process-group-comments').removeClass('unset').text(response.component.comments);
} else {
setUnauthorizedText();
} }
}, {
buttonText: 'Cancel', setEditable(false);
handler: {
click: function () {
$('#process-group-configuration').modal('hide');
} }
deferred.resolve();
}).fail(function (xhr, status, error) {
if (xhr.status === 403) {
setUnauthorizedText();
setEditable(false);
deferred.resolve();
} else {
deferred.reject(xhr, status, error);
} }
}], });
handler: { }).promise();
close: function () {
// clear the process group details // load the controller services
$('#process-group-id').text(''); var controllerServicesUri = config.urls.api + '/flow/process-groups/' + encodeURIComponent(groupId) + '/controller-services';
var controllerServices = nf.ControllerServices.loadControllerServices(controllerServicesUri, getControllerServicesTable());
// wait for everything to complete
return $.when(processGroup, controllerServices).fail(nf.Common.handleAjaxError);
};
/**
* Shows the process group configuration.
*/
var showConfiguration = function () {
// show the configuration dialog
nf.Shell.showContent('#process-group-configuration').done(function () {
reset();
});
// adjust the table size
nf.ProcessGroupConfiguration.resetTableSize();
};
/**
* Resets the process group configuration dialog.
*/
var reset = function () {
// reset button state
$('#process-group-configuration-save').mouseout();
// reset the fields
$('#process-group-name').val(''); $('#process-group-name').val('');
$('#process-group-comments').val(''); $('#process-group-comments').val('');
};
return {
/**
* Initializes the settings page.
*/
init: function () {
// initialize the process group configuration tabs
$('#process-group-configuration-tabs').tabbs({
tabStyle: 'settings-tab',
selectedTabStyle: 'settings-selected-tab',
tabs: [{
name: 'General',
tabContentId: 'general-process-group-configuration-tab-content'
}, {
name: 'Controller Services',
tabContentId: 'process-group-controller-services-tab-content'
}],
select: function () {
var tab = $(this).text();
if (tab === 'General') {
$('#add-process-group-configuration-controller-service').hide();
} else {
$('#add-process-group-configuration-controller-service').show();
// update the tooltip on the button
$('#add-process-group-configuration-controller-service').attr('title', function () {
if (tab === 'Controller Services') {
return 'Create a new controller service';
}
});
// resize the table
nf.ProcessGroupConfiguration.resetTableSize();
} }
} }
}); });
// settings refresh button...
nf.Common.addHoverEffect('#process-group-configuration-refresh-button', 'button-refresh', 'button-refresh-hover');
// handle window resizing
$(window).on('resize', function (e) {
nf.ProcessGroupConfiguration.resetTableSize();
});
// initialize each tab
initGeneral();
nf.ControllerServices.init(getControllerServicesTable());
}, },
/** /**
* Shows the details for the specified selection. * Update the size of the grid based on its container's current size.
*
* @argument {selection} selection The selection
*/ */
showConfiguration: function (selection) { resetTableSize: function () {
// if the specified selection is a processor, load its properties nf.ControllerServices.resetTableSize(getControllerServicesTable());
if (nf.CanvasUtils.isProcessGroup(selection)) { },
var selectionData = selection.datum();
// populate the process group settings /**
$('#process-group-id').text(selectionData.id); * Shows the settings dialog.
$('#process-group-name').val(selectionData.component.name); */
$('#process-group-comments').val(selectionData.component.comments); showConfiguration: function (groupId) {
// update the click listener
$('#process-group-configuration-refresh-button').off('click').on('click', function () {
loadConfiguration(groupId).done(showConfiguration);
});
// show the details // update the new controller service click listener
$('#process-group-configuration').modal('show'); $('#add-process-group-configuration-controller-service').off('click').on('click', function () {
var selectedTab = $('#process-group-configuration-tabs li.settings-selected-tab').text();
if (selectedTab === 'Controller Services') {
var controllerServicesUri = config.urls.api + '/process-groups/' + encodeURIComponent(groupId) + '/controller-services';
nf.ControllerServices.promptNewControllerService(controllerServicesUri, getControllerServicesTable());
} }
});
// load the configuration
return loadConfiguration(groupId).done(showConfiguration);
},
/**
* Selects the specified controller service.
*
* @param {string} controllerServiceId
*/
selectControllerService: function (controllerServiceId) {
var controllerServiceGrid = getControllerServicesTable().data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// select the desired service
var row = controllerServiceData.getRowById(controllerServiceId);
controllerServiceGrid.setSelectedRows([row]);
controllerServiceGrid.scrollRowIntoView(row);
// select the controller services tab
$('#process-group-configuration-tabs').find('li:eq(1)').click();
} }
}; };
}()); }());

View File

@ -24,6 +24,15 @@ nf.ReportingTask = (function () {
readOnly: 'read-only' readOnly: 'read-only'
}; };
/**
* Gets the controller services table.
*
* @returns {*|jQuery|HTMLElement}
*/
var getControllerServicesTable = function () {
return $('#controller-services-table');
};
/** /**
* Handle any expected reporting task configuration errors. * Handle any expected reporting task configuration errors.
* *
@ -205,7 +214,7 @@ nf.ReportingTask = (function () {
}).done(function (response) { }).done(function (response) {
// update the task // update the task
renderReportingTask(response); renderReportingTask(response);
nf.ControllerService.reloadReferencedServices(response.component); nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.component);
}).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError);
}; };
@ -464,7 +473,7 @@ nf.ReportingTask = (function () {
// save the reporting task // save the reporting task
saveReportingTask(reportingTaskEntity).done(function (response) { saveReportingTask(reportingTaskEntity).done(function (response) {
// reload the reporting task // reload the reporting task
nf.ControllerService.reloadReferencedServices(response.component); nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.component);
// close the details panel // close the details panel
$('#reporting-task-configuration').modal('hide'); $('#reporting-task-configuration').modal('hide');
@ -497,7 +506,7 @@ nf.ReportingTask = (function () {
nf.CustomUi.showCustomUi($('#reporting-task-id').text(), reportingTask.customUiUrl, true).done(function () { nf.CustomUi.showCustomUi($('#reporting-task-id').text(), reportingTask.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the reporting task // once the custom ui is closed, reload the reporting task
nf.ReportingTask.reload(reportingTaskEntity.id).done(function (response) { nf.ReportingTask.reload(reportingTaskEntity.id).done(function (response) {
nf.ControllerService.reloadReferencedServices(response.reportingTask); nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.reportingTask);
}); });
// show the settings // show the settings

View File

@ -337,8 +337,8 @@ nf.Common = (function () {
return; return;
} }
// status code 400, 404, and 409 are expected response codes for common errors. // status code 400, 403, 404, and 409 are expected response codes for common errors.
if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) { if (xhr.status === 400 || xhr.status === 403 || xhr.status === 404 || xhr.status === 409) {
nf.Dialog.showOkDialog({ nf.Dialog.showOkDialog({
dialogContent: nf.Common.escapeHtml(xhr.responseText), dialogContent: nf.Common.escapeHtml(xhr.responseText),
overlayBackground: false overlayBackground: false
@ -360,14 +360,7 @@ nf.Common = (function () {
} else if (xhr.status === 401) { } else if (xhr.status === 401) {
$('#message-title').text('Unauthorized'); $('#message-title').text('Unauthorized');
if ($.trim(xhr.responseText) === '') { if ($.trim(xhr.responseText) === '') {
$('#message-content').text('Authorization is required to use this NiFi.'); $('#message-content').text('Authentication is required to use this NiFi.');
} else {
$('#message-content').text(xhr.responseText);
}
} else if (xhr.status === 403) {
$('#message-title').text('Access Denied');
if ($.trim(xhr.responseText) === '') {
$('#message-content').text('Unable to authorize you to use this NiFi.');
} else { } else {
$('#message-content').text(xhr.responseText); $('#message-content').text(xhr.responseText);
} }