package com.thinkbiganalytics.nifi.v1.rest.client; /*- * #%L * thinkbig-nifi-rest-client-v1 * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed 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. * #L% */ import com.google.common.collect.ImmutableMap; import com.thinkbiganalytics.nifi.rest.client.AbstractNiFiProcessGroupsRestClient; import com.thinkbiganalytics.nifi.rest.client.NiFiComponentState; import com.thinkbiganalytics.nifi.rest.client.NiFiProcessGroupsRestClient; import com.thinkbiganalytics.nifi.rest.client.NifiComponentNotFoundException; import com.thinkbiganalytics.nifi.rest.support.NifiConstants; import org.apache.nifi.web.api.dto.ConnectableDTO; import org.apache.nifi.web.api.dto.ConnectionDTO; import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.FlowSnippetDTO; import org.apache.nifi.web.api.dto.PortDTO; 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; import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO; import org.apache.nifi.web.api.entity.ConnectionEntity; import org.apache.nifi.web.api.entity.ConnectionsEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServicesEntity; import org.apache.nifi.web.api.entity.FlowEntity; import org.apache.nifi.web.api.entity.FunnelEntity; import org.apache.nifi.web.api.entity.InputPortsEntity; import org.apache.nifi.web.api.entity.InstantiateTemplateRequestEntity; import org.apache.nifi.web.api.entity.LabelEntity; import org.apache.nifi.web.api.entity.OutputPortsEntity; import org.apache.nifi.web.api.entity.PortEntity; import org.apache.nifi.web.api.entity.ProcessGroupEntity; import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity; import org.apache.nifi.web.api.entity.ProcessGroupsEntity; import org.apache.nifi.web.api.entity.ProcessorEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupEntity; import org.apache.nifi.web.api.entity.ScheduleComponentsEntity; import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.ws.rs.ClientErrorException; import javax.ws.rs.NotFoundException; /** * Implements a {@link NiFiProcessGroupsRestClient} for communicating with NiFi v1.0. */ public class NiFiProcessGroupsRestClientV1 extends AbstractNiFiProcessGroupsRestClient { /** * Base path for process group requests */ private static final String BASE_PATH = "/process-groups/"; /** * REST client for communicating with NiFi */ private final NiFiRestClientV1 client; /** * Constructs a {@code NiFiProcessGroupsRestClientV1} with the specified NiFi REST client. * * @param client the REST client */ public NiFiProcessGroupsRestClientV1(@Nonnull final NiFiRestClientV1 client) { this.client = client; } @Nonnull @Override public ProcessGroupDTO create(@Nonnull String parentProcessGroupId, @Nonnull String name) { final ProcessGroupEntity entity = new ProcessGroupEntity(); final ProcessGroupDTO processGroup = new ProcessGroupDTO(); processGroup.setName(name); entity.setComponent(processGroup); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(0L); entity.setRevision(revision); try { return client.post(BASE_PATH + parentProcessGroupId + "/process-groups", entity, ProcessGroupEntity.class).getComponent(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(parentProcessGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public ConnectionDTO createConnection(@Nonnull final String processGroupId, @Nonnull final ConnectableDTO source, @Nonnull final ConnectableDTO dest) { final ConnectionEntity entity = new ConnectionEntity(); final ConnectionDTO connection = new ConnectionDTO(); connection.setDestination(dest); connection.setName(source.getName() + "-" + dest.getName()); connection.setSource(source); entity.setComponent(connection); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(0L); entity.setRevision(revision); try { return client.post(BASE_PATH + processGroupId + "/connections", entity, ConnectionEntity.class).getComponent(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public ControllerServiceDTO createControllerService(@Nonnull final String processGroupId, @Nonnull final ControllerServiceDTO controllerService) { final ControllerServiceEntity entity = new ControllerServiceEntity(); entity.setComponent(controllerService); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(0L); entity.setRevision(revision); try { return client.post(BASE_PATH + processGroupId + "/controller-services", entity, ControllerServiceEntity.class).getComponent(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public PortDTO createInputPort(@Nonnull final String processGroupId, @Nonnull final PortDTO inputPort) { final PortEntity entity = new PortEntity(); entity.setComponent(inputPort); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(0L); entity.setRevision(revision); try { return client.post(BASE_PATH + processGroupId + "/input-ports", entity, PortEntity.class).getComponent(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public PortDTO createOutputPort(@Nonnull final String processGroupId, @Nonnull final PortDTO outputPort) { final PortEntity entity = new PortEntity(); entity.setComponent(outputPort); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(0L); entity.setRevision(revision); try { return client.post(BASE_PATH + processGroupId + "/output-ports", entity, PortEntity.class).getComponent(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public Set<ProcessGroupDTO> findAll(@Nonnull final String parentGroupId) { try { return client.get(BASE_PATH + parentGroupId + "/process-groups", null, ProcessGroupsEntity.class) .getProcessGroups().stream() .map(ProcessGroupEntity::getComponent) .collect(Collectors.toSet()); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(parentGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public Optional<ProcessGroupDTO> findById(@Nonnull final String processGroupId, final boolean recursive, final boolean verbose) { return findById(processGroupId, recursive, verbose, true); } public Optional<ProcessGroupDTO> findById(@Nonnull final String processGroupId, final boolean recursive, final boolean verbose, boolean logRestAccessErrors) { return findEntityById(processGroupId, logRestAccessErrors).filter(processGroupEntity -> processGroupEntity != null) .map(ProcessGroupEntity::getComponent) .map(processGroup -> { if (verbose) { processGroup.setContents(getFlowSnippet(processGroupId, recursive)); } return processGroup; }); } /** * Gets a process group entity. * * @param processGroupId the process group id * @return the process group entity, if found */ @Nonnull public Optional<ProcessGroupEntity> findEntityById(@Nonnull final String processGroupId) { return findEntityById(processGroupId, true); } /** * Gets a process group entity. * * @param processGroupId the process group id * @param logRestAccessErrors true to log any REST exceptions. false will not log exceptions * @return the process group entity, if found */ @Nonnull public Optional<ProcessGroupEntity> findEntityById(@Nonnull final String processGroupId, boolean logRestAccessErrors) { try { return Optional.ofNullable(client.get(BASE_PATH + processGroupId, null, ProcessGroupEntity.class, logRestAccessErrors)); } catch (final NotFoundException e) { return Optional.empty(); } } public Optional<ProcessGroupStatusDTO> getStatus(String processGroupId) { return Optional.ofNullable(findEntityById(processGroupId).map(processGroupEntity -> processGroupEntity.getStatus()).orElse(null)); } @Nonnull @Override public Set<ConnectionDTO> getConnections(@Nonnull final String processGroupId) { try { return client.get(BASE_PATH + processGroupId + "/connections", null, ConnectionsEntity.class) .getConnections().stream() .map(ConnectionEntity::getComponent) .collect(Collectors.toSet()); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public Set<ControllerServiceDTO> getControllerServices(@Nonnull final String processGroupId) { try { return client.get("/flow/process-groups/" + processGroupId + "/controller-services", null, ControllerServicesEntity.class) .getControllerServices().stream() .map(ControllerServiceEntity::getComponent) .collect(Collectors.toSet()); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public Set<PortDTO> getInputPorts(@Nonnull final String processGroupId) { try { return client.get(BASE_PATH + processGroupId + "/input-ports", null, InputPortsEntity.class) .getInputPorts().stream() .map(PortEntity::getComponent) .collect(Collectors.toSet()); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public Set<PortDTO> getOutputPorts(@Nonnull final String processGroupId) { try { return client.get(BASE_PATH + processGroupId + "/output-ports", null, OutputPortsEntity.class) .getOutputPorts().stream() .map(PortEntity::getComponent) .collect(Collectors.toSet()); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public FlowSnippetDTO instantiateTemplate(@Nonnull final String processGroupId, @Nonnull final String templateId) { final InstantiateTemplateRequestEntity entity = new InstantiateTemplateRequestEntity(); entity.setOriginX(10.0); entity.setOriginY(10.0); entity.setTemplateId(templateId); try { final FlowEntity flow = client.post(BASE_PATH + processGroupId + "/template-instance", entity, FlowEntity.class); return toFlowSnippet(flow.getFlow(),true); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } catch (final ClientErrorException e) { final String msg = e.getResponse().readEntity(String.class); throw new NifiComponentNotFoundException("Unable to create Template instance for templateId: " + templateId + " under Process Group " + processGroupId + ". " + msg); } } @Override public void schedule(@Nonnull final String processGroupId, @Nonnull final String parentGroupId, @Nonnull final NiFiComponentState state) { final ScheduleComponentsEntity entity = new ScheduleComponentsEntity(); entity.setId(processGroupId); entity.setState(state.toString()); try { client.put("/flow/process-groups/" + processGroupId, entity, ScheduleComponentsEntity.class); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } @Nonnull @Override public ProcessGroupDTO update(@Nonnull final ProcessGroupDTO processGroup) { return findEntityById(processGroup.getId()) .flatMap(current -> { final ProcessGroupEntity entity = new ProcessGroupEntity(); entity.setComponent(processGroup); final RevisionDTO revision = new RevisionDTO(); revision.setVersion(current.getRevision().getVersion()); entity.setRevision(revision); try { return Optional.of(client.put(BASE_PATH + processGroup.getId(), entity, ProcessGroupEntity.class).getComponent()); } catch (final NotFoundException e) { return Optional.empty(); } }) .orElseThrow(() -> new NifiComponentNotFoundException(processGroup.getId(), NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, null)); } @Nonnull @Override protected Optional<ProcessGroupDTO> doDelete(@Nonnull final ProcessGroupDTO processGroup) { return findEntityById(processGroup.getId()) .flatMap(entity -> { final Long version = entity.getRevision().getVersion(); try { return Optional.of(client.delete(BASE_PATH + processGroup.getId(), ImmutableMap.of("version", version), ProcessGroupEntity.class).getComponent()); } catch (final NotFoundException e) { return Optional.empty(); } }); } /** * Fetches the flow snippet for the specified process group. * * @param processGroupId the process group id * @param recursive {@code true} to include flows for child process groups, or {@code false} to leave child process group flows empty * @return the flow for the process group */ @Nonnull private FlowSnippetDTO getFlowSnippet(@Nonnull final String processGroupId, final boolean recursive) { // Fetch the flow final FlowSnippetDTO snippet; try { snippet = toFlowSnippet(client.get("/flow/process-groups/" + processGroupId, null, ProcessGroupFlowEntity.class).getProcessGroupFlow().getFlow(),recursive); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(processGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } // Return flow return snippet; } private Set<ProcessGroupEntity> findAllEntities(@Nonnull final String parentGroupId) { try { return client.get(BASE_PATH + parentGroupId + "/process-groups", null, ProcessGroupsEntity.class) .getProcessGroups(); } catch (final NotFoundException e) { throw new NifiComponentNotFoundException(parentGroupId, NifiConstants.NIFI_COMPONENT_TYPE.PROCESS_GROUP, e); } } /** * Converts the specified flow to a flow snippet. * * @param flow the flow * @return the flow snippet */ @Nonnull private FlowSnippetDTO toFlowSnippet(@Nonnull final FlowDTO flow, boolean recursive) { final FlowSnippetDTO snippet = new FlowSnippetDTO(); snippet.setConnections(flow.getConnections().stream().map(ConnectionEntity::getComponent).collect(Collectors.toSet())); snippet.setControllerServices(Collections.emptySet()); snippet.setFunnels(flow.getFunnels().stream().map(FunnelEntity::getComponent).collect(Collectors.toSet())); snippet.setInputPorts(flow.getInputPorts().stream().map(PortEntity::getComponent).collect(Collectors.toSet())); snippet.setLabels(flow.getLabels().stream().map(LabelEntity::getComponent).collect(Collectors.toSet())); snippet.setOutputPorts(flow.getOutputPorts().stream().map(PortEntity::getComponent).collect(Collectors.toSet())); snippet.setProcessGroups(flow.getProcessGroups().stream().map(ProcessGroupEntity::getComponent).collect(Collectors.toSet())); snippet.setProcessors(flow.getProcessors().stream().map(ProcessorEntity::getComponent).collect(Collectors.toSet())); snippet.setRemoteProcessGroups(flow.getRemoteProcessGroups().stream().map(RemoteProcessGroupEntity::getComponent).collect(Collectors.toSet())); // Add flow for child process groups if (recursive) { for (final ProcessGroupDTO processGroup : snippet.getProcessGroups()) { processGroup.setContents(getFlowSnippet(processGroup.getId(), true)); } } return snippet; } }