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;
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.adapters.XmlJavaTypeAdapter;
import org.apache.nifi.web.api.dto.util.TimeAdapter;
import java.util.Date;
/**
* Details for the controller configuration.
@ -28,13 +29,10 @@ import org.apache.nifi.web.api.dto.util.TimeAdapter;
@XmlType(name = "config")
public class ControllerConfigurationDTO {
private String name;
private String comments;
private Integer maxTimerDrivenThreadCount;
private Integer maxEventDrivenThreadCount;
private Long autoRefreshIntervalSeconds;
private Boolean siteToSiteSecure;
private Date currentTime;
private Integer timeOffset;
@ -67,34 +65,6 @@ public class ControllerConfigurationDTO {
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
*/
@ -110,21 +80,6 @@ public class ControllerConfigurationDTO {
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
*/

View File

@ -17,6 +17,7 @@
package org.apache.nifi.web.api.entity;
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.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.
*/
@XmlRootElement(name = "controllerConfigurationEntity")
public class ControllerConfigurationEntity extends ComponentEntity {
public class ControllerConfigurationEntity extends Entity {
private ControllerConfigurationDTO config;
private RevisionDTO revision;
private AccessPolicyDTO accessPolicy;
/**
* @return revision for this request/response
@ -65,4 +67,19 @@ public class ControllerConfigurationEntity extends ComponentEntity {
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import org.apache.nifi.action.Action;
import org.apache.nifi.action.Component;
import org.apache.nifi.action.FlowChangeAction;
@ -33,6 +30,10 @@ import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
/**
* Audits configuration changes to the controller.
*/
@ -41,112 +42,6 @@ public class ControllerAuditor extends NiFiAuditor {
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.
*
@ -189,7 +84,7 @@ public class ControllerAuditor extends NiFiAuditor {
configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName());
configAction.setSourceName("Flow Controller");
configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails);
actions.add(configAction);
@ -242,7 +137,7 @@ public class ControllerAuditor extends NiFiAuditor {
configAction.setOperation(Operation.Configure);
configAction.setTimestamp(new Date());
configAction.setSourceId("Flow Controller");
configAction.setSourceName(controllerFacade.getName());
configAction.setSourceName("Flow Controller");
configAction.setSourceType(Component.Controller);
configAction.setActionDetails(configDetails);
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.StatusHistoryDTO;
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.ControllerServiceReferencingComponentsEntity;
import org.apache.nifi.web.api.entity.FlowEntity;
@ -262,7 +263,7 @@ public interface NiFiServiceFacade {
*
* @return Controller configuration transfer object
*/
ControllerConfigurationDTO getControllerConfiguration();
ControllerConfigurationEntity getControllerConfiguration();
/**
* Updates the configuration for this controller.
@ -271,7 +272,7 @@ public interface NiFiServiceFacade {
* @param controllerConfigurationDTO 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.

View File

@ -45,6 +45,7 @@ import org.apache.nifi.connectable.Funnel;
import org.apache.nifi.connectable.Port;
import org.apache.nifi.controller.ConfiguredComponent;
import org.apache.nifi.controller.Counter;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.ProcessorNode;
import org.apache.nifi.controller.ReportingTaskNode;
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.BulletinQuery;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
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.StatusHistoryDTO;
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.ControllerServiceReferencingComponentEntity;
import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity;
@ -183,9 +184,7 @@ import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -886,32 +885,25 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
@Override
public ConfigurationSnapshot<ControllerConfigurationDTO> updateControllerConfiguration(final Revision revision, final ControllerConfigurationDTO controllerConfigurationDTO) {
final Supplier<ControllerConfigurationDTO> daoUpdate = () -> {
// update the controller configuration through the proxy
if (controllerConfigurationDTO.getName() != null) {
controllerFacade.setName(controllerConfigurationDTO.getName());
}
if (controllerConfigurationDTO.getComments() != null) {
controllerFacade.setComments(controllerConfigurationDTO.getComments());
}
if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) {
controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount());
}
if (controllerConfigurationDTO.getMaxEventDrivenThreadCount() != null) {
controllerFacade.setMaxEventDrivenThreadCount(controllerConfigurationDTO.getMaxEventDrivenThreadCount());
}
return controllerConfigurationDTO;
};
public ControllerConfigurationEntity updateControllerConfiguration(final Revision revision, final ControllerConfigurationDTO controllerConfigurationDTO) {
final RevisionUpdate<ControllerConfigurationDTO> updatedComponent = updateComponent(
revision,
controllerFacade,
daoUpdate,
controller -> getControllerConfiguration());
() -> {
if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) {
controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount());
}
if (controllerConfigurationDTO.getMaxEventDrivenThreadCount() != null) {
controllerFacade.setMaxEventDrivenThreadCount(controllerConfigurationDTO.getMaxEventDrivenThreadCount());
}
return new ConfigurationSnapshot<>(updatedComponent.getLastModification().getRevision().getVersion());
return controllerConfigurationDTO;
},
controller -> dtoFactory.createControllerConfigurationDto(controllerFacade, properties.getAutoRefreshInterval()));
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade);
final RevisionDTO updateRevision = dtoFactory.createRevisionDTO(updatedComponent.getLastModification());
return entityFactory.createControllerConfigurationEntity(updatedComponent.getComponent(), updateRevision, accessPolicy);
}
@Override
@ -1266,9 +1258,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
*
* @return a RevisionUpdate that represents the updated configuration
*/
private <D, C> RevisionUpdate<D> createComponent(final ComponentDTO componentDto,
final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
private <D, C> RevisionUpdate<D> createComponent(final ComponentDTO componentDto, final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
final String modifier = NiFiUserUtils.getNiFiUserName();
// ensure id is set
@ -1638,6 +1628,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override
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(
controllerServiceDTO,
() -> {
@ -1645,7 +1638,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerServiceNode controllerService = controllerServiceDAO.createControllerService(controllerServiceDTO);
// TODO - this logic should be part of the controllerServiceDAO
final ProcessGroup group = processGroupDAO.getProcessGroup(groupId);
final ProcessGroup group = processGroupDAO.getProcessGroup(normalizedGroupId);
group.addControllerService(controllerService);
return controllerService;
},
@ -2350,8 +2343,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final ControllerDTO controllerDTO = new ControllerDTO();
controllerDTO.setId(controllerFacade.getRootGroupId());
controllerDTO.setInstanceId(controllerFacade.getInstanceId());
controllerDTO.setName(controllerFacade.getName());
controllerDTO.setComments(controllerFacade.getComments());
controllerDTO.setInputPorts(inputPortDtos);
controllerDTO.setOutputPorts(outputPortDtos);
controllerDTO.setInputPortCount(inputPorts.size());
@ -2374,29 +2365,13 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}
@Override
public ControllerConfigurationDTO getControllerConfiguration() {
ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO();
controllerConfig.setName(controllerFacade.getName());
controllerConfig.setComments(controllerFacade.getComments());
controllerConfig.setMaxTimerDrivenThreadCount(controllerFacade.getMaxTimerDrivenThreadCount());
controllerConfig.setMaxEventDrivenThreadCount(controllerFacade.getMaxEventDrivenThreadCount());
// 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;
public ControllerConfigurationEntity getControllerConfiguration() {
return revisionManager.get(FlowController.class.getSimpleName(), rev -> {
final ControllerConfigurationDTO dto = dtoFactory.createControllerConfigurationDto(controllerFacade, properties.getAutoRefreshInterval());
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(controllerFacade);
final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
return entityFactory.createControllerConfigurationEntity(dto, revision, accessPolicy);
});
}
@Override
@ -2656,9 +2631,18 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override
public Set<ControllerServiceEntity> getControllerServices(String groupId) {
// TODO - move this logic into the ControllerServiceDAO
final ProcessGroup group = processGroupDAO.getProcessGroup(groupId);
final Set<ControllerServiceNode> serviceNodes = group.getControllerServices(true);
final Set<String> serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet());
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);
serviceNodes = group.getControllerServices(true);
serviceIds = serviceNodes.stream().map(service -> service.getIdentifier()).collect(Collectors.toSet());
}
return revisionManager.get(serviceIds, () -> {
return serviceNodes.stream()

View File

@ -16,10 +16,43 @@
*/
package org.apache.nifi.web.api;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
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;
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.ws.rs.Consumes;
@ -36,41 +69,10 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
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.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;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* RESTful endpoint for managing a Flow Controller.
@ -85,12 +87,35 @@ public class ControllerResource extends ApplicationResource {
private NiFiServiceFacade serviceFacade;
private WebClusterManager clusterManager;
private NiFiProperties properties;
private Authorizer authorizer;
private ReportingTaskResource reportingTaskResource;
private ControllerServiceResource controllerServiceResource;
@Context
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
* 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() {
// TODO
// authorizeController(RequestAction.READ);
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
}
final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration();
// create the response entity
final ControllerConfigurationEntity entity = new ControllerConfigurationEntity();
entity.setConfig(controllerConfig);
// generate the response
final ControllerConfigurationEntity entity = serviceFacade.getControllerConfiguration();
return clusterContext(generateOkResponse(entity)).build();
}
@ -374,31 +395,19 @@ public class ControllerResource extends ApplicationResource {
return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), configEntity, getHeaders()).getResponse();
}
final RevisionDTO revisionDto = configEntity.getRevision();
final Revision revision = new Revision(revisionDto.getVersion(), revisionDto.getClientId(), "controller");
// handle expects request (usually from the cluster manager)
final String expects = httpServletRequest.getHeader(RequestReplicator.REQUEST_VALIDATION_HTTP_HEADER);
if (expects != null) {
return generateContinueResponse().build();
}
final ConfigurationSnapshot<ControllerConfigurationDTO> controllerResponse
= 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();
final Revision revision = getRevision(configEntity.getRevision(), FlowController.class.getSimpleName());
return withWriteLock(
serviceFacade,
revision,
lookup -> {
authorizeController(RequestAction.WRITE);
},
null,
() -> {
final ControllerConfigurationEntity entity = serviceFacade.updateControllerConfiguration(revision, configEntity.getConfig());
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**x
@ -519,26 +528,27 @@ public class ControllerResource extends ApplicationResource {
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
* new one will be generated. This value (whether specified or generated) is
* included in the response.
* @return A reportingTasksEntity.
* @param httpServletRequest request
* @param controllerServiceEntity A controllerServiceEntity.
* @return A controllerServiceEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("reporting-tasks")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@Path("controller-services")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Gets all reporting tasks",
response = ReportingTasksEntity.class,
value = "Creates a new controller service",
response = ControllerServiceEntity.class,
authorizations = {
@Authorization(value = "Read Only", type = "ROLE_MONITOR"),
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM"),
@Authorization(value = "Administrator", type = "ROLE_ADMIN")
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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.")
}
)
public Response getReportingTasks(
public Response createControllerService(
@Context final HttpServletRequest httpServletRequest,
@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) {
value = "The controller service configuration details.",
required = true
) final ControllerServiceEntity controllerServiceEntity) {
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
if (controllerServiceEntity == null || controllerServiceEntity.getComponent() == null) {
throw new IllegalArgumentException("Controller service details must be specified.");
}
// get all the reporting tasks
final Set<ReportingTaskEntity> reportingTasks = serviceFacade.getReportingTasks();
reportingTaskResource.populateRemainingReportingTaskEntitiesContent(reportingTasks);
if (controllerServiceEntity.getComponent().getId() != null) {
throw new IllegalArgumentException("Controller service ID cannot be specified.");
}
// create the response entity
final ReportingTasksEntity entity = new ReportingTasksEntity();
entity.setReportingTasks(reportingTasks);
if (StringUtils.isBlank(controllerServiceEntity.getComponent().getType())) {
throw new IllegalArgumentException("The type of controller service to create must be specified.");
}
// generate the response
return clusterContext(generateOkResponse(entity)).build();
if (properties.isClusterManager()) {
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
@ -586,8 +618,15 @@ public class ControllerResource extends ApplicationResource {
this.reportingTaskResource = reportingTaskResource;
}
public void setControllerServiceResource(ControllerServiceResource controllerServiceResource) {
this.controllerServiceResource = controllerServiceResource;
}
public void setProperties(NiFiProperties 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.BulletinBoardDTO;
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.RevisionDTO;
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.ProcessorTypesEntity;
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.ReportingTasksEntity;
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
import org.apache.nifi.web.api.entity.SearchResultsEntity;
import org.apache.nifi.web.api.entity.StatusHistoryEntity;
@ -141,6 +142,7 @@ public class FlowResource extends ApplicationResource {
private TemplateResource templateResource;
private ProcessGroupResource processGroupResource;
private ControllerServiceResource controllerServiceResource;
private ReportingTaskResource reportingTaskResource;
/**
* Populates the remaining fields in the specified process group.
@ -186,6 +188,9 @@ public class FlowResource extends ApplicationResource {
return flowStructure;
}
/**
* Authorizes access to the flow.
*/
private void authorizeFlow() {
final NiFiUser user = NiFiUserUtils.getNiFiUser();
@ -269,6 +274,56 @@ public class FlowResource extends ApplicationResource {
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.
*
@ -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.")
}
)
public Response getControllerServices(
public Response getControllerServicesFromGroup(
@ApiParam(
value = "The process group id.",
required = true
@ -320,6 +375,64 @@ public class FlowResource extends ApplicationResource {
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.
*
@ -358,8 +471,6 @@ public class FlowResource extends ApplicationResource {
@PathParam("id") String id,
ScheduleComponentsEntity scheduleComponentsEntity) {
authorizeFlow();
// ensure the same id is being used
if (!id.equals(scheduleComponentsEntity.getId())) {
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,
revisions,
lookup -> {
// ensure access to the flow
authorizeFlow();
// ensure access to every component being scheduled
componentsToSchedule.keySet().forEach(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();
}
final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration();
// create the about dto
final AboutDTO aboutDTO = new AboutDTO();
aboutDTO.setTitle(controllerConfig.getName());
aboutDTO.setTitle("NiFi"); // TODO - where to load title from
aboutDTO.setVersion(properties.getUiTitle());
aboutDTO.setUri(generateResourceUri());
@ -1853,6 +1965,10 @@ public class FlowResource extends ApplicationResource {
this.controllerServiceResource = controllerServiceResource;
}
public void setReportingTaskResource(ReportingTaskResource reportingTaskResource) {
this.reportingTaskResource = reportingTaskResource;
}
public void setAuthorizer(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.RemoteProcessGroupStatusSnapshotDTO;
import org.apache.nifi.web.api.entity.FlowBreadcrumbEntity;
import org.apache.nifi.web.controller.ControllerFacade;
import org.apache.nifi.web.revision.RevisionManager;
import javax.ws.rs.WebApplicationException;
@ -154,6 +155,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
@ -174,6 +176,22 @@ public final class DtoFactory {
private EntityFactory entityFactory;
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.
*
@ -1143,6 +1161,7 @@ public final class DtoFactory {
public ControllerServiceDTO createControllerServiceDto(final ControllerServiceNode controllerServiceNode) {
final ControllerServiceDTO dto = new ControllerServiceDTO();
dto.setId(controllerServiceNode.getIdentifier());
dto.setParentGroupId(controllerServiceNode.getProcessGroup() == null ? null : controllerServiceNode.getProcessGroup().getIdentifier());
dto.setName(controllerServiceNode.getName());
dto.setType(controllerServiceNode.getControllerServiceImplementation().getClass().getName());
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.RemoteProcessGroupStatusDTO;
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.ControllerServiceReferencingComponentEntity;
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 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) {
final ProcessGroupFlowEntity entity = new ProcessGroupFlowEntity();
entity.setProcessGroupFlow(dto);

View File

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

View File

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

View File

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

View File

@ -180,7 +180,6 @@ public class ReadOnlyAccessControlTest {
ControllerConfigurationEntity entity = response.getEntity(ControllerConfigurationEntity.class);
Assert.assertNotNull(entity);
Assert.assertNotNull(entity.getConfig());
Assert.assertEquals("NiFi Flow", entity.getConfig().getName());
Assert.assertEquals(10, entity.getConfig().getMaxTimerDrivenThreadCount().intValue());
Assert.assertEquals(5, entity.getConfig().getMaxEventDrivenThreadCount().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-component-state.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-processor-configuration.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-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-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-processor-configuration.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 id="operation-buttons">
<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">
<button ng-click="appCtrl.nf.Actions['enable'](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())));">
<i class="graph-control-action-icon icon icon-template"></i></button>
</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">
<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());">
@ -124,9 +132,7 @@
ng-disabled="!appCtrl.nf.Clipboard.isCopied()">
<i class="graph-control-action-icon fa fa-paste"></i></button>
</div>
<div class="clear"></div>
</div>
<div style="margin-top: 5px;">
<div class="button-spacer-large">&nbsp;</div>
<div id="operate-group" class="action-button" title="Group">
<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());">

View File

@ -15,24 +15,49 @@
limitations under the License.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="process-group-configuration" nf-draggable="{containment: 'parent', handle: '.dialog-header'}">
<div class="dialog-content">
<div class="setting">
<div class="setting-name">Name</div>
<div class="setting-field">
<input type="text" id="process-group-name" name="process-group-name" class="process-group-field"/>
<div id="process-group-configuration">
<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="setting">
<div class="setting-name">Id</div>
<div class="setting-field">
<span id="process-group-id"></span>
<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-name">Process group name</div>
<div class="editable setting-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 class="setting">
<div class="setting-name">Process group comments</div>
<div class="editable setting-field">
<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 class="editable settings-buttons">
<div id="process-group-configuration-save" class="button">Apply</div>
<div class="clear"></div>
</div>
</div>
</div>
</div>
<div class="setting">
<div class="setting-name">Comments</div>
<div class="setting-field">
<textarea cols="30" rows="4" id="process-group-comments" name="process-group-comments" class="process-group-field"></textarea>
<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>

View File

@ -16,43 +16,22 @@
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="settings">
<div id="settings-header-text">NiFi Settings</div>
<div id="settings-container">
<div id="settings-tabs-container">
<div id="settings-tabs"></div>
<div id="settings-refresh-button" class="pointer" title="Refresh"></div>
<div id="settings-last-refreshed-container">
<div id="settings-header-text" class="settings-header-text">NiFi Settings</div>
<div class="settings-container">
<div class="settings-tabs-container">
<div id="settings-tabs" class="settings-tabs"></div>
<div id="settings-refresh-button" class="pointer settings-refresh-button" title="Refresh"></div>
<div class="settings-last-refreshed-container">
Last updated:&nbsp;<span id="settings-last-refreshed"></span>
</div>
<div id="settings-refresh-required-icon" class="hidden"></div>
<div id="settings-loading-container" class="loading-container"></div>
<div id="new-service-or-task" class="add-icon-bg"></div>
<div class="clear"></div>
</div>
<div id="settings-tab-background"></div>
<div id="settings-tabs-content">
<div class="settings-tab-background"></div>
<div>
<div id="general-settings-tab-content" class="configuration-tab">
<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-name">
Maximum timer driven thread count
@ -60,6 +39,8 @@
</div>
<div class="editable setting-field">
<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 class="read-only setting-field">
<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>
</div>
</div>
<div id="settings-buttons" class="editable">
<div class="editable settings-buttons">
<div id="settings-save" class="button">Apply</div>
<div class="clear"></div>
</div>

View File

@ -14,11 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* flow status styles positioned at the top of the graph */
#flow-status {
height: 32px;
height: 33px;
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 {

View File

@ -24,6 +24,7 @@
left: 0px;
bottom: 0px;
right: 0px;
background-color: #f9fafb;
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);
z-index: 1;
@ -64,6 +65,11 @@ g.component rect.body.unauthorized {
fill: #f4f6f7;
}
g.component rect.border {
stroke: rgba(0,0,0,0.25);
stroke-width: 1;
}
g.component rect.border.unauthorized {
stroke-width: 1.5;
stroke: #ba554a;

View File

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

View File

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

View File

@ -14,17 +14,64 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#process-group-configuration {
z-index: 1301;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
display: none;
width: 400px;
height: 320px;
padding: 20px;
}
.process-group-field {
width: 369px;
#add-process-group-configuration-controller-service {
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;
}

View File

@ -25,13 +25,13 @@
padding: 20px;
}
#settings-header-text {
div.settings-header-text {
height: 35px;
font-size: 16px;
font-weight: bold;
}
#settings-container {
div.settings-container {
margin-top: 18px;
}
@ -45,11 +45,11 @@
/* settings tabs */
#settings-tabs-container {
div.settings-tabs-container {
border-bottom: 2px solid #666;
}
#settings-tabs {
div.settings-tabs {
float: left;
}
@ -78,7 +78,7 @@
border-right: 1px solid #666;
}
#settings-tab-background {
div.settings-tab-background {
height: 200px;
margin-top: 1px;
background-color: transparent;
@ -86,7 +86,7 @@
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;
}
@ -105,13 +105,13 @@ span.sorted {
text-decoration: underline;
}
#settings-refresh-button {
div.settings-refresh-button {
height: 24px;
width: 26px;
float: left;
}
#settings-last-refreshed-container {
div.settings-last-refreshed-container {
float: left;
color: #666;
font-weight: normal;
@ -134,15 +134,6 @@ span.sorted {
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-settings {
@ -150,23 +141,19 @@ span.sorted {
margin-left: 10px;
}
#general-settings input, #general-settings textarea {
#general-settings input {
font-size: 11px !important;
font-family: Verdana;
width: 350px;
vertical-align: middle;
}
#data-flow-comments-field {
height: 100px;
}
#settings-buttons {
margin-left: 304px;
div.settings-buttons {
margin-left: 310px;
margin-top: 10px;
}
#settings-save {
div.settings-buttons div.button {
float: left;
}

View File

@ -1108,36 +1108,43 @@
};
var goToControllerService = function (property) {
// close the dialog
var dialog = table.closest('.dialog');
if (dialog.hasClass('modal')) {
dialog.modal('hide');
} else {
dialog.hide();
}
$.Deferred(function (deferred) {
if ($('#settings').is(':visible')) {
deferred.resolve();
$.ajax({
type: 'GET',
url: '../nifi-api/controller-services/' + encodeURIComponent(property.value),
dataType: 'json'
}).done(function (controllerServiceEntity) {
// close the dialog
var dialog = table.closest('.dialog');
if (dialog.hasClass('modal')) {
dialog.modal('hide');
} else {
// reload the settings and show
nf.Settings.loadSettings().done(function () {
nf.Settings.showSettings();
deferred.resolve();
});
dialog.hide();
}
}).done(function () {
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// select the desired service
var row = controllerServiceData.getRowById(property.value);
controllerServiceGrid.setSelectedRows([row]);
controllerServiceGrid.scrollRowIntoView(row);
// select the controller services tab
$('#settings-tabs').find('li:eq(1)').click();
});
var controllerService = controllerServiceEntity.component;
$.Deferred(function (deferred) {
if (nf.Common.isDefinedAndNotNull(controllerService.parentGroupId)) {
nf.ProcessGroupConfiguration.showConfiguration(controllerService.parentGroupId).done(function () {
deferred.resolve();
});
} else {
if ($('#settings').is(':visible')) {
deferred.resolve();
} else {
// reload the settings and show
nf.Settings.showSettings().done(function () {
deferred.resolve();
});
}
}
}).done(function () {
if (nf.Common.isDefinedAndNotNull(controllerService.parentGroupId)) {
nf.ProcessGroupConfiguration.selectControllerService(property.value);
} else {
nf.Settings.selectControllerService(property.value);
}
});
}).fail(nf.Common.handleAjaxError);
};
// initialize the grid
@ -1580,13 +1587,15 @@
var propertyTableContainer = $(this);
var options = propertyTableContainer.data('options');
// clear the property table container
clear(propertyTableContainer);
// clear any existing new property dialogs
if (nf.Common.isDefinedAndNotNull(options.dialogContainer)) {
$(options.dialogContainer).children('div.new-property-dialog').remove();
$(options.dialogContainer).children('div.new-inline-controller-service-dialog').remove();
if (nf.Common.isDefinedAndNotNull(options)) {
// clear the property table container
clear(propertyTableContainer);
// clear any existing new property dialogs
if (nf.Common.isDefinedAndNotNull(options.dialogContainer)) {
$(options.dialogContainer).children('div.new-property-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: 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.flowStatusCtrl.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
*/
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)) {
nf.ProcessorConfiguration.showConfiguration(selection);
} else if (nf.CanvasUtils.isLabel(selection)) {
nf.LabelConfiguration.showConfiguration(selection);
} else if (nf.CanvasUtils.isProcessGroup(selection)) {
nf.ProcessGroupConfiguration.showConfiguration(selection);
nf.ProcessGroupConfiguration.showConfiguration(selectionData.id);
} else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) {
nf.RemoteProcessGroupConfiguration.showConfiguration(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).
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();
if (nf.CanvasUtils.isProcessor(selection)) {
nf.ProcessorDetails.showDetails(nf.Canvas.getGroupId(), selectionData.id);
} else if (nf.CanvasUtils.isProcessGroup(selection)) {
nf.ProcessGroupDetails.showDetails(selection);
nf.ProcessGroupDetails.showConfiguration(selectionData.id);
} else if (nf.CanvasUtils.isRemoteProcessGroup(selection)) {
nf.RemoteProcessGroupDetails.showDetails(selection);
} else if (nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection)) {

View File

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

View File

@ -112,12 +112,13 @@ nf.ControllerService = (function () {
/**
* Reloads the specified controller service. It's referencing and referenced
* components are NOT reloaded.
*
*
* @param {jQuery} serviceTable
* @param {string} id
*/
var reloadControllerService = function (id) {
var reloadControllerService = function (serviceTable, id) {
// 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 controllerServiceEntity = controllerServiceData.getItemById(id);
@ -135,18 +136,19 @@ nf.ControllerService = (function () {
url: controllerServiceEntity.component.uri,
dataType: 'json'
}).done(function (response) {
renderControllerService(response);
renderControllerService(serviceTable, response);
}).fail(nf.Common.handleAjaxError);
};
/**
* Renders the specified controller service.
*
*
* @param {object} serviceTable
* @param {object} controllerServiceEntity
*/
var renderControllerService = function (controllerServiceEntity) {
var renderControllerService = function (serviceTable, controllerServiceEntity) {
// 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 currentControllerServiceEntity = controllerServiceData.getItemById(controllerServiceEntity.id);
controllerServiceData.updateItem(controllerServiceEntity.id, $.extend({
@ -157,22 +159,24 @@ nf.ControllerService = (function () {
/**
* Reloads the specified controller services and all of its referencing
* and referenced components.
*
*
* @param {jQuery} serviceTable
* @param {type} controllerService
*/
var reloadControllerServiceAndReferencingComponents = function (controllerService) {
reloadControllerService(controllerService.id).done(function(response) {
reloadControllerServiceReferences(response.component);
var reloadControllerServiceAndReferencingComponents = function (serviceTable, controllerService) {
reloadControllerService(serviceTable, controllerService.id).done(function(response) {
reloadControllerServiceReferences(serviceTable, response.component);
});
};
/**
* Reloads components that reference this controller service as well as
* other services that this controller service references.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerService
*/
var reloadControllerServiceReferences = function (controllerService) {
var reloadControllerServiceReferences = function (serviceTable, controllerService) {
// reload all dependent processors if they are currently visible
$.each(controllerService.referencingComponents, function (_, referencingComponentEntity) {
// ensure we can read the referencing component prior to reloading
@ -210,7 +214,7 @@ nf.ControllerService = (function () {
}
} else {
// reload the referencing services
reloadControllerService(reference.id);
reloadControllerService(serviceTable, reference.id);
// update the current state of this service
var referencingComponentState = $('div.' + reference.id + '-state');
@ -220,14 +224,14 @@ nf.ControllerService = (function () {
// consider it's referencing components if appropriate
if (reference.referenceCycle === false) {
reloadControllerServiceReferences(reference);
reloadControllerServiceReferences(serviceTable, reference);
}
}
});
// see if this controller service references another controller service
// in order to update the referenced service referencing components
nf.ControllerService.reloadReferencedServices(controllerService);
nf.ControllerService.reloadReferencedServices(serviceTable, controllerService);
};
/**
@ -361,11 +365,12 @@ nf.ControllerService = (function () {
/**
* Adds the specified reference for this controller service.
*
*
* @param {jQuery} serviceTable
* @param {jQuery} referenceContainer
* @param {array} referencingComponents
*/
var createReferencingComponents = function (referenceContainer, referencingComponents) {
var createReferencingComponents = function (serviceTable, referenceContainer, referencingComponents) {
if (nf.Common.isEmpty(referencingComponents)) {
referenceContainer.append('<div class="unset">No referencing components.</div>');
return;
@ -427,7 +432,7 @@ nf.ControllerService = (function () {
processors.append(processorItem);
} else if (referencingComponent.referenceType === 'ControllerService') {
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();
// select the selected row
@ -443,12 +448,12 @@ nf.ControllerService = (function () {
var referencingServiceReferencesContainer = $('<div class="referencing-component-references hidden"></div>');
var serviceTwist = $('<div class="service expansion-button collapsed pointer"></div>').on('click', function() {
if (serviceTwist.hasClass('collapsed')) {
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var referencingService = controllerServiceData.getItemById(referencingComponent.id);
// create the markup for the references
createReferencingComponents(referencingServiceReferencesContainer, referencingService.referencingComponents);
createReferencingComponents(serviceTable, referencingServiceReferencesContainer, referencingService.referencingComponents);
} else {
referencingServiceReferencesContainer.empty();
}
@ -572,12 +577,13 @@ nf.ControllerService = (function () {
/**
* Sets whether the specified controller service is enabled.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} enabled
* @param {function} pollCondition
*/
var setEnabled = function (controllerServiceEntity, enabled, pollCondition) {
var setEnabled = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
// build the request entity
var updateControllerServiceEntity = {
'revision': nf.Client.getRevision(controllerServiceEntity),
@ -594,7 +600,7 @@ nf.ControllerService = (function () {
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
renderControllerService(response);
renderControllerService(serviceTable, response);
}).fail(nf.Common.handleAjaxError);
// wait until the polling of each service finished
@ -663,12 +669,13 @@ nf.ControllerService = (function () {
/**
* Updates the scheduled state of the processors/reporting tasks referencing
* the specified controller service.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} running
* @param {function} pollCondition
*/
var updateReferencingSchedulableComponents = function (controllerServiceEntity, running, pollCondition) {
var updateReferencingSchedulableComponents = function (serviceTable, controllerServiceEntity, running, pollCondition) {
var referenceEntity = {
'id': controllerServiceEntity.id,
'state': running ? 'RUNNING' : 'STOPPED',
@ -705,7 +712,7 @@ nf.ControllerService = (function () {
var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service
@ -933,12 +940,13 @@ nf.ControllerService = (function () {
/**
* Updates the referencing services with the specified state.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
* @param {boolean} enabled
* @param {function} pollCondition
*/
var updateReferencingServices = function (controllerServiceEntity, enabled, pollCondition) {
var updateReferencingServices = function (serviceTable, controllerServiceEntity, enabled, pollCondition) {
// build the reference entity
var referenceEntity = {
'id': controllerServiceEntity.id,
@ -972,7 +980,7 @@ nf.ControllerService = (function () {
var services = getReferencingControllerServiceIds(controllerServiceEntity.component);
// get the controller service grid
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
// start polling for each controller service
@ -1003,14 +1011,14 @@ nf.ControllerService = (function () {
*
* @argument {object} controllerService The controller service to disable
*/
var showDisableControllerServiceDialog = function (controllerService) {
var showDisableControllerServiceDialog = function (serviceTable, controllerService) {
// populate the disable controller service dialog
$('#disable-controller-service-id').text(controllerService.id);
$('#disable-controller-service-name').text(controllerService.name);
// load the controller referencing components list
var referencingComponentsContainer = $('#disable-controller-service-referencing-components');
createReferencingComponents(referencingComponentsContainer, controllerService.referencingComponents);
createReferencingComponents(serviceTable, referencingComponentsContainer, controllerService.referencingComponents);
var hasUnauthorized = false;
$.each(controllerService.referencingComponents, function (_, referencingComponent) {
@ -1026,7 +1034,9 @@ nf.ControllerService = (function () {
buttons.push({
buttonText: 'Disable',
handler: {
click: disableHandler
click: function () {
disableHandler(serviceTable);
}
}
});
}
@ -1051,20 +1061,36 @@ nf.ControllerService = (function () {
/**
* Shows the dialog for enabling a controller service.
*
*
* @param {object} serviceTable
* @param {object} controllerService
*/
var showEnableControllerServiceDialog = function (controllerService) {
var showEnableControllerServiceDialog = function (serviceTable, controllerService) {
// populate the disable controller service dialog
$('#enable-controller-service-id').text(controllerService.id);
$('#enable-controller-service-name').text(controllerService.name);
// load the controller referencing components list
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
$('#enable-controller-service-dialog').modal('show');
$('#enable-controller-service-dialog').modal('setButtonModel', buttons).modal('show');
// load the bulletins
queryBulletins([controllerService.id]).done(function(response) {
@ -1084,9 +1110,11 @@ nf.ControllerService = (function () {
/**
* Handles the disable action of the disable controller service dialog.
*
* @param {jQuery} serviceTable
*/
var disableHandler = function() {
var disableDialog = $(this);
var disableHandler = function(serviceTable) {
var disableDialog = $('#disable-controller-service-dialog');
var canceled = false;
// only provide a cancel option
@ -1107,7 +1135,7 @@ nf.ControllerService = (function () {
// get the controller service
var controllerServiceId = $('#disable-controller-service-id').text();
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component;
@ -1133,7 +1161,7 @@ nf.ControllerService = (function () {
$.Deferred(function (deferred) {
// stop all referencing schedulable components
var stopped = updateReferencingSchedulableComponents(controllerServiceEntity, false, continuePolling);
var stopped = updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, false, continuePolling);
// once everything has stopped
stopped.done(function () {
@ -1141,7 +1169,7 @@ nf.ControllerService = (function () {
var disableReferencingServices = $('#disable-referencing-services').addClass('ajax-loading');
// disable all referencing services
var disabled = updateReferencingServices(controllerServiceEntity, false, continuePolling);
var disabled = updateReferencingServices(serviceTable, controllerServiceEntity, false, continuePolling);
// everything is disabled
disabled.done(function () {
@ -1149,7 +1177,7 @@ nf.ControllerService = (function () {
var disableControllerService = $('#disable-controller-service').addClass('ajax-loading');
// disable this service
setEnabled(controllerServiceEntity, false, continuePolling).done(function () {
setEnabled(serviceTable, controllerServiceEntity, false, continuePolling).done(function () {
deferred.resolve();
disableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () {
@ -1165,7 +1193,7 @@ nf.ControllerService = (function () {
disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
});
}).always(function () {
reloadControllerServiceAndReferencingComponents(controllerService);
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
setCloseButton();
// 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.
*
* @param {jQuery} serviceTable
*/
var enableHandler = function() {
var enableDialog = $(this);
var enableHandler = function(serviceTable) {
var enableDialog = $('#enable-controller-service-dialog');
var canceled = false;
// get the controller service
var controllerServiceId = $('#enable-controller-service-id').text();
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
var controllerServiceEntity = controllerServiceData.getItemById(controllerServiceId);
var controllerService = controllerServiceEntity.component;
@ -1256,7 +1286,7 @@ nf.ControllerService = (function () {
$.Deferred(function (deferred) {
// enable this controller service
var enable = setEnabled(controllerServiceEntity, true, continuePolling);
var enable = setEnabled(serviceTable, controllerServiceEntity, true, continuePolling);
if (scope === config.serviceAndReferencingComponents) {
// once the service is enabled, activate all referencing components
@ -1265,7 +1295,7 @@ nf.ControllerService = (function () {
var enableReferencingServices = $('#enable-referencing-services').addClass('ajax-loading');
// enable the referencing services
var servicesEnabled = updateReferencingServices(controllerServiceEntity, true, continuePolling);
var servicesEnabled = updateReferencingServices(serviceTable, controllerServiceEntity, true, continuePolling);
// once all the referencing services are enbled
servicesEnabled.done(function () {
@ -1273,7 +1303,7 @@ nf.ControllerService = (function () {
var enableReferencingSchedulable = $('#enable-referencing-schedulable').addClass('ajax-loading');
// start all referencing schedulable components
updateReferencingSchedulableComponents(controllerServiceEntity, true, continuePolling).done(function() {
updateReferencingSchedulableComponents(serviceTable, controllerServiceEntity, true, continuePolling).done(function() {
deferred.resolve();
enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
}).fail(function () {
@ -1298,7 +1328,7 @@ nf.ControllerService = (function () {
});
}
}).always(function () {
reloadControllerServiceAndReferencingComponents(controllerService);
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
setCloseButton();
// inform the user if the action was canceled
@ -1331,8 +1361,10 @@ nf.ControllerService = (function () {
/**
* Goes to a service configuration from the property table.
*
* @param {jQuery} serviceTable
*/
var goToServiceFromProperty = function () {
var goToServiceFromProperty = function (serviceTable) {
return $.Deferred(function (deferred) {
// close all fields currently being edited
$('#controller-service-properties').propertytable('saveRow');
@ -1348,7 +1380,7 @@ nf.ControllerService = (function () {
},
yesHandler: function () {
var controllerServiceEntity = $('#controller-service-configuration').data('controllerServiceDetails');
saveControllerService(controllerServiceEntity).done(function () {
saveControllerService(serviceTable, controllerServiceEntity).done(function () {
deferred.resolve();
}).fail(function () {
deferred.reject();
@ -1361,7 +1393,7 @@ nf.ControllerService = (function () {
}).promise();
};
var saveControllerService = function (controllerServiceEntity) {
var saveControllerService = function (serviceTable, controllerServiceEntity) {
// marshal the settings and properties and update the controller service
var updatedControllerService = marshalDetails();
@ -1391,11 +1423,11 @@ nf.ControllerService = (function () {
contentType: 'application/json'
}).done(function (response) {
// reload the controller service
renderControllerService(response);
renderControllerService(serviceTable, response);
// reload all previously referenced controller services
$.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
reloadControllerService(oldServiceReferenceId);
reloadControllerService(serviceTable, oldServiceReferenceId);
});
}).fail(handleControllerServiceConfigurationError);
} else {
@ -1480,7 +1512,7 @@ nf.ControllerService = (function () {
});
// initialize the conroller service configuration dialog
$('#controller-service-configuration').data('mode', config.edit).modal({
$('#controller-service-configuration').modal({
headerText: 'Configure Controller Service',
overlayBackground: false,
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
$('#disable-controller-service-dialog').modal({
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-bulletins');
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({
headerText: 'Enable Controller Service',
overlayBackground: false,
buttons: [{
buttonText: 'Enable',
handler: {
click: enableHandler
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}],
handler: {
close: function() {
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-bulletins');
referencingComponents.css('border-width', '0').empty();
// reset dialog
enableDialog.modal('setButtonModel', [{
buttonText: 'Enable',
handler: {
click: enableHandler
}
}, {
buttonText: 'Cancel',
handler: {
click: closeModal
}
}]);
}
}
});
@ -1633,23 +1619,26 @@ nf.ControllerService = (function () {
/**
* Shows the configuration dialog for the specified controller service.
*
*
* @argument {jQuery} serviceTable The controller service table
* @argument {object} controllerServiceEntity The controller service
*/
showConfiguration: function (controllerServiceEntity) {
showConfiguration: function (serviceTable, controllerServiceEntity) {
var controllerServiceDialog = $('#controller-service-configuration');
if (controllerServiceDialog.data('mode') === config.readOnly) {
if (controllerServiceDialog.data('mode') !== config.edit) {
// update the visibility
$('#controller-service-configuration .controller-service-read-only').hide();
$('#controller-service-configuration .controller-service-editable').show();
// initialize the property table
$('#controller-service-properties').propertytable('destroy').propertytable({
readOnly: false,
groupId: nf.Canvas.getGroupId(),
dialogContainer: '#new-controller-service-property-container',
descriptorDeferred: getControllerServicePropertyDescriptor,
goToServiceDeferred: goToServiceFromProperty
goToServiceDeferred: function () {
return goToServiceFromProperty(serviceTable);
}
});
// update the mode
@ -1692,7 +1681,7 @@ nf.ControllerService = (function () {
var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list
createReferencingComponents(referenceContainer, controllerService.referencingComponents);
createReferencingComponents(serviceTable, referenceContainer, controllerService.referencingComponents);
var buttons = [{
buttonText: 'Apply',
@ -1702,8 +1691,8 @@ nf.ControllerService = (function () {
$('#controller-service-properties').propertytable('saveRow');
// save the controller service
saveControllerService(controllerServiceEntity).done(function (response) {
reloadControllerServiceReferences(response.component);
saveControllerService(serviceTable, controllerServiceEntity).done(function (response) {
reloadControllerServiceReferences(serviceTable, response.component);
// close the details panel
controllerServiceDialog.modal('hide');
@ -1735,7 +1724,7 @@ nf.ControllerService = (function () {
// show the custom ui
nf.CustomUi.showCustomUi($('#controller-service-id').text(), controllerService.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the controller service
reloadControllerServiceAndReferencingComponents(controllerService);
reloadControllerServiceAndReferencingComponents(serviceTable, controllerService);
// show the settings
nf.Settings.showSettings();
@ -1753,7 +1742,7 @@ nf.ControllerService = (function () {
overlayBackground: false,
noHandler: openCustomUi,
yesHandler: function () {
saveControllerService(controllerServiceEntity).done(function () {
saveControllerService(serviceTable, controllerServiceEntity).done(function () {
// open the custom ui
openCustomUi();
});
@ -1784,12 +1773,13 @@ nf.ControllerService = (function () {
/**
* Shows the controller service details in a read only dialog.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
showDetails: function(controllerServiceEntity) {
showDetails: function(serviceTable, controllerServiceEntity) {
var controllerServiceDialog = $('#controller-service-configuration');
if (controllerServiceDialog.data('mode') === config.edit) {
if (controllerServiceDialog.data('mode') !== config.readOnly) {
// update the visibility
$('#controller-service-configuration .controller-service-read-only').show();
$('#controller-service-configuration .controller-service-editable').hide();
@ -1839,7 +1829,7 @@ nf.ControllerService = (function () {
var referenceContainer = $('#controller-service-referencing-components');
// load the controller referencing components list
createReferencingComponents(referenceContainer, controllerService.referencingComponents);
createReferencingComponents(serviceTable, referenceContainer, controllerService.referencingComponents);
var buttons = [{
buttonText: 'Ok',
@ -1888,41 +1878,45 @@ nf.ControllerService = (function () {
/**
* Enables the specified controller service.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
enable: function(controllerServiceEntity) {
showEnableControllerServiceDialog(controllerServiceEntity.component);
enable: function(serviceTable, controllerServiceEntity) {
showEnableControllerServiceDialog(serviceTable, controllerServiceEntity.component);
},
/**
* Disables the specified controller service.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
disable: function(controllerServiceEntity) {
showDisableControllerServiceDialog(controllerServiceEntity.component);
disable: function(serviceTable, controllerServiceEntity) {
showDisableControllerServiceDialog(serviceTable, controllerServiceEntity.component);
},
/**
* Reloads the services that the specified comonent references. This is
* necessary because the specified component state is reflected in the
* referenced service referencing components.
*
*
* @param {jQuery} serviceTable
* @param {object} component
*/
reloadReferencedServices: function(component) {
reloadReferencedServices: function(serviceTable, component) {
$.each(getReferencedServices(component), function (_, referencedServiceId) {
reloadControllerService(referencedServiceId);
reloadControllerService(serviceTable, referencedServiceId);
});
},
/**
* Deletes the specified controller service.
*
*
* @param {jQuery} serviceTable
* @param {object} controllerServiceEntity
*/
remove: function(controllerServiceEntity) {
remove: function(serviceTable, controllerServiceEntity) {
// prompt for removal?
var revision = nf.Client.getRevision(controllerServiceEntity);
@ -1935,12 +1929,12 @@ nf.ControllerService = (function () {
dataType: 'json'
}).done(function (response) {
// remove the service
var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
var controllerServiceGrid = serviceTable.data('gridInstance');
var controllerServiceData = controllerServiceGrid.getData();
controllerServiceData.deleteItem(controllerServiceEntity.id);
// reload the as necessary
reloadControllerServiceReferences(controllerServiceEntity.component);
reloadControllerServiceReferences(serviceTable, controllerServiceEntity.component);
}).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,88 +19,271 @@
nf.ProcessGroupConfiguration = (function () {
var config = {
filterText: 'Filter',
styles: {
filterList: 'filter-list'
},
urls: {
api: '../nifi-api'
}
};
/**
* 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
var entity = {
'revision': nf.Client.getRevision({
'revision': {
'version': version
}
}),
'component': {
'id': groupId,
'name': $('#process-group-name').val(),
'comments': $('#process-group-comments').val()
}
};
// update the selected component
$.ajax({
type: 'PUT',
data: JSON.stringify(entity),
url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId),
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
// refresh the process group if necessary
if (response.accessPolicy.canRead && response.component.parentGroupId === nf.Canvas.getGroupId()) {
nf.ProcessGroup.set(response);
}
// show the result dialog
nf.Dialog.showOkDialog({
dialogContent: 'Process group configuration successfully saved.',
overlayBackground: false
});
// 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();
}
setEditable(false);
}
deferred.resolve();
}).fail(function (xhr, status, error) {
if (xhr.status === 403) {
setUnauthorizedText();
setEditable(false);
deferred.resolve();
} else {
deferred.reject(xhr, status, error);
}
});
}).promise();
// load the controller services
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-comments').val('');
};
return {
/**
* Initializes the settings page.
*/
init: function () {
$('#process-group-configuration').modal({
headerText: 'Configure Process Group',
overlayBackground: true,
buttons: [{
buttonText: 'Apply',
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();
// build the entity
var entity = {
'revision': nf.Client.getRevision(processGroupData),
'component': {
'id': processGroupId,
'name': $('#process-group-name').val(),
'comments': $('#process-group-comments').val()
}
};
// 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 selected component
$.ajax({
type: 'PUT',
data: JSON.stringify(entity),
url: processGroupData.component.uri,
dataType: 'json',
contentType: 'application/json'
}).done(function (response) {
// refresh the process group
nf.ProcessGroup.set(response);
// close the details panel
$('#process-group-configuration').modal('hide');
}).fail(function (xhr, status, error) {
// close the details panel
$('#process-group-configuration').modal('hide');
// handle the error
nf.Common.handleAjaxError(xhr, status, error);
});
// 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';
}
}
}, {
buttonText: 'Cancel',
handler: {
click: function () {
$('#process-group-configuration').modal('hide');
}
}
}],
handler: {
close: function () {
// clear the process group details
$('#process-group-id').text('');
$('#process-group-name').val('');
$('#process-group-comments').val('');
});
// 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.
*
* @argument {selection} selection The selection
* Update the size of the grid based on its container's current size.
*/
showConfiguration: function (selection) {
// if the specified selection is a processor, load its properties
if (nf.CanvasUtils.isProcessGroup(selection)) {
var selectionData = selection.datum();
resetTableSize: function () {
nf.ControllerServices.resetTableSize(getControllerServicesTable());
},
// populate the process group settings
$('#process-group-id').text(selectionData.id);
$('#process-group-name').val(selectionData.component.name);
$('#process-group-comments').val(selectionData.component.comments);
/**
* Shows the settings dialog.
*/
showConfiguration: function (groupId) {
// update the click listener
$('#process-group-configuration-refresh-button').off('click').on('click', function () {
loadConfiguration(groupId).done(showConfiguration);
});
// show the details
$('#process-group-configuration').modal('show');
}
// update the new controller service click listener
$('#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'
};
/**
* Gets the controller services table.
*
* @returns {*|jQuery|HTMLElement}
*/
var getControllerServicesTable = function () {
return $('#controller-services-table');
};
/**
* Handle any expected reporting task configuration errors.
*
@ -205,7 +214,7 @@ nf.ReportingTask = (function () {
}).done(function (response) {
// update the task
renderReportingTask(response);
nf.ControllerService.reloadReferencedServices(response.component);
nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.component);
}).fail(nf.Common.handleAjaxError);
};
@ -464,7 +473,7 @@ nf.ReportingTask = (function () {
// save the reporting task
saveReportingTask(reportingTaskEntity).done(function (response) {
// reload the reporting task
nf.ControllerService.reloadReferencedServices(response.component);
nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.component);
// close the details panel
$('#reporting-task-configuration').modal('hide');
@ -497,7 +506,7 @@ nf.ReportingTask = (function () {
nf.CustomUi.showCustomUi($('#reporting-task-id').text(), reportingTask.customUiUrl, true).done(function () {
// once the custom ui is closed, reload the reporting task
nf.ReportingTask.reload(reportingTaskEntity.id).done(function (response) {
nf.ControllerService.reloadReferencedServices(response.reportingTask);
nf.ControllerService.reloadReferencedServices(getControllerServicesTable(), response.reportingTask);
});
// show the settings

View File

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