diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java index cf35c15f5c..3d3e826e11 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java @@ -136,7 +136,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho final JAXBElement element = unmarshaller.unmarshal(new StreamSource(authorizersConfigurationFile), Authorizers.class); return element.getValue(); } catch (SAXException | JAXBException e) { - throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath()); + throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e); } } else { throw new Exception("Unable to find the authorizer configuration file at " + authorizersConfigurationFile.getAbsolutePath()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AboutDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AboutDTO.java index 83aad410e3..fca8d96558 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AboutDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AboutDTO.java @@ -17,6 +17,7 @@ package org.apache.nifi.web.api.dto; import com.wordnik.swagger.annotations.ApiModelProperty; + import javax.xml.bind.annotation.XmlType; /** @@ -28,6 +29,9 @@ public class AboutDTO { private String title; private String version; + private String uri; + private String contentViewerUrl; + /* getters / setters */ /** * The title to be used on the page and in the About dialog. @@ -61,4 +65,31 @@ public class AboutDTO { this.version = version; } + /** + * @return URI for this NiFi controller + */ + @ApiModelProperty( + value = "The URI for the NiFi." + ) + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + /** + * @return the URL for the content viewer if configured + */ + @ApiModelProperty( + value = "The URL for the content viewer if configured." + ) + public String getContentViewerUrl() { + return contentViewerUrl; + } + + public void setContentViewerUrl(String contentViewerUrl) { + this.contentViewerUrl = contentViewerUrl; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java index 9f92598625..c6a7766171 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java @@ -39,9 +39,6 @@ public class ControllerConfigurationDTO { private Date currentTime; private Integer timeOffset; - private String contentViewerUrl; - private String uri; - /** * @return maximum number of timer driven threads this NiFi has available */ @@ -156,32 +153,4 @@ public class ControllerConfigurationDTO { public void setTimeOffset(Integer timeOffset) { this.timeOffset = timeOffset; } - - /** - * @return the URL for the content viewer if configured - */ - @ApiModelProperty( - value = "The URL for the content viewer if configured." - ) - public String getContentViewerUrl() { - return contentViewerUrl; - } - - public void setContentViewerUrl(String contentViewerUrl) { - this.contentViewerUrl = contentViewerUrl; - } - - /** - * @return URI for this NiFi controller - */ - @ApiModelProperty( - value = "The URI for the NiFi." - ) - public String getUri() { - return uri; - } - - public void setUri(String uri) { - this.uri = uri; - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ResourceDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ResourceDTO.java new file mode 100644 index 0000000000..9560c13627 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ResourceDTO.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.dto; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlType; + +/** + * Resource that supports access/authorization policies. + */ +@XmlType(name = "resource") +public class ResourceDTO { + + private String identifier; + private String name; + + /* getters / setters */ + /** + * The name of the resource. + * + * @return The name of the resource + */ + @ApiModelProperty( + value = "The name of the resource." + ) + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * The identifier of the resource. + * + * @return The identifier of the resource + */ + @ApiModelProperty( + value = "The identifier of the resource." + ) + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CopySnippetRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CopySnippetRequestEntity.java new file mode 100644 index 0000000000..a20fee31c1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CopySnippetRequestEntity.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a request to the API. + */ +@XmlRootElement(name = "copySnippetRequestEntity") +public class CopySnippetRequestEntity extends Entity { + + private String snippetId; + private Double originX; + private Double originY; + + @ApiModelProperty( + value = "The identifier of the snippet." + ) + public String getSnippetId() { + return snippetId; + } + + public void setSnippetId(String snippetId) { + this.snippetId = snippetId; + } + + @ApiModelProperty( + value = "The x coordinate of the origin of the bounding box where the new components will be placed." + ) + public Double getOriginX() { + return originX; + } + + public void setOriginX(Double originX) { + this.originX = originX; + } + + @ApiModelProperty( + value = "The y coordinate of the origin of the bounding box where the new components will be placed." + ) + public Double getOriginY() { + return originY; + } + + public void setOriginY(Double originY) { + this.originY = originY; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CreateTemplateRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CreateTemplateRequestEntity.java new file mode 100644 index 0000000000..865631a95d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/CreateTemplateRequestEntity.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a request to the API. + */ +@XmlRootElement(name = "copySnippetRequestEntity") +public class CreateTemplateRequestEntity extends Entity { + + private String name; + private String description; + private String snippetId; + + @ApiModelProperty( + value = "The name of the template." + ) + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty( + value = "The description of the template." + ) + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty( + value = "The identifier of the snippet." + ) + public String getSnippetId() { + return snippetId; + } + + public void setSnippetId(String snippetId) { + this.snippetId = snippetId; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/InstantiateTemplateRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/InstantiateTemplateRequestEntity.java new file mode 100644 index 0000000000..f4e55d4e92 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/InstantiateTemplateRequestEntity.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a request to the API. + */ +@XmlRootElement(name = "instantiateTemplateRequestEntity") +public class InstantiateTemplateRequestEntity extends Entity { + + private String templateId; + private Double originX; + private Double originY; + + @ApiModelProperty( + value = "The identifier of the template." + ) + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + @ApiModelProperty( + value = "The x coordinate of the origin of the bounding box where the new components will be placed." + ) + public Double getOriginX() { + return originX; + } + + public void setOriginX(Double originX) { + this.originX = originX; + } + + @ApiModelProperty( + value = "The y coordinate of the origin of the bounding box where the new components will be placed." + ) + public Double getOriginY() { + return originY; + } + + public void setOriginY(Double originY) { + this.originY = originY; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ResourcesEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ResourcesEntity.java new file mode 100644 index 0000000000..0cd8473c39 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ResourcesEntity.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import org.apache.nifi.web.api.dto.ResourceDTO; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +/** + * 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 references to ResourceDTOs. + */ +@XmlRootElement(name = "resourcesEntity") +public class ResourcesEntity extends Entity { + + private List resources; + + /** + * The AboutDTO that is being serialized. + * + * @return The AboutDTO object + */ + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SubmitReplayRequestEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SubmitReplayRequestEntity.java new file mode 100644 index 0000000000..f491e0d24c --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SubmitReplayRequestEntity.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api.entity; + +import com.wordnik.swagger.annotations.ApiModelProperty; + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * A serialized representation of this class can be placed in the entity body of a request to the API. + */ +@XmlRootElement(name = "copySnippetRequestEntity") +public class SubmitReplayRequestEntity extends Entity { + + private Long eventId; + private String clusterNodeId; + + @ApiModelProperty( + value = "The event identifier" + ) + public Long getEventId() { + return eventId; + } + + public void setEventId(Long eventId) { + this.eventId = eventId; + } + + @ApiModelProperty( + value = "The identifier of the node where to submit the replay request." + ) + public String getClusterNodeId() { + return clusterNodeId; + } + + public void setClusterNodeId(String clusterNodeId) { + this.clusterNodeId = clusterNodeId; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java index 5e122ec16c..20ace59851 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java @@ -25,6 +25,7 @@ public enum ResourceType { RemoteProcessGroup("/remote-process-groups"), Label("/labels"), ControllerService("/controller-services"), + ReportingTask("/reporting-tasks"), Template("/templates"); final String value; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java index a745f5caed..aefd307ed3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java @@ -331,7 +331,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C public static final Pattern PROCESSORS_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/processors"); public static final Pattern PROCESSOR_URI_PATTERN = Pattern.compile("/nifi-api/processors/[a-f0-9\\-]{36}"); - public static final Pattern PROCESSOR_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/processors/[a-f0-9\\-]{36}/status"); + public static final Pattern PROCESSOR_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/processors/[a-f0-9\\-]{36}/status"); public static final Pattern PROCESSOR_STATE_URI_PATTERN = Pattern.compile("/nifi-api/processors/[a-f0-9\\-]{36}/state"); public static final Pattern CLUSTER_PROCESSOR_URI_PATTERN = Pattern.compile("/nifi-api/cluster/processors/[a-f0-9\\-]{36}"); @@ -339,20 +339,20 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C public static final Pattern REMOTE_PROCESS_GROUP_URI_PATTERN = Pattern.compile("/nifi-api/remote-process-groups/[a-f0-9\\-]{36}"); public static final Pattern PROCESS_GROUP_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))"); - public static final Pattern GROUP_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/status"); - public static final Pattern CONTROLLER_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/controller/status"); + public static final Pattern GROUP_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/status"); + public static final Pattern CONTROLLER_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/status"); public static final Pattern TEMPLATE_INSTANCE_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/template-instance"); public static final Pattern FLOW_SNIPPET_INSTANCE_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/snippet-instance"); - public static final String PROVENANCE_URI = "/nifi-api/controller/provenance"; - public static final Pattern PROVENANCE_QUERY_URI = Pattern.compile("/nifi-api/controller/provenance/[a-f0-9\\-]{36}"); - public static final Pattern PROVENANCE_EVENT_URI = Pattern.compile("/nifi-api/controller/provenance/events/[0-9]+"); + public static final String PROVENANCE_URI = "/nifi-api/provenance"; + public static final Pattern PROVENANCE_QUERY_URI = Pattern.compile("/nifi-api/provenance/[a-f0-9\\-]{36}"); + public static final Pattern PROVENANCE_EVENT_URI = Pattern.compile("/nifi-api/provenance/events/[0-9]+"); - public static final String CONTROLLER_SERVICES_URI = "/nifi-api/controller-services/node"; + public static final Pattern CONTROLLER_SERVICES_URI = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/controller-services/node"); public static final Pattern CONTROLLER_SERVICE_URI_PATTERN = Pattern.compile("/nifi-api/controller-services/node/[a-f0-9\\-]{36}"); public static final Pattern CONTROLLER_SERVICE_STATE_URI_PATTERN = Pattern.compile("/nifi-api/controller-services/node/[a-f0-9\\-]{36}/state"); public static final Pattern CONTROLLER_SERVICE_REFERENCES_URI_PATTERN = Pattern.compile("/nifi-api/controller-services/node/[a-f0-9\\-]{36}/references"); - public static final String REPORTING_TASKS_URI = "/nifi-api/reporting-tasks/node"; + public static final String REPORTING_TASKS_URI = "/nifi-api/controller/reporting-tasks/node"; public static final Pattern REPORTING_TASK_URI_PATTERN = Pattern.compile("/nifi-api/reporting-tasks/node/[a-f0-9\\-]{36}"); public static final Pattern REPORTING_TASK_STATE_URI_PATTERN = Pattern.compile("/nifi-api/reporting-tasks/node/[a-f0-9\\-]{36}/state"); public static final Pattern BULLETIN_BOARD_URI_PATTERN = Pattern.compile("/nifi-api/bulletin-board"); @@ -361,17 +361,17 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C public static final Pattern COUNTER_URI_PATTERN = Pattern.compile("/nifi-api/controller/counters/[a-f0-9\\-]{36}"); public static final Pattern PROCESSOR_STATUS_HISTORY_URI_PATTERN = - Pattern.compile("/nifi-api/processors/[a-f0-9\\-]{36}/status/history"); - public static final Pattern PROCESS_GROUP_STATUS_HISTORY_URI_PATTERN = Pattern.compile("/nifi-api/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/status/history"); + Pattern.compile("/nifi-api/flow/processors/[a-f0-9\\-]{36}/status/history"); + public static final Pattern PROCESS_GROUP_STATUS_HISTORY_URI_PATTERN = Pattern.compile("/nifi-api/flow/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/status/history"); public static final Pattern REMOTE_PROCESS_GROUP_STATUS_HISTORY_URI_PATTERN = Pattern - .compile("/nifi-api/remote-process-groups/[a-f0-9\\-]{36}/status/history"); + .compile("/nifi-api/flow/remote-process-groups/[a-f0-9\\-]{36}/status/history"); public static final Pattern CONNECTION_STATUS_HISTORY_URI_PATTERN = Pattern - .compile("/nifi-api/connections/[a-f0-9\\-]{36}/status/history"); + .compile("/nifi-api/flow/connections/[a-f0-9\\-]{36}/status/history"); - public static final Pattern CONNECTION_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/connections/[a-f0-9\\-]{36}/status"); - public static final Pattern INPUT_PORT_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/input-ports/[a-f0-9\\-]{36}/status"); - public static final Pattern OUTPUT_PORT_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/output-ports/[a-f0-9\\-]{36}/status"); - public static final Pattern REMOTE_PROCESS_GROUP_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/remote-process-groups/[a-f0-9\\-]{36}/status"); + public static final Pattern CONNECTION_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/connections/[a-f0-9\\-]{36}/status"); + public static final Pattern INPUT_PORT_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/input-ports/[a-f0-9\\-]{36}/status"); + public static final Pattern OUTPUT_PORT_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/output-ports/[a-f0-9\\-]{36}/status"); + public static final Pattern REMOTE_PROCESS_GROUP_STATUS_URI_PATTERN = Pattern.compile("/nifi-api/flow/remote-process-groups/[a-f0-9\\-]{36}/status"); public static final Pattern DROP_REQUESTS_URI = Pattern.compile("/nifi-api/connections/[a-f0-9\\-]{36}/drop-requests"); public static final Pattern DROP_REQUEST_URI = Pattern.compile("/nifi-api/connections/[a-f0-9\\-]{36}/drop-requests/[a-f0-9\\-]{36}"); @@ -2412,13 +2412,13 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C } private static boolean isControllerServicesEndpoint(final URI uri, final String method) { - return "GET".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.equals(uri.getPath()); + return "GET".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.matcher(uri.getPath()).matches(); } private static boolean isControllerServiceEndpoint(final URI uri, final String method) { if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && CONTROLLER_SERVICE_URI_PATTERN.matcher(uri.getPath()).matches()) { return true; - } else if ("POST".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.equals(uri.getPath())) { + } else if ("POST".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.matcher(uri.getPath()).matches()) { return true; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java index ee59942e0d..56038fedab 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/groups/ProcessGroup.java @@ -506,6 +506,13 @@ public interface ProcessGroup { */ ProcessGroup findProcessGroup(String id); + /** + * @return a List of all ProcessGroups that are children or descendants of this + * ProcessGroup. This performs a recursive search of all descendant + * ProcessGroups + */ + List findAllProcessGroups(); + /** * @param id of the group * @return the RemoteProcessGroup with the given ID, if it exists as a child @@ -559,6 +566,13 @@ public interface ProcessGroup { */ Port findInputPort(String id); + /** + * @return a List of all InputPorts that are children or descendants of this + * ProcessGroup. This performs a recursive search of all descendant + * ProcessGroups + */ + List findAllInputPorts(); + /** * @param name of port * @return the input port with the given name, if it exists; otherwise @@ -574,6 +588,13 @@ public interface ProcessGroup { */ Port findOutputPort(String id); + /** + * @return a List of all OutputPorts that are children or descendants of this + * ProcessGroup. This performs a recursive search of all descendant + * ProcessGroups + */ + List findAllOutputPorts(); + /** * @param name of the port * @return the output port with the given name, if it exists; otherwise diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml index ff3ecba984..ab318d3963 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml @@ -71,6 +71,10 @@ org.apache.nifi nifi-site-to-site-client + + org.apache.nifi + nifi-framework-authorization + org.quartz-scheduler quartz diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java index 71e51b6e00..db99771c3b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java @@ -1380,6 +1380,19 @@ public final class StandardProcessGroup implements ProcessGroup { return null; } + @Override + public List findAllProcessGroups() { + return findAllProcessGroups(this); + } + + private List findAllProcessGroups(final ProcessGroup start) { + final List allProcessGroups = new ArrayList<>(start.getProcessGroups()); + for (final ProcessGroup childGroup : start.getProcessGroups()) { + allProcessGroups.addAll(findAllProcessGroups(childGroup)); + } + return allProcessGroups; + } + @Override public List findAllRemoteProcessGroups() { return findAllRemoteProcessGroups(this); @@ -1535,11 +1548,37 @@ public final class StandardProcessGroup implements ProcessGroup { return findPort(id, this, new InputPortRetriever()); } + @Override + public List findAllInputPorts() { + return findAllInputPorts(this); + } + + private List findAllInputPorts(final ProcessGroup start) { + final List allOutputPorts = new ArrayList<>(start.getInputPorts()); + for (final ProcessGroup group : start.getProcessGroups()) { + allOutputPorts.addAll(findAllInputPorts(group)); + } + return allOutputPorts; + } + @Override public Port findOutputPort(final String id) { return findPort(id, this, new OutputPortRetriever()); } + @Override + public List findAllOutputPorts() { + return findAllOutputPorts(this); + } + + private List findAllOutputPorts(final ProcessGroup start) { + final List allOutputPorts = new ArrayList<>(start.getOutputPorts()); + for (final ProcessGroup group : start.getProcessGroups()) { + allOutputPorts.addAll(findAllOutputPorts(group)); + } + return allOutputPorts; + } + @Override public Port getInputPortByName(final String name) { return getPortByName(name, this, new InputPortRetriever()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java index 55410a3fc0..11d7b2f7f3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/remote/StandardRemoteProcessGroup.java @@ -80,8 +80,8 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup { private static final Logger logger = LoggerFactory.getLogger(StandardRemoteProcessGroup.class); - public static final String CONTROLLER_URI_PATH = "/controller"; - public static final String ROOT_GROUP_STATUS_URI_PATH = "/controller/process-groups/root/status"; + public static final String SITE_TO_SITE_URI_PATH = "/site-to-site"; + public static final String ROOT_GROUP_STATUS_URI_PATH = "/flow/process-groups/root/status"; // status codes public static final int OK_STATUS_CODE = Status.OK.getStatusCode(); @@ -749,7 +749,7 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup { } final RemoteNiFiUtils utils = new RemoteNiFiUtils(isWebApiSecure() ? sslContext : null); - final String uriVal = apiUri.toString() + CONTROLLER_URI_PATH; + final String uriVal = apiUri.toString() + SITE_TO_SITE_URI_PATH; URI uri; try { uri = new URI(uriVal); @@ -1080,7 +1080,7 @@ public class StandardRemoteProcessGroup implements RemoteProcessGroup { public void run() { try { final RemoteNiFiUtils utils = new RemoteNiFiUtils(isWebApiSecure() ? sslContext : null); - final ClientResponse response = utils.get(new URI(apiUri + CONTROLLER_URI_PATH), getCommunicationsTimeout(TimeUnit.MILLISECONDS)); + final ClientResponse response = utils.get(new URI(apiUri + SITE_TO_SITE_URI_PATH), getCommunicationsTimeout(TimeUnit.MILLISECONDS)); final int statusCode = response.getStatus(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml index 38bafbd27c..2836641fce 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml @@ -197,6 +197,11 @@ nifi-utils provided + + org.apache.nifi + nifi-framework-authorization + provided + javax.servlet javax.servlet-api diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index 0c6cbee221..67f24e17d4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -46,6 +46,7 @@ import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO; import org.apache.nifi.web.api.dto.ReportingTaskDTO; +import org.apache.nifi.web.api.dto.ResourceDTO; import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.SnippetDTO; import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO; @@ -67,6 +68,7 @@ import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import java.util.Date; +import java.util.List; import java.util.Set; /** @@ -1400,4 +1402,12 @@ public interface NiFiServiceFacade { * @return the system diagnostics */ SystemDiagnosticsDTO getSystemDiagnostics(); + + // ---------------------------------------- + // Resources + // ---------------------------------------- + /** + * @return the resources + */ + List getResources(); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java index a3a9e48d68..e9117a9375 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.web; -import org.apache.nifi.admin.service.KeyService; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; @@ -54,7 +53,6 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte private static final Logger logger = LoggerFactory.getLogger(NiFiWebApiSecurityConfiguration.class); private NiFiProperties properties; - private KeyService keyService; private NodeAuthorizedUserFilter nodeAuthorizedUserFilter; @@ -174,16 +172,10 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte public NiFiAnonymousUserFilter anonymousFilterBean() throws Exception { if (anonymousAuthenticationFilter == null) { anonymousAuthenticationFilter = new NiFiAnonymousUserFilter(); - anonymousAuthenticationFilter.setKeyService(keyService); } return anonymousAuthenticationFilter; } - @Autowired - public void setKeyService(KeyService keyService) { - this.keyService = keyService; - } - @Autowired public void setProperties(NiFiProperties properties) { this.properties = properties; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java index 7b5ab27fb1..66f1546878 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java @@ -56,10 +56,10 @@ public class StandardNiFiContentAccess implements ContentAccess { public static final String CLIENT_ID_PARAM = "clientId"; private static final Pattern FLOWFILE_CONTENT_URI_PATTERN = Pattern - .compile("/controller/process-groups/((?:root)|(?:[a-f0-9\\-]{36}))/connections/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content.*"); + .compile("/flowfile-queues/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content.*"); private static final Pattern PROVENANCE_CONTENT_URI_PATTERN = Pattern - .compile("/controller/provenance/events/([0-9]+)/content/((?:input)|(?:output)).*"); + .compile("/provenance/events/([0-9]+)/content/((?:input)|(?:output)).*"); private NiFiProperties properties; private NiFiServiceFacade serviceFacade; @@ -140,8 +140,8 @@ public class StandardNiFiContentAccess implements ContentAccess { return new DownloadableContent(filename, contentType, clientResponse.getEntityInputStream()); } else { // example URIs: - // http://localhost:8080/nifi-api/controller/provenance/events/{id}/content/{input|output} - // http://localhost:8080/nifi-api/controller/process-groups/{root|uuid}/connections/{uuid}/flowfiles/{uuid}/content + // http://localhost:8080/nifi-api/provenance/events/{id}/content/{input|output} + // http://localhost:8080/nifi-api/flowfile-queues/{uuid}/flowfiles/{uuid}/content // get just the context path for comparison final String dataUri = StringUtils.substringAfter(request.getDataUri(), "/nifi-api"); @@ -152,11 +152,10 @@ public class StandardNiFiContentAccess implements ContentAccess { // flowfile listing content final Matcher flowFileMatcher = FLOWFILE_CONTENT_URI_PATTERN.matcher(dataUri); if (flowFileMatcher.matches()) { - final String groupId = flowFileMatcher.group(1); - final String connectionId = flowFileMatcher.group(2); - final String flowfileId = flowFileMatcher.group(3); + final String connectionId = flowFileMatcher.group(1); + final String flowfileId = flowFileMatcher.group(2); - return getFlowFileContent(groupId, connectionId, flowfileId, dataUri); + return getFlowFileContent(connectionId, flowfileId, dataUri); } // provenance event content @@ -177,7 +176,7 @@ public class StandardNiFiContentAccess implements ContentAccess { } } - private DownloadableContent getFlowFileContent(final String groupId, final String connectionId, final String flowfileId, final String dataUri) { + private DownloadableContent getFlowFileContent(final String connectionId, final String flowfileId, final String dataUri) { // TODO - ensure the user is authorized - not checking with @PreAuthorized annotation as aspect not trigger on call within a class // if (!NiFiUserUtils.getAuthorities().contains(Authority.ROLE_DFM.toString())) { // throw new AccessDeniedException("Access is denied."); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 562199010c..0969eacc7d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -24,6 +24,7 @@ import org.apache.nifi.action.Operation; import org.apache.nifi.action.details.FlowChangePurgeDetails; import org.apache.nifi.admin.service.AuditService; import org.apache.nifi.admin.service.KeyService; +import org.apache.nifi.authorization.Resource; import org.apache.nifi.cluster.context.ClusterContext; import org.apache.nifi.cluster.context.ClusterContextThreadLocal; import org.apache.nifi.cluster.manager.exception.UnknownNodeException; @@ -97,6 +98,7 @@ import org.apache.nifi.web.api.dto.PropertyHistoryDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO; import org.apache.nifi.web.api.dto.ReportingTaskDTO; +import org.apache.nifi.web.api.dto.ResourceDTO; import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.SnippetDTO; import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO; @@ -1529,6 +1531,16 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { return dtoFactory.createSystemDiagnosticsDto(sysDiagnostics); } + @Override + public List getResources() { + final List resources = controllerFacade.getResources(); + final List resourceDtos = new ArrayList<>(resources.size()); + for (final Resource resource : resources) { + resourceDtos.add(dtoFactory.createResourceDto(resource)); + } + return resourceDtos; + } + /** * Ensures the specified user has permission to access the specified port. */ @@ -1624,9 +1636,6 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { final long refreshInterval = FormatUtils.getTimeDuration(properties.getAutoRefreshInterval(), TimeUnit.SECONDS); controllerConfig.setAutoRefreshIntervalSeconds(refreshInterval); - // get the content viewer url - controllerConfig.setContentViewerUrl(properties.getProperty(NiFiProperties.CONTENT_VIEWER_URL)); - final Date now = new Date(); controllerConfig.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime())); controllerConfig.setCurrentTime(now); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java index f5d5e2f036..b8f135015e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java @@ -415,7 +415,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // create the request URL URI requestUrl; try { - String path = "/nifi-api/controller/controller-services/node/" + URLEncoder.encode(id, "UTF-8"); + String path = "/nifi-api/controller-services/node/" + URLEncoder.encode(id, "UTF-8"); requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null); } catch (final URISyntaxException | UnsupportedEncodingException use) { throw new ClusterRequestException(use); @@ -471,7 +471,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // create the request URL URI requestUrl; try { - String path = "/nifi-api/controller/controller-services/node/" + URLEncoder.encode(id, "UTF-8"); + String path = "/nifi-api/controller-services/node/" + URLEncoder.encode(id, "UTF-8"); requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null); } catch (final URISyntaxException | UnsupportedEncodingException use) { throw new ClusterRequestException(use); @@ -550,7 +550,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // create the request URL URI requestUrl; try { - String path = "/nifi-api/controller/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8"); + String path = "/nifi-api/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8"); requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null); } catch (final URISyntaxException | UnsupportedEncodingException use) { throw new ClusterRequestException(use); @@ -606,7 +606,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration // create the request URL URI requestUrl; try { - String path = "/nifi-api/controller/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8"); + String path = "/nifi-api/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8"); requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null); } catch (final URISyntaxException | UnsupportedEncodingException use) { throw new ClusterRequestException(use); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java index c57e4ff67c..bb8e701c0b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java @@ -79,12 +79,12 @@ import java.util.List; import java.util.concurrent.TimeUnit; /** - * RESTful endpoint for managing a cluster. + * RESTful endpoint for managing access. */ @Path("/access") @Api( - value = "/access", - description = "Endpoints for obtaining an access token or checking access status" + value = "/access", + description = "Endpoints for obtaining an access token or checking access status." ) public class AccessResource extends ApplicationResource { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java index 3b429e7e45..966aa29cc3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java @@ -337,6 +337,12 @@ public abstract class ApplicationResource { return params; } + /** + * Used when replicating requests to ensure the client id is the same on each node. + * + * @param entity entity + * @return entity with the client id set + */ protected Entity updateClientId(final Entity entity) { if (entity != null && entity.getRevision() != null && StringUtils.isBlank(entity.getRevision().getClientId())) { entity.getRevision().setClientId(new ClientIdParameter().getClientId()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/BulletinBoardResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/BulletinBoardResource.java deleted file mode 100644 index f7ae37e9f8..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/BulletinBoardResource.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.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.cluster.manager.impl.WebClusterManager; -import org.apache.nifi.util.NiFiProperties; -import org.apache.nifi.web.NiFiServiceFacade; -import org.apache.nifi.web.api.dto.BulletinBoardDTO; -import org.apache.nifi.web.api.dto.BulletinQueryDTO; -import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.entity.BulletinBoardEntity; -import org.apache.nifi.web.api.request.BulletinBoardPatternParameter; -import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.api.request.IntegerParameter; -import org.apache.nifi.web.api.request.LongParameter; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -/** - * RESTful endpoint for managing a Template. - */ -@Path("bulletin-board") -public class BulletinBoardResource extends ApplicationResource { - - private NiFiProperties properties; - private WebClusterManager clusterManager; - - private NiFiServiceFacade serviceFacade; - - /** - * Retrieves all the of templates 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. - * @param after Supporting querying for bulletins after a particular - * bulletin id. - * @param limit The max number of bulletins to return. - * @param sourceName Source name filter. Supports a regular expression. - * @param message Message filter. Supports a regular expression. - * @param sourceId Source id filter. Supports a regular expression. - * @param groupId Group id filter. Supports a regular expression. - * @return A bulletinBoardEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets current bulletins", - response = BulletinBoardEntity.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 getBulletinBoard( - @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, - @ApiParam( - value = "Includes bulletins with an id after this value.", - required = false - ) - @QueryParam("after") LongParameter after, - @ApiParam( - value = "Includes bulletins originating from this sources whose name match this regular expression.", - required = false - ) - @QueryParam("sourceName") BulletinBoardPatternParameter sourceName, - @ApiParam( - value = "Includes bulletins whose message that match this regular expression.", - required = false - ) - @QueryParam("message") BulletinBoardPatternParameter message, - @ApiParam( - value = "Includes bulletins originating from this sources whose id match this regular expression.", - required = false - ) - @QueryParam("sourceId") BulletinBoardPatternParameter sourceId, - @ApiParam( - value = "Includes bulletins originating from this sources whose group id match this regular expression.", - required = false - ) - @QueryParam("groupId") BulletinBoardPatternParameter groupId, - @ApiParam( - value = "The number of bulletins to limit the response to.", - required = false - ) - @QueryParam("limit") IntegerParameter limit) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // build the bulletin query - final BulletinQueryDTO query = new BulletinQueryDTO(); - - if (sourceId != null) { - query.setSourceId(sourceId.getRawPattern()); - } - if (groupId != null) { - query.setGroupId(groupId.getRawPattern()); - } - if (sourceName != null) { - query.setName(sourceName.getRawPattern()); - } - if (message != null) { - query.setMessage(message.getRawPattern()); - } - if (after != null) { - query.setAfter(after.getLong()); - } - if (limit != null) { - query.setLimit(limit.getInteger()); - } - - // get the bulletin board - final BulletinBoardDTO bulletinBoard = serviceFacade.getBulletinBoard(query); - - // create the revision - RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - BulletinBoardEntity entity = new BulletinBoardEntity(); - entity.setRevision(revision); - entity.setBulletinBoard(bulletinBoard); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - // setters - public void setServiceFacade(NiFiServiceFacade serviceFacade) { - this.serviceFacade = serviceFacade; - } - - public void setClusterManager(WebClusterManager clusterManager) { - this.clusterManager = clusterManager; - } - - public void setProperties(NiFiProperties properties) { - this.properties = properties; - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java index a2a2d5bcdf..526663287c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java @@ -66,7 +66,7 @@ import java.util.List; @Path("/cluster") @Api( value = "/cluster", - description = "Provides access to the cluster of Nodes that comprise this NiFi" + description = "Endpoint for managing the cluster." ) public class ClusterResource extends ApplicationResource { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java index fd4a81c123..7d5df509f1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java @@ -16,39 +16,23 @@ */ package org.apache.nifi.web.api; +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.cluster.context.ClusterContext; -import org.apache.nifi.cluster.context.ClusterContextThreadLocal; -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.stream.io.StreamUtils; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.ConfigurationSnapshot; -import org.apache.nifi.web.DownloadableContent; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.ConnectionDTO; -import org.apache.nifi.web.api.dto.DropRequestDTO; -import org.apache.nifi.web.api.dto.FlowFileDTO; import org.apache.nifi.web.api.dto.FlowFileSummaryDTO; import org.apache.nifi.web.api.dto.ListingRequestDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.status.ConnectionStatusDTO; -import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.entity.ConnectionEntity; -import org.apache.nifi.web.api.entity.ConnectionStatusEntity; -import org.apache.nifi.web.api.entity.DropRequestEntity; -import org.apache.nifi.web.api.entity.FlowFileEntity; -import org.apache.nifi.web.api.entity.ListingRequestEntity; -import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.LongParameter; @@ -58,33 +42,27 @@ import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.StreamingOutput; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.UUID; /** * RESTful endpoint for managing a Connection. */ -@Path("connections") +@Path("/connections") +@Api( + value = "/connections", + description = "Endpoint for managing a Connection." +) public class ConnectionResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; @@ -211,169 +189,6 @@ public class ConnectionResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the specified connection status. - * - * @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. - * @param id The id of the connection history to retrieve. - * @return A connectionStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status for a connection", - response = ConnectionStatusEntity.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 = 404, message = "The specified resource could not be found."), - @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 getConnectionStatus( - @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, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("id") String id) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final ConnectionStatusEntity entity = (ConnectionStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getConnectionStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the specified connection status - final ConnectionStatusDTO connectionStatus = serviceFacade.getConnectionStatus(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final ConnectionStatusEntity entity = new ConnectionStatusEntity(); - entity.setRevision(revision); - entity.setConnectionStatus(connectionStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the specified connection status history. - * - * @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. - * @param id The id of the connection to retrieve. - * @return A statusHistoryEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status/history") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets the status history for a connection", - response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), - @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 getConnectionStatusHistory( - @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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the specified processor status history - final StatusHistoryDTO connectionStatusHistory = serviceFacade.getConnectionStatusHistory(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final StatusHistoryEntity entity = new StatusHistoryEntity(); - entity.setRevision(revision); - entity.setStatusHistory(connectionStatusHistory); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Updates the specified connection. * @@ -557,631 +372,6 @@ public class ConnectionResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Gets the specified flowfile from the specified connection. - * - * @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. - * @param connectionId The connection id - * @param flowFileUuid The flowfile uuid - * @param clusterNodeId The cluster node id where the flowfile resides - * @return a flowFileDTO - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/flowfiles/{flowfile-uuid}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Gets a FlowFile from a Connection.", - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 getFlowFile( - @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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The flowfile uuid.", - required = true - ) - @PathParam("flowfile-uuid") String flowFileUuid, - @ApiParam( - value = "The id of the node where the content exists if clustered.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - throw new IllegalArgumentException("The id of the node in the cluster is required."); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the flowfile - final FlowFileDTO flowfileDto = serviceFacade.getFlowFile(connectionId, flowFileUuid); - populateRemainingFlowFileContent(connectionId, flowfileDto); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final FlowFileEntity entity = new FlowFileEntity(); - entity.setRevision(revision); - entity.setFlowFile(flowfileDto); - - return generateOkResponse(entity).build(); - } - - /** - * Gets the content for the specified flowfile in the specified connection. - * - * @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. - * @param connectionId The connection id - * @param flowFileUuid The flowfile uuid - * @param clusterNodeId The cluster node id - * @return The content stream - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.WILDCARD) - @Path("/{connection-id}/flowfiles/{flowfile-uuid}/content") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Gets the content for a FlowFile in a Connection.", - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 downloadFlowFileContent( - @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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The flowfile uuid.", - required = true - ) - @PathParam("flowfile-uuid") String flowFileUuid, - @ApiParam( - value = "The id of the node where the content exists if clustered.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - throw new IllegalArgumentException("The id of the node in the cluster is required."); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the uri of the request - final String uri = generateResourceUri("connections", connectionId, "flowfiles", flowFileUuid, "content"); - - // get an input stream to the content - final DownloadableContent content = serviceFacade.getContent(connectionId, flowFileUuid, uri); - - // generate a streaming response - final StreamingOutput response = new StreamingOutput() { - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - try (InputStream is = content.getContent()) { - // stream the content to the response - StreamUtils.copy(is, output); - - // flush the response - output.flush(); - } - } - }; - - // use the appropriate content type - String contentType = content.getType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_OCTET_STREAM; - } - - return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); - } - - /** - * Creates a request to list the flowfiles in the queue of the specified connection. - * - * @param httpServletRequest request - * @param id The id of the connection - * @return A listRequestEntity - */ - @POST - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/listing-requests") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Lists the contents of the queue in this connection.", - response = ListingRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."), - @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 = 404, message = "The specified resource could not be found."), - @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 createFlowFileListing( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - serviceFacade.verifyListQueue(id); - return generateContinueResponse().build(); - } - - // ensure the id is the same across the cluster - final String listingRequestId; - final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); - if (clusterContext != null) { - listingRequestId = UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString(); - } else { - listingRequestId = UUID.randomUUID().toString(); - } - - // submit the listing request - final ListingRequestDTO listingRequest = serviceFacade.createFlowFileListingRequest(id, listingRequestId); - populateRemainingFlowFileListingContent(id, listingRequest); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - - // create the response entity - final ListingRequestEntity entity = new ListingRequestEntity(); - entity.setRevision(revision); - entity.setListingRequest(listingRequest); - - // generate the URI where the response will be - final URI location = URI.create(listingRequest.getUri()); - return Response.status(Status.ACCEPTED).location(location).entity(entity).build(); - } - - /** - * Checks the status of an outstanding listing request. - * - * @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. - * @param connectionId The id of the connection - * @param listingRequestId The id of the drop request - * @return A dropRequestEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/listing-requests/{listing-request-id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Gets the current status of a listing request for the specified connection.", - response = ListingRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 getListingRequest( - @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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The listing request id.", - required = true - ) - @PathParam("listing-request-id") String listingRequestId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the listing request - final ListingRequestDTO listingRequest = serviceFacade.getFlowFileListingRequest(connectionId, listingRequestId); - populateRemainingFlowFileListingContent(connectionId, listingRequest); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ListingRequestEntity entity = new ListingRequestEntity(); - entity.setRevision(revision); - entity.setListingRequest(listingRequest); - - return generateOkResponse(entity).build(); - } - - /** - * Deletes the specified listing request. - * - * @param httpServletRequest request - * @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. - * @param connectionId The connection id - * @param listingRequestId The drop request id - * @return A dropRequestEntity - */ - @DELETE - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/listing-requests/{listing-request-id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Cancels and/or removes a request to list the contents of this connection.", - response = DropRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 deleteListingRequest( - @Context 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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The listing request id.", - required = true - ) - @PathParam("listing-request-id") String listingRequestId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // delete the listing request - final ListingRequestDTO listingRequest = serviceFacade.deleteFlowFileListingRequest(connectionId, listingRequestId); - - // prune the results as they were already received when the listing completed - listingRequest.setFlowFileSummaries(null); - - // populate remaining content - populateRemainingFlowFileListingContent(connectionId, listingRequest); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ListingRequestEntity entity = new ListingRequestEntity(); - entity.setRevision(revision); - entity.setListingRequest(listingRequest); - - return generateOkResponse(entity).build(); - } - - /** - * Creates a request to delete the flowfiles in the queue of the specified connection. - * - * @param httpServletRequest request - * @param id The id of the connection - * @return A dropRequestEntity - */ - @POST - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/drop-requests") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Creates a request to drop the contents of the queue in this connection.", - response = DropRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @ApiResponses( - value = { - @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."), - @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 = 404, message = "The specified resource could not be found."), - @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 createDropRequest( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // ensure the id is the same across the cluster - final String dropRequestId; - final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); - if (clusterContext != null) { - dropRequestId = UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString(); - } else { - dropRequestId = UUID.randomUUID().toString(); - } - - // submit the drop request - final DropRequestDTO dropRequest = serviceFacade.createFlowFileDropRequest(id, dropRequestId); - dropRequest.setUri(generateResourceUri("connections", id, "drop-requests", dropRequest.getId())); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - - // create the response entity - final DropRequestEntity entity = new DropRequestEntity(); - entity.setRevision(revision); - entity.setDropRequest(dropRequest); - - // generate the URI where the response will be - final URI location = URI.create(dropRequest.getUri()); - return Response.status(Status.ACCEPTED).location(location).entity(entity).build(); - } - - /** - * Checks the status of an outstanding drop request. - * - * @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. - * @param connectionId The id of the connection - * @param dropRequestId The id of the drop request - * @return A dropRequestEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/drop-requests/{drop-request-id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Gets the current status of a drop request for the specified connection.", - response = DropRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 getDropRequest( - @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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The drop request id.", - required = true - ) - @PathParam("drop-request-id") String dropRequestId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the drop request - final DropRequestDTO dropRequest = serviceFacade.getFlowFileDropRequest(connectionId, dropRequestId); - dropRequest.setUri(generateResourceUri("connections", connectionId, "drop-requests", dropRequestId)); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final DropRequestEntity entity = new DropRequestEntity(); - entity.setRevision(revision); - entity.setDropRequest(dropRequest); - - return generateOkResponse(entity).build(); - } - - /** - * Deletes the specified drop request. - * - * @param httpServletRequest request - * @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. - * @param connectionId The connection id - * @param dropRequestId The drop request id - * @return A dropRequestEntity - */ - @DELETE - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{connection-id}/drop-requests/{drop-request-id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Cancels and/or removes a request to drop the contents of this connection.", - response = DropRequestEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 removeDropRequest( - @Context 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, - @ApiParam( - value = "The connection id.", - required = true - ) - @PathParam("connection-id") String connectionId, - @ApiParam( - value = "The drop request id.", - required = true - ) - @PathParam("drop-request-id") String dropRequestId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // delete the drop request - final DropRequestDTO dropRequest = serviceFacade.deleteFlowFileDropRequest(connectionId, dropRequestId); - dropRequest.setUri(generateResourceUri("connections", connectionId, "drop-requests", dropRequestId)); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final DropRequestEntity entity = new DropRequestEntity(); - entity.setRevision(revision); - entity.setDropRequest(dropRequest); - - return generateOkResponse(entity).build(); - } - // setters public void setServiceFacade(NiFiServiceFacade serviceFacade) { this.serviceFacade = serviceFacade; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index a3fdc1201a..10f44563e8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -24,6 +24,8 @@ 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.cluster.context.ClusterContext; +import org.apache.nifi.cluster.context.ClusterContextThreadLocal; import org.apache.nifi.cluster.manager.NodeResponse; import org.apache.nifi.cluster.manager.exception.UnknownNodeException; import org.apache.nifi.cluster.manager.impl.WebClusterManager; @@ -32,45 +34,30 @@ import org.apache.nifi.cluster.protocol.NodeIdentifier; import org.apache.nifi.user.NiFiUser; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.ConfigurationSnapshot; -import org.apache.nifi.web.IllegalClusterResourceRequestException; import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; -import org.apache.nifi.web.api.dto.AboutDTO; -import org.apache.nifi.web.api.dto.BannerDTO; import org.apache.nifi.web.api.dto.ControllerConfigurationDTO; -import org.apache.nifi.web.api.dto.ControllerDTO; import org.apache.nifi.web.api.dto.CounterDTO; import org.apache.nifi.web.api.dto.CountersDTO; +import org.apache.nifi.web.api.dto.ReportingTaskDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.search.SearchResultsDTO; -import org.apache.nifi.web.api.dto.status.ControllerStatusDTO; -import org.apache.nifi.web.api.entity.AboutEntity; import org.apache.nifi.web.api.entity.AuthorityEntity; -import org.apache.nifi.web.api.entity.BannerEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; -import org.apache.nifi.web.api.entity.ControllerEntity; -import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity; -import org.apache.nifi.web.api.entity.ControllerStatusEntity; 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.IdentityEntity; -import org.apache.nifi.web.api.entity.PrioritizerTypesEntity; import org.apache.nifi.web.api.entity.ProcessGroupEntity; -import org.apache.nifi.web.api.entity.ProcessorTypesEntity; -import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity; -import org.apache.nifi.web.api.entity.SearchResultsEntity; +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 org.apache.nifi.web.api.request.IntegerParameter; -import org.apache.nifi.web.api.request.LongParameter; import org.apache.nifi.web.security.user.NiFiUserUtils; +import org.apache.nifi.web.util.Availability; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; -import javax.ws.rs.HEAD; import javax.ws.rs.HttpMethod; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -83,19 +70,19 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; +import java.util.UUID; /** * RESTful endpoint for managing a Flow Controller. */ @Path("/controller") @Api( - value = "/controller", - description = "Provides realtime command and control of this NiFi instance" + value = "/controller", + description = "Provides realtime command and control of this NiFi instance" ) public class ControllerResource extends ApplicationResource { @@ -103,161 +90,11 @@ public class ControllerResource extends ApplicationResource { private WebClusterManager clusterManager; private NiFiProperties properties; + private ReportingTaskResource reportingTaskResource; + @Context private ResourceContext resourceContext; - /** - * Locates the Provenance sub-resource. - * - * @return the Provenance sub-resource - */ - @Path("/provenance") - @ApiOperation( - value = "Gets the provenance resource", - response = ProvenanceResource.class - ) - public ProvenanceResource getProvenanceResource() { - return resourceContext.getResource(ProvenanceResource.class); - } - - /** - * Locates the Template sub-resource. - * - * @return the Template sub-resource - */ - @Path("/templates") - @ApiOperation( - value = "Gets the template resource", - response = TemplateResource.class - ) - public TemplateResource getTemplateResource() { - return resourceContext.getResource(TemplateResource.class); - } - - /** - * Locates the Snippets sub-resource. - * - * @return the Snippets sub-resource - */ - @Path("/snippets") - @ApiOperation( - value = "Gets the snippet resource", - response = SnippetResource.class - ) - public SnippetResource getSnippetResource() { - return resourceContext.getResource(SnippetResource.class); - } - - /** - * Returns a 200 OK response to indicate this is a valid controller endpoint. - * - * @return An OK response with an empty entity body. - */ - @HEAD - @Produces(MediaType.APPLICATION_JSON) - public Response getControllerHead() { - if (properties.isClusterManager()) { - throw new IllegalClusterResourceRequestException("A cluster manager cannot process the request."); - } - - return Response.ok().build(); - } - - /** - * Returns the details of 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 controllerEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - // TODO - @PreAuthorize("hasRole('ROLE_NIFI')") - @ApiOperation( - value = "Returns the details about this NiFi necessary to communicate via site to site", - response = ControllerEntity.class, - authorizations = @Authorization(value = "NiFi", type = "ROLE_NIFI") - ) - @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 getController( - @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) { - - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the controller dto - final ControllerDTO controller = serviceFacade.getController(); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // build the response entity - final ControllerEntity entity = new ControllerEntity(); - entity.setRevision(revision); - entity.setController(controller); - - // generate the response - return clusterContext(noCache(Response.ok(entity))).build(); - } - - /** - * Performs a search request for this controller. - * - * @param value Search string - * @return A searchResultsEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/search-results") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Performs a search against this NiFi using the specified search term", - response = SearchResultsEntity.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 searchController(@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // query the controller - final SearchResultsDTO results = serviceFacade.searchController(value); - - // create the entity - final SearchResultsEntity entity = new SearchResultsEntity(); - entity.setSearchResultsDTO(results); - - // generate the response - return clusterContext(noCache(Response.ok(entity))).build(); - } - /** * 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. @@ -265,14 +102,13 @@ public class ControllerResource extends ApplicationResource { * Alternatively, we could have performed a PUT request. However, PUT requests are supposed to be idempotent and this endpoint is certainly not. * * @param httpServletRequest request - * @param version The revision is used to verify the client is working with the latest version of the flow. - * @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. + * @param revisionEntity The revision is used to verify the client is working with the latest version of the flow. * @return A processGroupEntity. */ @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/archive") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("archive") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Creates a new archive of this NiFi flow configuration", @@ -296,15 +132,14 @@ public class ControllerResource extends ApplicationResource { public Response createArchive( @Context HttpServletRequest httpServletRequest, @ApiParam( - value = "The revision is used to verify the client is working with the latest version of the flow.", - required = true - ) - @FormParam(VERSION) LongParameter version, - @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 - ) - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) { + value = "The revision used to verify the client is working with the latest version of the flow.", + required = true + ) Entity revisionEntity) { + + // ensure the revision was specified + if (revisionEntity == null || revisionEntity.getRevision() == null) { + throw new IllegalArgumentException("Revision must be specified."); + } // replicate if cluster manager if (properties.isClusterManager()) { @@ -318,18 +153,13 @@ public class ControllerResource extends ApplicationResource { return generateContinueResponse().build(); } - // determine the specified version - Long clientVersion = null; - if (version != null) { - clientVersion = version.getLong(); - } - // create the archive - final ConfigurationSnapshot controllerResponse = serviceFacade.createArchive(new Revision(clientVersion, clientId.getClientId())); + final RevisionDTO requestRevision = revisionEntity.getRevision(); + final ConfigurationSnapshot controllerResponse = serviceFacade.createArchive(new Revision(requestRevision.getVersion(), requestRevision.getClientId())); // create the revision final RevisionDTO updatedRevision = new RevisionDTO(); - updatedRevision.setClientId(clientId.getClientId()); + updatedRevision.setClientId(requestRevision.getClientId()); updatedRevision.setVersion(controllerResponse.getVersion()); // create the response entity @@ -341,107 +171,6 @@ public class ControllerResource extends ApplicationResource { return clusterContext(generateCreatedResponse(uri, controllerEntity)).build(); } - /** - * Gets current revision of this NiFi. - * - * @return A revisionEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/revision") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets the current revision of this NiFi", - notes = "NiFi employs an optimistic locking strategy where the client must include a revision in their request when " - + "performing an update. If the specified revision does not match the current base revision a 409 status code " - + "is returned. The revision is comprised of a clientId and a version number. The version is a simple integer " - + "value that is incremented with each change. Including the most recent version tells NiFi that your working " - + "with the most recent flow. In addition to the version the client who is performing the updates is recorded. " - + "This allows the same client to submit multiple requests without having to wait for the previously ones to " - + "return. Invoking this endpoint will return the current base revision. It is also available when retrieving " - + "a process group and in the response of all mutable requests.", - response = Entity.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 getRevision() { - // create the current revision - final RevisionDTO revision = serviceFacade.getRevision(); - - // create the response entity - final Entity entity = new Entity(); - entity.setRevision(revision); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the status for 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 controllerStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets the current status of this NiFi", - response = Entity.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 getControllerStatus( - @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) { - - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - final ControllerStatusDTO controllerStatus = serviceFacade.getControllerStatus(); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ControllerStatusEntity entity = new ControllerStatusEntity(); - entity.setRevision(revision); - entity.setControllerStatus(controllerStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Retrieves the counters report for this NiFi. * @@ -450,8 +179,8 @@ public class ControllerResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/counters") + @Produces(MediaType.APPLICATION_JSON) + @Path("counters") // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") @ApiOperation( value = "Gets the current counters for this NiFi", @@ -545,8 +274,8 @@ public class ControllerResource extends ApplicationResource { */ @PUT @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/counters/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("counters/{id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Updates the specified counter. This will reset the counter value to 0", @@ -608,8 +337,8 @@ public class ControllerResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/config") + @Produces(MediaType.APPLICATION_JSON) + @Path("config") // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_NIFI')") @ApiOperation( value = "Retrieves the configuration for this NiFi", @@ -642,7 +371,6 @@ public class ControllerResource extends ApplicationResource { } final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration(); - controllerConfig.setUri(generateResourceUri("controller")); // create the revision final RevisionDTO revision = new RevisionDTO(); @@ -657,62 +385,6 @@ public class ControllerResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Update the configuration for this NiFi. - * - * @param httpServletRequest request - * @param version The revision is used to verify the client is working with the latest version of the flow. - * @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. - * @param name The name of this controller. - * @param comments The comments of this controller. - * @param maxTimerDrivenThreadCount The maximum number of timer driven threads this controller has available. - * @param maxEventDrivenThreadCount The maximum number of timer driven threads this controller has available. - * @return A controllerConfigurationEntity. - */ - @PUT - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/config") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response updateControllerConfig( - @Context HttpServletRequest httpServletRequest, - @FormParam(VERSION) LongParameter version, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormParam("name") String name, - @FormParam("comments") String comments, - @FormParam("maxTimerDrivenThreadCount") IntegerParameter maxTimerDrivenThreadCount, - @FormParam("maxEventDrivenThreadCount") IntegerParameter maxEventDrivenThreadCount) { - - // create the controller config dto - final ControllerConfigurationDTO configDTO = new ControllerConfigurationDTO(); - configDTO.setName(name); - configDTO.setComments(comments); - - if (maxTimerDrivenThreadCount != null) { - configDTO.setMaxTimerDrivenThreadCount(maxTimerDrivenThreadCount.getInteger()); - } - - if (maxEventDrivenThreadCount != null) { - configDTO.setMaxEventDrivenThreadCount(maxEventDrivenThreadCount.getInteger()); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - if (version != null) { - revision.setVersion(version.getLong()); - } - - // create the dto entity - ControllerConfigurationEntity entity = new ControllerConfigurationEntity(); - entity.setRevision(revision); - entity.setConfig(configDTO); - - // update the controller configuration - return updateControllerConfig(httpServletRequest, entity); - } - /** * Update the configuration for this NiFi. * @@ -721,9 +393,9 @@ public class ControllerResource extends ApplicationResource { * @return A controllerConfigurationEntity. */ @PUT - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/config") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("config") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Retrieves the configuration for this NiFi", @@ -757,12 +429,7 @@ public class ControllerResource extends ApplicationResource { // replicate if cluster manager if (properties.isClusterManager()) { - // change content type to JSON for serializing entity - final Map headersToOverride = new HashMap<>(); - headersToOverride.put("content-type", MediaType.APPLICATION_JSON); - - // replicate the request - return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(configEntity), getHeaders(headersToOverride)).getResponse(); + return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(configEntity), getHeaders()).getResponse(); } // handle expects request (usually from the cluster manager) @@ -775,7 +442,6 @@ public class ControllerResource extends ApplicationResource { final ConfigurationSnapshot controllerResponse = serviceFacade.updateControllerConfiguration(new Revision(revision.getVersion(), revision.getClientId()), configEntity.getConfig()); final ControllerConfigurationDTO controllerConfig = controllerResponse.getConfiguration(); - controllerConfig.setUri(generateResourceUri("controller")); // get the updated revision final RevisionDTO updatedRevision = new RevisionDTO(); @@ -791,48 +457,7 @@ public class ControllerResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the identity of the user making the request. - * - * @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 An identityEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/identity") - @ApiOperation( - value = "Retrieves the user identity of the user making the request", - response = IdentityEntity.class - ) - public Response getIdentity( - @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) { - - // note that the cluster manager will handle this request directly - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - if (user == null) { - throw new WebApplicationException(new Throwable("Unable to access details for current user.")); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - IdentityEntity entity = new IdentityEntity(); - entity.setRevision(revision); - entity.setUserId(user.getIdentity()); - entity.setIdentity(user.getUserName()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** + /**x * Retrieves the user details, including the authorities, about the user making the request. * * @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. @@ -840,8 +465,8 @@ public class ControllerResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/authorities") + @Produces(MediaType.APPLICATION_JSON) + @Path("authorities") // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')") @ApiOperation( value = "Retrieves the user details, including the authorities, about the user making the request", @@ -890,335 +515,175 @@ public class ControllerResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // --------------- + // reporting tasks + // --------------- + /** - * Retrieves the banners for this NiFi. + * Creates a new Reporting Task. * - * @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 bannerEntity. + * @param httpServletRequest request + * @param availability Whether the reporting task is available on the NCM + * only (ncm) or on the nodes only (node). If this instance is not clustered + * all tasks should use the node availability. + * @param reportingTaskEntity A reportingTaskEntity. + * @return A reportingTaskEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("reporting-tasks/{availability}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Creates a new reporting task", + response = ReportingTaskEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 createReportingTask( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "Whether the reporting task is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", + allowableValues = "NCM, NODE", + required = true + ) + @PathParam("availability") String availability, + @ApiParam( + value = "The reporting task configuration details.", + required = true + ) ReportingTaskEntity reportingTaskEntity) { + + final Availability avail = reportingTaskResource.parseAvailability(availability); + + if (reportingTaskEntity == null || reportingTaskEntity.getReportingTask() == null) { + throw new IllegalArgumentException("Reporting task details must be specified."); + } + + if (reportingTaskEntity.getRevision() == null) { + throw new IllegalArgumentException("Revision must be specified."); + } + + if (reportingTaskEntity.getReportingTask().getId() != null) { + throw new IllegalArgumentException("Reporting task ID cannot be specified."); + } + + if (StringUtils.isBlank(reportingTaskEntity.getReportingTask().getType())) { + throw new IllegalArgumentException("The type of reporting task to create must be specified."); + } + + // get the revision + final RevisionDTO revision = reportingTaskEntity.getRevision(); + + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(reportingTaskEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // set the processor id as appropriate + final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); + if (clusterContext != null) { + reportingTaskEntity.getReportingTask().setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString()); + } else { + reportingTaskEntity.getReportingTask().setId(UUID.randomUUID().toString()); + } + + // create the reporting task and generate the json + final ConfigurationSnapshot controllerResponse = serviceFacade.createReportingTask( + new Revision(revision.getVersion(), revision.getClientId()), reportingTaskEntity.getReportingTask()); + final ReportingTaskDTO reportingTask = controllerResponse.getConfiguration(); + + // get the updated revision + final RevisionDTO updatedRevision = new RevisionDTO(); + updatedRevision.setClientId(revision.getClientId()); + updatedRevision.setVersion(controllerResponse.getVersion()); + + // build the response entity + final ReportingTaskEntity entity = new ReportingTaskEntity(); + entity.setRevision(updatedRevision); + entity.setReportingTask(reportingTaskResource.populateRemainingReportingTaskContent(availability, reportingTask)); + + // build the response + return clusterContext(generateCreatedResponse(URI.create(reportingTask.getUri()), entity)).build(); + } + + /** + * 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. + * @param availability Whether the reporting task is available on the NCM + * only (ncm) or on the nodes only (node). If this instance is not clustered + * all tasks should use the node availability. + * @return A reportingTasksEntity. */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/banners") + @Produces(MediaType.APPLICATION_JSON) + @Path("reporting-tasks/{availability}") // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") @ApiOperation( - value = "Retrieves the banners for this NiFi", - response = BannerEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } + 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.") - } + 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 getBanners( - @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) { + 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, + @ApiParam( + value = "Whether the reporting task is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", + allowableValues = "NCM, NODE", + required = true + ) + @PathParam("availability") String availability) { - // get the banner from the properties - will come from the NCM when clustered - final String bannerText = properties.getBannerText(); + final Availability avail = reportingTaskResource.parseAvailability(availability); - // create the DTO - final BannerDTO bannerDTO = new BannerDTO(); - bannerDTO.setHeaderText(bannerText); - bannerDTO.setFooterText(bannerText); + // replicate if cluster manager + if (properties.isClusterManager() && Availability.NODE.equals(avail)) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get all the reporting tasks + final Set reportingTasks = reportingTaskResource.populateRemainingReportingTasksContent(availability, serviceFacade.getReportingTasks()); // create the revision final RevisionDTO revision = new RevisionDTO(); revision.setClientId(clientId.getClientId()); // create the response entity - final BannerEntity entity = new BannerEntity(); + final ReportingTasksEntity entity = new ReportingTasksEntity(); entity.setRevision(revision); - entity.setBanners(bannerDTO); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the types of processors that this NiFi supports. - * - * @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 processorTypesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/processor-types") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Retrieves the types of processors that this NiFi supports", - response = ProcessorTypesEntity.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 getProcessorTypes( - @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(); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create response entity - final ProcessorTypesEntity entity = new ProcessorTypesEntity(); - entity.setRevision(revision); - entity.setProcessorTypes(serviceFacade.getProcessorTypes()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the types of controller services that this NiFi supports. - * - * @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. - * @param serviceType Returns only services that implement this type - * @return A controllerServicesTypesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/controller-service-types") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Retrieves the types of controller services that this NiFi supports", - response = ControllerServiceTypesEntity.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 getControllerServiceTypes( - @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, - @ApiParam( - value = "If specified, will only return controller services of this type.", - required = false - ) - @QueryParam("serviceType") String serviceType) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create response entity - final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity(); - entity.setRevision(revision); - entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes(serviceType)); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the types of reporting tasks that this NiFi supports. - * - * @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 controllerServicesTypesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/reporting-task-types") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Retrieves the types of reporting tasks that this NiFi supports", - response = ReportingTaskTypesEntity.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 getReportingTaskTypes( - @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(); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create response entity - final ReportingTaskTypesEntity entity = new ReportingTaskTypesEntity(); - entity.setRevision(revision); - entity.setReportingTaskTypes(serviceFacade.getReportingTaskTypes()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the types of prioritizers that this NiFi supports. - * - * @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 prioritizerTypesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/prioritizers") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Retrieves the types of prioritizers that this NiFi supports", - response = PrioritizerTypesEntity.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 getPrioritizers( - @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(); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create response entity - final PrioritizerTypesEntity entity = new PrioritizerTypesEntity(); - entity.setRevision(revision); - entity.setPrioritizerTypes(serviceFacade.getWorkQueuePrioritizerTypes()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves details about this NiFi to put in the About dialog. - * - * @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 An aboutEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/about") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Retrieves details about this NiFi to put in the About dialog", - response = AboutEntity.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 getAboutInfo( - @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(); - } - - final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration(); - - // create the about dto - final AboutDTO aboutDTO = new AboutDTO(); - aboutDTO.setTitle(controllerConfig.getName()); - aboutDTO.setVersion(properties.getUiTitle()); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final AboutEntity entity = new AboutEntity(); - entity.setRevision(revision); - entity.setAbout(aboutDTO); + entity.setReportingTasks(reportingTasks); // generate the response return clusterContext(generateOkResponse(entity)).build(); @@ -1233,6 +698,10 @@ public class ControllerResource extends ApplicationResource { this.clusterManager = clusterManager; } + public void setReportingTaskResource(ReportingTaskResource reportingTaskResource) { + this.reportingTaskResource = reportingTaskResource; + } + public void setProperties(NiFiProperties properties) { this.properties = properties; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java index 2cff337d45..34299ac369 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java @@ -16,14 +16,13 @@ */ package org.apache.nifi.web.api; +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.cluster.context.ClusterContext; -import org.apache.nifi.cluster.context.ClusterContextThreadLocal; import org.apache.nifi.cluster.manager.impl.WebClusterManager; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.service.ControllerServiceState; @@ -42,7 +41,6 @@ import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.entity.ComponentStateEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity; -import org.apache.nifi.web.api.entity.ControllerServicesEntity; import org.apache.nifi.web.api.entity.Entity; import org.apache.nifi.web.api.entity.PropertyDescriptorEntity; import org.apache.nifi.web.api.entity.UpdateControllerServiceReferenceRequestEntity; @@ -69,17 +67,19 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; /** * RESTful endpoint for managing a Controller Service. */ -@Path("controller-services") +@Path("/controller-services") +@Api( + value = "/controller-services", + description = "Endpoint for managing a Controller Service." +) public class ControllerServiceResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(ControllerServiceResource.class); @@ -133,7 +133,7 @@ public class ControllerServiceResource extends ApplicationResource { * @param availability avail * @return avail */ - private Availability parseAvailability(final String availability) { + public Availability parseAvailability(final String availability) { final Availability avail; try { avail = Availability.valueOf(availability.toUpperCase()); @@ -149,176 +149,6 @@ public class ControllerServiceResource extends ApplicationResource { return avail; } - /** - * Retrieves all the of controller services 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. - * @param availability Whether the controller service is available on the - * NCM only (ncm) or on the nodes only (node). If this instance is not - * clustered all services should use the node availability. - * @return A controllerServicesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("{availability}") - // 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 getControllerServices( - @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, - @ApiParam( - value = "Whether the controller service is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", - allowableValues = "NCM, NODE", - required = true - ) - @PathParam("availability") String availability) { - - final Availability avail = parseAvailability(availability); - - // replicate if cluster manager - if (properties.isClusterManager() && Availability.NODE.equals(avail)) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get all the controller services - final Set controllerServices = populateRemainingControllerServicesContent(availability, serviceFacade.getControllerServices()); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ControllerServicesEntity entity = new ControllerServicesEntity(); - entity.setRevision(revision); - entity.setControllerServices(controllerServices); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Creates a new Controller Service. - * - * @param httpServletRequest request - * @param availability Whether the controller service is available on the - * NCM only (ncm) or on the nodes only (node). If this instance is not - * clustered all services should use the node availability. - * @param controllerServiceEntity A controllerServiceEntity. - * @return A controllerServiceEntity. - */ - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @Path("{availability}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Creates a new controller service", - response = ControllerServiceEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 createControllerService( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "Whether the controller service is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", - allowableValues = "NCM, NODE", - required = true - ) - @PathParam("availability") String availability, - @ApiParam( - value = "The controller service configuration details.", - required = true - ) ControllerServiceEntity controllerServiceEntity) { - - final Availability avail = parseAvailability(availability); - - if (controllerServiceEntity == null || controllerServiceEntity.getControllerService() == null) { - throw new IllegalArgumentException("Controller service details must be specified."); - } - - if (controllerServiceEntity.getRevision() == null) { - throw new IllegalArgumentException("Revision must be specified."); - } - - if (controllerServiceEntity.getControllerService().getId() != null) { - throw new IllegalArgumentException("Controller service ID cannot be specified."); - } - - if (StringUtils.isBlank(controllerServiceEntity.getControllerService().getType())) { - throw new IllegalArgumentException("The type of controller service to create must be specified."); - } - - // get the revision - final RevisionDTO revision = controllerServiceEntity.getRevision(); - - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(controllerServiceEntity), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // set the processor id as appropriate - final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); - if (clusterContext != null) { - controllerServiceEntity.getControllerService().setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString()); - } else { - controllerServiceEntity.getControllerService().setId(UUID.randomUUID().toString()); - } - - // create the controller service and generate the json - final ConfigurationSnapshot controllerResponse = serviceFacade.createControllerService( - new Revision(revision.getVersion(), revision.getClientId()), controllerServiceEntity.getControllerService()); - final ControllerServiceDTO controllerService = controllerResponse.getConfiguration(); - - // get the updated revision - final RevisionDTO updatedRevision = new RevisionDTO(); - updatedRevision.setClientId(revision.getClientId()); - updatedRevision.setVersion(controllerResponse.getVersion()); - - // build the response entity - final ControllerServiceEntity entity = new ControllerServiceEntity(); - entity.setRevision(updatedRevision); - entity.setControllerService(populateRemainingControllerServiceContent(availability, controllerService)); - - // build the response - return clusterContext(generateCreatedResponse(URI.create(controllerService.getUri()), entity)).build(); - } - /** * Retrieves the specified controller service. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowFileQueueResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowFileQueueResource.java new file mode 100644 index 0000000000..6874ad55c9 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowFileQueueResource.java @@ -0,0 +1,755 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +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.cluster.context.ClusterContext; +import org.apache.nifi.cluster.context.ClusterContextThreadLocal; +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.stream.io.StreamUtils; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.DownloadableContent; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.DropRequestDTO; +import org.apache.nifi.web.api.dto.FlowFileDTO; +import org.apache.nifi.web.api.dto.FlowFileSummaryDTO; +import org.apache.nifi.web.api.dto.ListingRequestDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.DropRequestEntity; +import org.apache.nifi.web.api.entity.FlowFileEntity; +import org.apache.nifi.web.api.entity.ListingRequestEntity; +import org.apache.nifi.web.api.request.ClientIdParameter; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.StreamingOutput; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * RESTful endpoint for managing a flowfile queue. + */ +@Path("/flowfile-queues") +@Api( + value = "/flowfile-queues", + description = "Endpoint for managing a FlowFile Queue." +) +public class FlowFileQueueResource extends ApplicationResource { + + private NiFiServiceFacade serviceFacade; + private WebClusterManager clusterManager; + private NiFiProperties properties; + + /** + * Populate the URIs for the specified flowfile listing. + * + * @param connectionId connection + * @param flowFileListing flowfile listing + * @return dto + */ + public ListingRequestDTO populateRemainingFlowFileListingContent(final String connectionId, final ListingRequestDTO flowFileListing) { + // uri of the listing + flowFileListing.setUri(generateResourceUri("flowfile-queues", connectionId, "listing-requests", flowFileListing.getId())); + + // uri of each flowfile + if (flowFileListing.getFlowFileSummaries() != null) { + for (FlowFileSummaryDTO flowFile : flowFileListing.getFlowFileSummaries()) { + populateRemainingFlowFileContent(connectionId, flowFile); + } + } + return flowFileListing; + } + + /** + * Populate the URIs for the specified flowfile. + * + * @param connectionId the connection id + * @param flowFile the flowfile + * @return the dto + */ + public FlowFileSummaryDTO populateRemainingFlowFileContent(final String connectionId, final FlowFileSummaryDTO flowFile) { + flowFile.setUri(generateResourceUri("flowfile-queues", connectionId, "flowfiles", flowFile.getUuid())); + return flowFile; + } + + /** + * Gets the specified flowfile from the specified connection. + * + * @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. + * @param connectionId The connection id + * @param flowFileUuid The flowfile uuid + * @param clusterNodeId The cluster node id where the flowfile resides + * @return a flowFileDTO + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/flowfiles/{flowfile-uuid}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Gets a FlowFile from a Connection.", + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 getFlowFile( + @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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The flowfile uuid.", + required = true + ) + @PathParam("flowfile-uuid") String flowFileUuid, + @ApiParam( + value = "The id of the node where the content exists if clustered.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + throw new IllegalArgumentException("The id of the node in the cluster is required."); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the flowfile + final FlowFileDTO flowfileDto = serviceFacade.getFlowFile(connectionId, flowFileUuid); + populateRemainingFlowFileContent(connectionId, flowfileDto); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final FlowFileEntity entity = new FlowFileEntity(); + entity.setRevision(revision); + entity.setFlowFile(flowfileDto); + + return generateOkResponse(entity).build(); + } + + /** + * Gets the content for the specified flowfile in the specified connection. + * + * @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. + * @param connectionId The connection id + * @param flowFileUuid The flowfile uuid + * @param clusterNodeId The cluster node id + * @return The content stream + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.WILDCARD) + @Path("{connection-id}/flowfiles/{flowfile-uuid}/content") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Gets the content for a FlowFile in a Connection.", + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 downloadFlowFileContent( + @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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The flowfile uuid.", + required = true + ) + @PathParam("flowfile-uuid") String flowFileUuid, + @ApiParam( + value = "The id of the node where the content exists if clustered.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + throw new IllegalArgumentException("The id of the node in the cluster is required."); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the uri of the request + final String uri = generateResourceUri("flowfile-queues", connectionId, "flowfiles", flowFileUuid, "content"); + + // get an input stream to the content + final DownloadableContent content = serviceFacade.getContent(connectionId, flowFileUuid, uri); + + // generate a streaming response + final StreamingOutput response = new StreamingOutput() { + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + try (InputStream is = content.getContent()) { + // stream the content to the response + StreamUtils.copy(is, output); + + // flush the response + output.flush(); + } + } + }; + + // use the appropriate content type + String contentType = content.getType(); + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + + return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); + } + + /** + * Creates a request to list the flowfiles in the queue of the specified connection. + * + * @param httpServletRequest request + * @param id The id of the connection + * @return A listRequestEntity + */ + @POST + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/listing-requests") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Lists the contents of the queue in this connection.", + response = ListingRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."), + @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 = 404, message = "The specified resource could not be found."), + @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 createFlowFileListing( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + serviceFacade.verifyListQueue(id); + return generateContinueResponse().build(); + } + + // ensure the id is the same across the cluster + final String listingRequestId; + final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); + if (clusterContext != null) { + listingRequestId = UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString(); + } else { + listingRequestId = UUID.randomUUID().toString(); + } + + // submit the listing request + final ListingRequestDTO listingRequest = serviceFacade.createFlowFileListingRequest(id, listingRequestId); + populateRemainingFlowFileListingContent(id, listingRequest); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + + // create the response entity + final ListingRequestEntity entity = new ListingRequestEntity(); + entity.setRevision(revision); + entity.setListingRequest(listingRequest); + + // generate the URI where the response will be + final URI location = URI.create(listingRequest.getUri()); + return Response.status(Status.ACCEPTED).location(location).entity(entity).build(); + } + + /** + * Checks the status of an outstanding listing request. + * + * @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. + * @param connectionId The id of the connection + * @param listingRequestId The id of the drop request + * @return A dropRequestEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/listing-requests/{listing-request-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Gets the current status of a listing request for the specified connection.", + response = ListingRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 getListingRequest( + @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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The listing request id.", + required = true + ) + @PathParam("listing-request-id") String listingRequestId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the listing request + final ListingRequestDTO listingRequest = serviceFacade.getFlowFileListingRequest(connectionId, listingRequestId); + populateRemainingFlowFileListingContent(connectionId, listingRequest); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final ListingRequestEntity entity = new ListingRequestEntity(); + entity.setRevision(revision); + entity.setListingRequest(listingRequest); + + return generateOkResponse(entity).build(); + } + + /** + * Deletes the specified listing request. + * + * @param httpServletRequest request + * @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. + * @param connectionId The connection id + * @param listingRequestId The drop request id + * @return A dropRequestEntity + */ + @DELETE + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/listing-requests/{listing-request-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Cancels and/or removes a request to list the contents of this connection.", + response = DropRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 deleteListingRequest( + @Context 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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The listing request id.", + required = true + ) + @PathParam("listing-request-id") String listingRequestId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // delete the listing request + final ListingRequestDTO listingRequest = serviceFacade.deleteFlowFileListingRequest(connectionId, listingRequestId); + + // prune the results as they were already received when the listing completed + listingRequest.setFlowFileSummaries(null); + + // populate remaining content + populateRemainingFlowFileListingContent(connectionId, listingRequest); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final ListingRequestEntity entity = new ListingRequestEntity(); + entity.setRevision(revision); + entity.setListingRequest(listingRequest); + + return generateOkResponse(entity).build(); + } + + /** + * Creates a request to delete the flowfiles in the queue of the specified connection. + * + * @param httpServletRequest request + * @param id The id of the connection + * @return A dropRequestEntity + */ + @POST + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/drop-requests") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Creates a request to drop the contents of the queue in this connection.", + response = DropRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @ApiResponses( + value = { + @ApiResponse(code = 202, message = "The request has been accepted. A HTTP response header will contain the URI where the response can be polled."), + @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 = 404, message = "The specified resource could not be found."), + @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 createDropRequest( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // ensure the id is the same across the cluster + final String dropRequestId; + final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); + if (clusterContext != null) { + dropRequestId = UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString(); + } else { + dropRequestId = UUID.randomUUID().toString(); + } + + // submit the drop request + final DropRequestDTO dropRequest = serviceFacade.createFlowFileDropRequest(id, dropRequestId); + dropRequest.setUri(generateResourceUri("flowfile-queues", id, "drop-requests", dropRequest.getId())); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + + // create the response entity + final DropRequestEntity entity = new DropRequestEntity(); + entity.setRevision(revision); + entity.setDropRequest(dropRequest); + + // generate the URI where the response will be + final URI location = URI.create(dropRequest.getUri()); + return Response.status(Status.ACCEPTED).location(location).entity(entity).build(); + } + + /** + * Checks the status of an outstanding drop request. + * + * @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. + * @param connectionId The id of the connection + * @param dropRequestId The id of the drop request + * @return A dropRequestEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/drop-requests/{drop-request-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Gets the current status of a drop request for the specified connection.", + response = DropRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 getDropRequest( + @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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The drop request id.", + required = true + ) + @PathParam("drop-request-id") String dropRequestId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the drop request + final DropRequestDTO dropRequest = serviceFacade.getFlowFileDropRequest(connectionId, dropRequestId); + dropRequest.setUri(generateResourceUri("flowfile-queues", connectionId, "drop-requests", dropRequestId)); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final DropRequestEntity entity = new DropRequestEntity(); + entity.setRevision(revision); + entity.setDropRequest(dropRequest); + + return generateOkResponse(entity).build(); + } + + /** + * Deletes the specified drop request. + * + * @param httpServletRequest request + * @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. + * @param connectionId The connection id + * @param dropRequestId The drop request id + * @return A dropRequestEntity + */ + @DELETE + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{connection-id}/drop-requests/{drop-request-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Cancels and/or removes a request to drop the contents of this connection.", + response = DropRequestEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 removeDropRequest( + @Context 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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("connection-id") String connectionId, + @ApiParam( + value = "The drop request id.", + required = true + ) + @PathParam("drop-request-id") String dropRequestId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // delete the drop request + final DropRequestDTO dropRequest = serviceFacade.deleteFlowFileDropRequest(connectionId, dropRequestId); + dropRequest.setUri(generateResourceUri("flowfile-queues", connectionId, "drop-requests", dropRequestId)); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final DropRequestEntity entity = new DropRequestEntity(); + entity.setRevision(revision); + entity.setDropRequest(dropRequest); + + return generateOkResponse(entity).build(); + } + + // setters + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + + public void setClusterManager(WebClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java new file mode 100644 index 0000000000..e1beecc650 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -0,0 +1,1653 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +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.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.user.NiFiUser; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.NiFiServiceFacade; +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.RevisionDTO; +import org.apache.nifi.web.api.dto.search.SearchResultsDTO; +import org.apache.nifi.web.api.dto.status.ConnectionStatusDTO; +import org.apache.nifi.web.api.dto.status.ControllerStatusDTO; +import org.apache.nifi.web.api.dto.status.NodeProcessGroupStatusSnapshotDTO; +import org.apache.nifi.web.api.dto.status.PortStatusDTO; +import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO; +import org.apache.nifi.web.api.dto.status.ProcessGroupStatusSnapshotDTO; +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.AboutEntity; +import org.apache.nifi.web.api.entity.BannerEntity; +import org.apache.nifi.web.api.entity.BulletinBoardEntity; +import org.apache.nifi.web.api.entity.ConnectionStatusEntity; +import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity; +import org.apache.nifi.web.api.entity.ControllerStatusEntity; +import org.apache.nifi.web.api.entity.Entity; +import org.apache.nifi.web.api.entity.IdentityEntity; +import org.apache.nifi.web.api.entity.PortStatusEntity; +import org.apache.nifi.web.api.entity.PrioritizerTypesEntity; +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.ReportingTaskTypesEntity; +import org.apache.nifi.web.api.entity.SearchResultsEntity; +import org.apache.nifi.web.api.entity.StatusHistoryEntity; +import org.apache.nifi.web.api.request.BulletinBoardPatternParameter; +import org.apache.nifi.web.api.request.ClientIdParameter; +import org.apache.nifi.web.api.request.IntegerParameter; +import org.apache.nifi.web.api.request.LongParameter; +import org.apache.nifi.web.security.user.NiFiUserUtils; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.HashSet; +import java.util.Set; + +/** + * RESTful endpoint for managing a Flow. + */ +@Path("/flow") +@Api( + value = "/flow", + description = "Endpoint for accessing the flow structure and component statuses." +) +public class FlowResource extends ApplicationResource { + + private static final String RECURSIVE = "false"; + + private NiFiServiceFacade serviceFacade; + private WebClusterManager clusterManager; + private NiFiProperties properties; + + @Context + private ResourceContext resourceContext; + + /** + * Performs a search request in this flow. + * + * @param value Search string + * @return A searchResultsEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("search-results") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Performs a search against this NiFi using the specified search term", + response = SearchResultsEntity.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 searchFlow(@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // query the controller + final SearchResultsDTO results = serviceFacade.searchController(value); + + // create the entity + final SearchResultsEntity entity = new SearchResultsEntity(); + entity.setSearchResultsDTO(results); + + // generate the response + return clusterContext(noCache(Response.ok(entity))).build(); + } + + /** + * Gets current revision of this NiFi. + * + * @return A revisionEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("revision") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets the current revision of this NiFi", + notes = "NiFi employs an optimistic locking strategy where the client must include a revision in their request when " + + "performing an update. If the specified revision does not match the current base revision a 409 status code " + + "is returned. The revision is comprised of a clientId and a version number. The version is a simple integer " + + "value that is incremented with each change. Including the most recent version tells NiFi that your working " + + "with the most recent flow. In addition to the version the client who is performing the updates is recorded. " + + "This allows the same client to submit multiple requests without having to wait for the previously ones to " + + "return. Invoking this endpoint will return the current base revision. It is also available when retrieving " + + "a process group and in the response of all mutable requests.", + response = Entity.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 getRevision() { + // create the current revision + final RevisionDTO revision = serviceFacade.getRevision(); + + // create the response entity + final Entity entity = new Entity(); + entity.setRevision(revision); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the status for 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 controllerStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets the current status of this NiFi", + response = Entity.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 getControllerStatus( + @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) { + + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + final ControllerStatusDTO controllerStatus = serviceFacade.getControllerStatus(); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final ControllerStatusEntity entity = new ControllerStatusEntity(); + entity.setRevision(revision); + entity.setControllerStatus(controllerStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the identity of the user making the request. + * + * @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 An identityEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("identity") + @ApiOperation( + value = "Retrieves the user identity of the user making the request", + response = IdentityEntity.class + ) + public Response getIdentity( + @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) { + + // note that the cluster manager will handle this request directly + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user == null) { + throw new WebApplicationException(new Throwable("Unable to access details for current user.")); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + IdentityEntity entity = new IdentityEntity(); + entity.setRevision(revision); + entity.setUserId(user.getIdentity()); + entity.setIdentity(user.getUserName()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the banners for 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 bannerEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("banners") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves the banners for this NiFi", + response = BannerEntity.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 getBanners( + @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) { + + // get the banner from the properties - will come from the NCM when clustered + final String bannerText = properties.getBannerText(); + + // create the DTO + final BannerDTO bannerDTO = new BannerDTO(); + bannerDTO.setHeaderText(bannerText); + bannerDTO.setFooterText(bannerText); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final BannerEntity entity = new BannerEntity(); + entity.setRevision(revision); + entity.setBanners(bannerDTO); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the types of processors that this NiFi supports. + * + * @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 processorTypesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("processor-types") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves the types of processors that this NiFi supports", + response = ProcessorTypesEntity.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 getProcessorTypes( + @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(); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create response entity + final ProcessorTypesEntity entity = new ProcessorTypesEntity(); + entity.setRevision(revision); + entity.setProcessorTypes(serviceFacade.getProcessorTypes()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the types of controller services that this NiFi supports. + * + * @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. + * @param serviceType Returns only services that implement this type + * @return A controllerServicesTypesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("controller-service-types") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves the types of controller services that this NiFi supports", + response = ControllerServiceTypesEntity.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 getControllerServiceTypes( + @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, + @ApiParam( + value = "If specified, will only return controller services of this type.", + required = false + ) + @QueryParam("serviceType") String serviceType) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create response entity + final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity(); + entity.setRevision(revision); + entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes(serviceType)); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the types of reporting tasks that this NiFi supports. + * + * @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 controllerServicesTypesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("reporting-task-types") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves the types of reporting tasks that this NiFi supports", + response = ReportingTaskTypesEntity.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 getReportingTaskTypes( + @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(); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create response entity + final ReportingTaskTypesEntity entity = new ReportingTaskTypesEntity(); + entity.setRevision(revision); + entity.setReportingTaskTypes(serviceFacade.getReportingTaskTypes()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the types of prioritizers that this NiFi supports. + * + * @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 prioritizerTypesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("prioritizers") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves the types of prioritizers that this NiFi supports", + response = PrioritizerTypesEntity.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 getPrioritizers( + @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(); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create response entity + final PrioritizerTypesEntity entity = new PrioritizerTypesEntity(); + entity.setRevision(revision); + entity.setPrioritizerTypes(serviceFacade.getWorkQueuePrioritizerTypes()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves details about this NiFi to put in the About dialog. + * + * @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 An aboutEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("about") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Retrieves details about this NiFi to put in the About dialog", + response = AboutEntity.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 getAboutInfo( + @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(); + } + + final ControllerConfigurationDTO controllerConfig = serviceFacade.getControllerConfiguration(); + + // create the about dto + final AboutDTO aboutDTO = new AboutDTO(); + aboutDTO.setTitle(controllerConfig.getName()); + aboutDTO.setVersion(properties.getUiTitle()); + aboutDTO.setUri(generateResourceUri()); + + // get the content viewer url + aboutDTO.setContentViewerUrl(properties.getProperty(NiFiProperties.CONTENT_VIEWER_URL)); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final AboutEntity entity = new AboutEntity(); + entity.setRevision(revision); + entity.setAbout(aboutDTO); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // -------------- + // bulletin board + // -------------- + + /** + * Retrieves all the of templates 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. + * @param after Supporting querying for bulletins after a particular + * bulletin id. + * @param limit The max number of bulletins to return. + * @param sourceName Source name filter. Supports a regular expression. + * @param message Message filter. Supports a regular expression. + * @param sourceId Source id filter. Supports a regular expression. + * @param groupId Group id filter. Supports a regular expression. + * @return A bulletinBoardEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("bulletin-board") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets current bulletins", + response = BulletinBoardEntity.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 getBulletinBoard( + @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, + @ApiParam( + value = "Includes bulletins with an id after this value.", + required = false + ) + @QueryParam("after") LongParameter after, + @ApiParam( + value = "Includes bulletins originating from this sources whose name match this regular expression.", + required = false + ) + @QueryParam("sourceName") BulletinBoardPatternParameter sourceName, + @ApiParam( + value = "Includes bulletins whose message that match this regular expression.", + required = false + ) + @QueryParam("message") BulletinBoardPatternParameter message, + @ApiParam( + value = "Includes bulletins originating from this sources whose id match this regular expression.", + required = false + ) + @QueryParam("sourceId") BulletinBoardPatternParameter sourceId, + @ApiParam( + value = "Includes bulletins originating from this sources whose group id match this regular expression.", + required = false + ) + @QueryParam("groupId") BulletinBoardPatternParameter groupId, + @ApiParam( + value = "The number of bulletins to limit the response to.", + required = false + ) + @QueryParam("limit") IntegerParameter limit) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // build the bulletin query + final BulletinQueryDTO query = new BulletinQueryDTO(); + + if (sourceId != null) { + query.setSourceId(sourceId.getRawPattern()); + } + if (groupId != null) { + query.setGroupId(groupId.getRawPattern()); + } + if (sourceName != null) { + query.setName(sourceName.getRawPattern()); + } + if (message != null) { + query.setMessage(message.getRawPattern()); + } + if (after != null) { + query.setAfter(after.getLong()); + } + if (limit != null) { + query.setLimit(limit.getInteger()); + } + + // get the bulletin board + final BulletinBoardDTO bulletinBoard = serviceFacade.getBulletinBoard(query); + + // create the revision + RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + BulletinBoardEntity entity = new BulletinBoardEntity(); + entity.setRevision(revision); + entity.setBulletinBoard(bulletinBoard); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // ------ + // status + // ------ + + /** + * Retrieves the specified processor status. + * + * @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. + * @param id The id of the processor history to retrieve. + * @return A processorStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("processors/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status for a processor", + response = ProcessorStatusEntity.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 = 404, message = "The specified resource could not be found."), + @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 getProcessorStatus( + @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, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The processor id.", + required = true + ) + @PathParam("id") String id) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final ProcessorStatusEntity entity = (ProcessorStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getProcessorStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the specified processor status + final ProcessorStatusDTO processorStatus = serviceFacade.getProcessorStatus(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final ProcessorStatusEntity entity = new ProcessorStatusEntity(); + entity.setRevision(revision); + entity.setProcessorStatus(processorStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified input port status. + * + * @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. + * @param id The id of the processor history to retrieve. + * @return A portStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("input-ports/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status for an input port", + response = PortStatusEntity.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 = 404, message = "The specified resource could not be found."), + @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 getInputPortStatus( + @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, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The input port id.", + required = true + ) + @PathParam("id") String id) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final PortStatusEntity entity = (PortStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getPortStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the specified input port status + final PortStatusDTO portStatus = serviceFacade.getInputPortStatus(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final PortStatusEntity entity = new PortStatusEntity(); + entity.setRevision(revision); + entity.setPortStatus(portStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified output port status. + * + * @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. + * @param id The id of the processor history to retrieve. + * @return A portStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("output-ports/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status for an output port", + response = PortStatusEntity.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 = 404, message = "The specified resource could not be found."), + @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 getOutputPortStatus( + @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, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The output port id.", + required = true + ) + @PathParam("id") String id) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final PortStatusEntity entity = (PortStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getPortStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the specified output port status + final PortStatusDTO portStatus = serviceFacade.getOutputPortStatus(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final PortStatusEntity entity = new PortStatusEntity(); + entity.setRevision(revision); + entity.setPortStatus(portStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified remote process group status. + * + * @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. + * @param id The id of the processor history to retrieve. + * @return A remoteProcessGroupStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("remote-process-groups/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status for a remote process group", + response = ProcessorStatusEntity.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 = 404, message = "The specified resource could not be found."), + @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 getRemoteProcessGroupStatus( + @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, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The remote process group id.", + required = true + ) + @PathParam("id") String id) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final RemoteProcessGroupStatusEntity entity = (RemoteProcessGroupStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getRemoteProcessGroupStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the specified remote process group status + final RemoteProcessGroupStatusDTO remoteProcessGroupStatus = serviceFacade.getRemoteProcessGroupStatus(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final RemoteProcessGroupStatusEntity entity = new RemoteProcessGroupStatusEntity(); + entity.setRevision(revision); + entity.setRemoteProcessGroupStatus(remoteProcessGroupStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the status report for 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. + * @param recursive Optional recursive flag that defaults to false. If set to true, all descendant groups and the status of their content will be included. + * @param groupId The group id + * @return A processGroupStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("process-groups/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_NIFI')") + @ApiOperation( + value = "Gets the status for a process group", + notes = "The status for a process group includes status for all descendent components. When invoked on the root group with " + + "recursive set to true, it will return the current status of every component in the flow.", + response = ProcessGroupStatusEntity.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 = "NiFi", type = "ROLE_NIFI") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 getProcessGroupStatus( + @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, + @ApiParam( + value = "Whether all descendant groups and the status of their content will be included. Optional, defaults to false", + required = false + ) + @QueryParam("recursive") @DefaultValue(RECURSIVE) Boolean recursive, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final ProcessGroupStatusEntity entity = (ProcessGroupStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getProcessGroupStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the status + final ProcessGroupStatusDTO statusReport = serviceFacade.getProcessGroupStatus(groupId); + + // prune the response as necessary + if (!recursive) { + pruneChildGroups(statusReport.getAggregateSnapshot()); + if (statusReport.getNodeSnapshots() != null) { + for (final NodeProcessGroupStatusSnapshotDTO nodeSnapshot : statusReport.getNodeSnapshots()) { + pruneChildGroups(nodeSnapshot.getStatusSnapshot()); + } + } + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final ProcessGroupStatusEntity entity = new ProcessGroupStatusEntity(); + entity.setRevision(revision); + entity.setProcessGroupStatus(statusReport); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + private void pruneChildGroups(final ProcessGroupStatusSnapshotDTO snapshot) { + for (final ProcessGroupStatusSnapshotDTO childProcessGroupStatus : snapshot.getProcessGroupStatusSnapshots()) { + childProcessGroupStatus.setConnectionStatusSnapshots(null); + childProcessGroupStatus.setProcessGroupStatusSnapshots(null); + childProcessGroupStatus.setInputPortStatusSnapshots(null); + childProcessGroupStatus.setOutputPortStatusSnapshots(null); + childProcessGroupStatus.setProcessorStatusSnapshots(null); + childProcessGroupStatus.setRemoteProcessGroupStatusSnapshots(null); + } + } + + /** + * Retrieves the specified connection status. + * + * @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. + * @param id The id of the connection history to retrieve. + * @return A connectionStatusEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("connections/{id}/status") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status for a connection", + response = ConnectionStatusEntity.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 = 404, message = "The specified resource could not be found."), + @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 getConnectionStatus( + @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, + @ApiParam( + value = "Whether or not to include the breakdown per node. Optional, defaults to false", + required = false + ) + @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, + @ApiParam( + value = "The id of the node where to get the status.", + required = false + ) + @QueryParam("clusterNodeId") String clusterNodeId, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("id") String id) { + + // ensure a valid request + if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { + throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); + } + + if (properties.isClusterManager()) { + // determine where this request should be sent + if (clusterNodeId == null) { + final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); + final ConnectionStatusEntity entity = (ConnectionStatusEntity) nodeResponse.getUpdatedEntity(); + + // ensure there is an updated entity (result of merging) and prune the response as necessary + if (entity != null && !nodewise) { + entity.getConnectionStatus().setNodeSnapshots(null); + } + + return nodeResponse.getResponse(); + } else { + // get the target node and ensure it exists + final Node targetNode = clusterManager.getNode(clusterNodeId); + if (targetNode == null) { + throw new UnknownNodeException("The specified cluster node does not exist."); + } + + final Set targetNodes = new HashSet<>(); + targetNodes.add(targetNode.getNodeId()); + + // replicate the request to the specific node + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + } + } + + // get the specified connection status + final ConnectionStatusDTO connectionStatus = serviceFacade.getConnectionStatus(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final ConnectionStatusEntity entity = new ConnectionStatusEntity(); + entity.setRevision(revision); + entity.setConnectionStatus(connectionStatus); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // -------------- + // status history + // -------------- + + /** + * Retrieves the specified processor status history. + * + * @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. + * @param id The id of the processor history to retrieve. + * @return A statusHistoryEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("processors/{id}/status/history") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status history for a processor", + response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), + @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 getProcessorStatusHistory( + @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, + @ApiParam( + value = "The processor id.", + required = true + ) + @PathParam("id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the specified processor status history + final StatusHistoryDTO processorStatusHistory = serviceFacade.getProcessorStatusHistory(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final StatusHistoryEntity entity = new StatusHistoryEntity(); + entity.setRevision(revision); + entity.setStatusHistory(processorStatusHistory); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified remote process groups status history. + * + * @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. + * @param groupId The group id + * @return A processorEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("process-groups/{id}/status/history") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets status history for a remote process group", + response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), + @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 getProcessGroupStatusHistory( + @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the specified processor status history + final StatusHistoryDTO processGroupStatusHistory = serviceFacade.getProcessGroupStatusHistory(groupId); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final StatusHistoryEntity entity = new StatusHistoryEntity(); + entity.setRevision(revision); + entity.setStatusHistory(processGroupStatusHistory); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified remote process groups status history. + * + * @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. + * @param id The id of the remote process group to retrieve the status fow. + * @return A statusHistoryEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("remote-process-groups/{id}/status/history") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets the status history", + response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), + @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 getRemoteProcessGroupStatusHistory( + @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, + @ApiParam( + value = "The remote process group id.", + required = true + ) + @PathParam("id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the specified processor status history + final StatusHistoryDTO remoteProcessGroupStatusHistory = serviceFacade.getRemoteProcessGroupStatusHistory(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final StatusHistoryEntity entity = new StatusHistoryEntity(); + entity.setRevision(revision); + entity.setStatusHistory(remoteProcessGroupStatusHistory); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Retrieves the specified connection status history. + * + * @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. + * @param id The id of the connection to retrieve. + * @return A statusHistoryEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("connections/{id}/status/history") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets the status history for a connection", + response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), + @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 getConnectionStatusHistory( + @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, + @ApiParam( + value = "The connection id.", + required = true + ) + @PathParam("id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the specified processor status history + final StatusHistoryDTO connectionStatusHistory = serviceFacade.getConnectionStatusHistory(id); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // generate the response entity + final StatusHistoryEntity entity = new StatusHistoryEntity(); + entity.setRevision(revision); + entity.setStatusHistory(connectionStatusHistory); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // setters + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + + public void setClusterManager(WebClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java index 541241c62e..1c4a0eab9b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.web.api; +import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; @@ -57,7 +58,11 @@ import java.util.Set; /** * RESTful endpoint for managing a Funnel. */ -@Path("funnels") +@Path("/funnels") +@Api( + value = "/funnel", + description = "Endpoint for managing a Funnel." +) public class FunnelResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(FunnelResource.class); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java index 47c2b17ec6..1e8c9b7311 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.web.api; +import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; @@ -48,7 +49,11 @@ import javax.ws.rs.core.Response; /** * RESTful endpoint for querying the history of this Controller. */ -@Path("history") +@Path("/history") +@Api( + value = "/history", + description = "Endpoint for accessing flow history." +) public class HistoryResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java index 568628eaf0..1d0629fadc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java @@ -16,26 +16,21 @@ */ package org.apache.nifi.web.api; +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.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.PortDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.status.PortStatusDTO; import org.apache.nifi.web.api.entity.InputPortEntity; -import org.apache.nifi.web.api.entity.PortStatusEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.LongParameter; @@ -55,14 +50,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; /** * RESTful endpoint for managing an Input Port. */ -@Path("input-ports") +@Path("/input-ports") +@Api( + value = "/input-ports", + description = "Endpoint for managing an Input Port." +) public class InputPortResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; @@ -153,106 +151,6 @@ public class InputPortResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the specified input port status. - * - * @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. - * @param id The id of the processor history to retrieve. - * @return A portStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status for an input port", - response = PortStatusEntity.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 = 404, message = "The specified resource could not be found."), - @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 getInputPortStatus( - @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, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The input port id.", - required = true - ) - @PathParam("id") String id) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final PortStatusEntity entity = (PortStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getPortStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the specified input port status - final PortStatusDTO portStatus = serviceFacade.getInputPortStatus(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final PortStatusEntity entity = new PortStatusEntity(); - entity.setRevision(revision); - entity.setPortStatus(portStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Updates the specified input port. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java index 4cd60f7320..411bad2e72 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java @@ -16,6 +16,7 @@ */ package org.apache.nifi.web.api; +import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; @@ -57,7 +58,11 @@ import java.util.Set; /** * RESTful endpoint for managing a Label. */ -@Path("labels") +@Path("/labels") +@Api( + value = "/labels", + description = "Endpoint for managing a Label." +) public class LabelResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(LabelResource.class); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java index 0d555255c2..13353908f8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java @@ -16,26 +16,21 @@ */ package org.apache.nifi.web.api; +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.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.PortDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.status.PortStatusDTO; import org.apache.nifi.web.api.entity.OutputPortEntity; -import org.apache.nifi.web.api.entity.PortStatusEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.LongParameter; import org.slf4j.Logger; @@ -57,14 +52,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; /** * RESTful endpoint for managing an Output Port. */ -@Path("output-ports") +@Path("/output-ports") +@Api( + value = "/output-ports", + description = "Endpoint for managing an Output Port." +) public class OutputPortResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(OutputPortResource.class); @@ -157,106 +155,6 @@ public class OutputPortResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the specified output port status. - * - * @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. - * @param id The id of the processor history to retrieve. - * @return A portStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status for an output port", - response = PortStatusEntity.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 = 404, message = "The specified resource could not be found."), - @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 getOutputPortStatus( - @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, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The output port id.", - required = true - ) - @PathParam("id") String id) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final PortStatusEntity entity = (PortStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getPortStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the specified output port status - final PortStatusDTO portStatus = serviceFacade.getOutputPortStatus(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final PortStatusEntity entity = new PortStatusEntity(); - entity.setRevision(revision); - entity.setPortStatus(portStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Updates the specified output port. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java index 1154a391f8..208d30db9f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java @@ -17,6 +17,8 @@ package org.apache.nifi.web.api; import com.sun.jersey.api.core.ResourceContext; +import com.sun.jersey.multipart.FormDataParam; +import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; @@ -25,16 +27,13 @@ import com.wordnik.swagger.annotations.Authorization; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.cluster.context.ClusterContext; import org.apache.nifi.cluster.context.ClusterContextThreadLocal; -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.ConnectionDTO; +import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.FlowSnippetDTO; import org.apache.nifi.web.api.dto.FunnelDTO; import org.apache.nifi.web.api.dto.LabelDTO; @@ -43,38 +42,43 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.status.NodeProcessGroupStatusSnapshotDTO; -import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO; -import org.apache.nifi.web.api.dto.status.ProcessGroupStatusSnapshotDTO; -import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; +import org.apache.nifi.web.api.dto.SnippetDTO; +import org.apache.nifi.web.api.dto.TemplateDTO; 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.CopySnippetRequestEntity; +import org.apache.nifi.web.api.entity.CreateTemplateRequestEntity; import org.apache.nifi.web.api.entity.FlowSnippetEntity; import org.apache.nifi.web.api.entity.FunnelEntity; import org.apache.nifi.web.api.entity.FunnelsEntity; import org.apache.nifi.web.api.entity.InputPortEntity; 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.LabelsEntity; import org.apache.nifi.web.api.entity.OutputPortEntity; import org.apache.nifi.web.api.entity.OutputPortsEntity; import org.apache.nifi.web.api.entity.ProcessGroupEntity; -import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity; import org.apache.nifi.web.api.entity.ProcessGroupsEntity; import org.apache.nifi.web.api.entity.ProcessorEntity; import org.apache.nifi.web.api.entity.ProcessorsEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupsEntity; -import org.apache.nifi.web.api.entity.StatusHistoryEntity; +import org.apache.nifi.web.api.entity.SnippetEntity; +import org.apache.nifi.web.api.entity.TemplateEntity; +import org.apache.nifi.web.api.entity.TemplatesEntity; import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.api.request.DoubleParameter; import org.apache.nifi.web.api.request.LongParameter; +import org.apache.nifi.web.util.Availability; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; -import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; import javax.ws.rs.POST; @@ -83,13 +87,21 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; +import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -97,9 +109,15 @@ import java.util.UUID; /** * RESTful endpoint for managing a Group. */ -@Path("process-groups") +@Path("/process-groups") +@Api( + value = "/process-groups", + description = "Endpoint for managing a Process Group." +) public class ProcessGroupResource extends ApplicationResource { + private static final Logger logger = LoggerFactory.getLogger(ProcessGroupResource.class); + private static final String VERBOSE = "false"; private static final String RECURSIVE = "false"; @@ -117,6 +135,8 @@ public class ProcessGroupResource extends ApplicationResource { private LabelResource labelResource; private RemoteProcessGroupResource remoteProcessGroupResource; private ConnectionResource connectionResource; + private TemplateResource templateResource; + private ControllerServiceResource controllerServiceResource; /** * Populates the remaining fields in the specified process groups. @@ -172,240 +192,21 @@ public class ProcessGroupResource extends ApplicationResource { } /** - * Copies the specified snippet within this ProcessGroup. The snippet instance that is instantiated cannot be referenced at a later time, therefore there is no - * corresponding URI. Instead the request URI is returned. - * - * Alternatively, we could have performed a PUT request. However, PUT requests are supposed to be idempotent and this endpoint is certainly not. - * - * @param httpServletRequest request - * @param groupId The group id - * @param version The revision is used to verify the client is working with the latest version of the flow. - * @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. - * @param snippetId The id of the snippet to copy. - * @param originX The x coordinate of the origin of the bounding box. - * @param originY The y coordinate of the origin of the bounding box. - * @return A flowSnippetEntity. + * Populates the uri for the specified snippet. */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) - @Path("{id}/snippet-instance") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Copies a snippet", - response = FlowSnippetEntity.class, - authorizations = { - @Authorization(value = "ROLE_DFM", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 copySnippet( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The process group id.", - required = true - ) - @PathParam("id") String groupId, - @ApiParam( - value = "The revision is used to verify the client is working with the latest version of the flow.", - required = false - ) - @FormParam(VERSION) LongParameter version, - @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 - ) - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The snippet id.", - required = true - ) - @FormParam("snippetId") String snippetId, - @ApiParam( - value = "The x coordinate of the origin of the bounding box where the new components will be placed.", - required = true - ) - @FormParam("originX") DoubleParameter originX, - @ApiParam( - value = "The y coordinate of the origin of the bounding box where the new components will be placed.", - required = true - ) - @FormParam("originY") DoubleParameter originY) { + private SnippetDTO populateRemainingSnippetContent(SnippetDTO snippet) { + String snippetGroupId = snippet.getParentGroupId(); + FlowSnippetDTO snippetContents = snippet.getContents(); - // ensure the position has been specified - if (originX == null || originY == null) { - throw new IllegalArgumentException("The origin position (x, y) must be specified"); + // populate the snippet href + snippet.setUri(generateResourceUri("process-groups", snippetGroupId, "snippets", snippet.getId())); + + // populate the snippet content uris + if (snippet.getContents() != null) { + populateRemainingSnippetContent(snippetContents); } - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // determine the specified version - Long clientVersion = null; - if (version != null) { - clientVersion = version.getLong(); - } - - // copy the specified snippet - final ConfigurationSnapshot controllerResponse = serviceFacade.copySnippet( - new Revision(clientVersion, clientId.getClientId()), - groupId, snippetId, originX.getDouble(), originY.getDouble()); - - // get the snippet - final FlowSnippetDTO flowSnippet = controllerResponse.getConfiguration(); - - // prune response as necessary - for (ProcessGroupDTO group : flowSnippet.getProcessGroups()) { - if (group.getContents() != null) { - group.setContents(null); - } - } - - // get the updated revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - revision.setVersion(controllerResponse.getVersion()); - - // create the response entity - final FlowSnippetEntity entity = new FlowSnippetEntity(); - entity.setRevision(revision); - entity.setContents(populateRemainingSnippetContent(flowSnippet)); - - // generate the response - return clusterContext(generateCreatedResponse(getAbsolutePath(), entity)).build(); - } - - /** - * Instantiates the specified template within this ProcessGroup. The template instance that is instantiated cannot be referenced at a later time, therefore there is no - * corresponding URI. Instead the request URI is returned. - * - * Alternatively, we could have performed a PUT request. However, PUT requests are supposed to be idempotent and this endpoint is certainly not. - * - * @param httpServletRequest request - * @param groupId The group id - * @param version The revision is used to verify the client is working with the latest version of the flow. - * @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. - * @param templateId The id of the template to instantiate. - * @param originX The x coordinate of the origin of the bounding box. - * @param originY The y coordinate of the origin of the bounding box. - * @return A flowSnippetEntity. - */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) - @Path("{id}/template-instance") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Instantiates a template", - response = FlowSnippetEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 instantiateTemplate( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The process group id.", - required = true - ) - @PathParam("id") String groupId, - @ApiParam( - value = "The revision is used to verify the client is working with the latest version of the flow.", - required = false - ) - @FormParam(VERSION) LongParameter version, - @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 - ) - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The id of the template", - required = false - ) - @FormParam("templateId") String templateId, - @ApiParam( - value = "The x coordinate of the origin of the bounding box where the new components will be placed.", - required = true - ) - @FormParam("originX") DoubleParameter originX, - @ApiParam( - value = "The y coordinate of the origin of the bounding box where the new components will be placed.", - required = true - ) - @FormParam("originY") DoubleParameter originY) { - - // ensure the position has been specified - if (originX == null || originY == null) { - throw new IllegalArgumentException("The origin position (x, y) must be specified"); - } - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // determine the specified version - Long clientVersion = null; - if (version != null) { - clientVersion = version.getLong(); - } - - // create the template and generate the json - final ConfigurationSnapshot response = serviceFacade.createTemplateInstance( - new Revision(clientVersion, clientId.getClientId()), groupId, originX.getDouble(), originY.getDouble(), templateId); - final FlowSnippetDTO flowSnippet = response.getConfiguration(); - - // prune response as necessary - for (ProcessGroupDTO group : flowSnippet.getProcessGroups()) { - if (group.getContents() != null) { - group.setContents(null); - } - } - - // get the updated revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - revision.setVersion(response.getVersion()); - - // create the response entity - final FlowSnippetEntity entity = new FlowSnippetEntity(); - entity.setRevision(revision); - entity.setContents(populateRemainingSnippetContent(flowSnippet)); - - // generate the response - return clusterContext(generateCreatedResponse(getAbsolutePath(), entity)).build(); + return snippet; } /** @@ -521,7 +322,7 @@ public class ProcessGroupResource 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 updateProcessGroupReference( + public Response updateProcessGroup( @Context HttpServletRequest httpServletRequest, @ApiParam( value = "The process group id.", @@ -665,195 +466,6 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the status report for 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. - * @param recursive Optional recursive flag that defaults to false. If set to true, all descendant groups and the status of their content will be included. - * @param groupId The group id - * @return A processGroupStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_NIFI')") - @ApiOperation( - value = "Gets the status for a process group", - notes = "The status for a process group includes status for all descendent components. When invoked on the root group with " - + "recursive set to true, it will return the current status of every component in the flow.", - response = ProcessGroupStatusEntity.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 = "NiFi", type = "ROLE_NIFI") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 getProcessGroupStatus( - @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, - @ApiParam( - value = "Whether all descendant groups and the status of their content will be included. Optional, defaults to false", - required = false - ) - @QueryParam("recursive") @DefaultValue(RECURSIVE) Boolean recursive, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The process group id.", - required = true - ) - @PathParam("id") String groupId) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final ProcessGroupStatusEntity entity = (ProcessGroupStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getProcessGroupStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the status - final ProcessGroupStatusDTO statusReport = serviceFacade.getProcessGroupStatus(groupId); - - // prune the response as necessary - if (!recursive) { - pruneChildGroups(statusReport.getAggregateSnapshot()); - if (statusReport.getNodeSnapshots() != null) { - for (final NodeProcessGroupStatusSnapshotDTO nodeSnapshot : statusReport.getNodeSnapshots()) { - pruneChildGroups(nodeSnapshot.getStatusSnapshot()); - } - } - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ProcessGroupStatusEntity entity = new ProcessGroupStatusEntity(); - entity.setRevision(revision); - entity.setProcessGroupStatus(statusReport); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - private void pruneChildGroups(final ProcessGroupStatusSnapshotDTO snapshot) { - for (final ProcessGroupStatusSnapshotDTO childProcessGroupStatus : snapshot.getProcessGroupStatusSnapshots()) { - childProcessGroupStatus.setConnectionStatusSnapshots(null); - childProcessGroupStatus.setProcessGroupStatusSnapshots(null); - childProcessGroupStatus.setInputPortStatusSnapshots(null); - childProcessGroupStatus.setOutputPortStatusSnapshots(null); - childProcessGroupStatus.setProcessorStatusSnapshots(null); - childProcessGroupStatus.setRemoteProcessGroupStatusSnapshots(null); - } - } - - /** - * Retrieves the specified remote process groups status history. - * - * @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. - * @param groupId The group id - * @return A processorEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("{id}/status/history") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status history for a remote process group", - response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), - @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 getProcessGroupStatusHistory( - @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The process group id.", - required = true - ) - @PathParam("id") String groupId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the specified processor status history - final StatusHistoryDTO processGroupStatusHistory = serviceFacade.getProcessGroupStatusHistory(groupId); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final StatusHistoryEntity entity = new StatusHistoryEntity(); - entity.setRevision(revision); - entity.setStatusHistory(processGroupStatusHistory); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Adds the specified process group. * @@ -1005,6 +617,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ---------- + // processors + // ---------- + /** * Creates a new processor. * @@ -1161,6 +777,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ----------- + // input ports + // ----------- + /** * Creates a new input port. * @@ -1315,6 +935,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ------------ + // output ports + // ------------ + /** * Creates a new output port. * @@ -1469,6 +1093,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ------- + // funnels + // ------- + /** * Creates a new Funnel. * @@ -1630,6 +1258,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ------ + // labels + // ------ + /** * Creates a new Label. * @@ -1784,6 +1416,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // --------------------- + // remote process groups + // --------------------- + /** * Creates a new remote process group. * @@ -1990,6 +1626,10 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // ----------- + // connections + // ----------- + /** * Creates a new connection. * @@ -2157,6 +1797,1039 @@ public class ProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } + // -------- + // snippets + // -------- + + /** + * Creates a snippet based off the specified configuration. + * + * @param httpServletRequest request + * @param snippetEntity A snippetEntity + * @return A snippetEntity + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/snippets") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Creates a snippet", + response = SnippetEntity.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 = 404, message = "The specified resource could not be found."), + @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 createSnippet( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The snippet configuration details.", + required = true + ) + final SnippetEntity snippetEntity) { + + if (snippetEntity == null || snippetEntity.getSnippet() == null) { + throw new IllegalArgumentException("Snippet details must be specified."); + } + + if (snippetEntity.getRevision() == null) { + throw new IllegalArgumentException("Revision must be specified."); + } + + if (snippetEntity.getSnippet().getId() != null) { + throw new IllegalArgumentException("Snippet ID cannot be specified."); + } + + // ensure the group id has been specified + if (snippetEntity.getSnippet().getParentGroupId() == null) { + throw new IllegalArgumentException("The group id must be specified when creating a snippet."); + } + + if (properties.isClusterManager()) { + return (Response) clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(snippetEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // set the processor id as appropriate + final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); + if (clusterContext != null) { + snippetEntity.getSnippet().setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString()); + } else { + snippetEntity.getSnippet().setId(UUID.randomUUID().toString()); + } + + // create the snippet + final RevisionDTO revision = snippetEntity.getRevision(); + final ConfigurationSnapshot response = serviceFacade.createSnippet(new Revision(revision.getVersion(), revision.getClientId()), snippetEntity.getSnippet()); + + // get the snippet + final SnippetDTO snippet = response.getConfiguration(); + + // always prune the response when creating + snippet.setContents(null); + + // get the updated revision + final RevisionDTO updatedRevision = new RevisionDTO(); + updatedRevision.setClientId(revision.getClientId()); + updatedRevision.setVersion(response.getVersion()); + + // build the response entity + SnippetEntity entity = new SnippetEntity(); + entity.setRevision(updatedRevision); + entity.setSnippet(populateRemainingSnippetContent(snippet)); + + // build the response + return clusterContext(generateCreatedResponse(URI.create(snippet.getUri()), entity)).build(); + } + + /** + * Retrieves the specified snippet. + * + * @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. + * @param verbose Whether or not to include the contents of the snippet in + * the response. + * @param id The id of the snippet to retrieve. + * @return A snippetEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/snippets/{snippet-id}") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets a snippet", + response = SnippetEntity.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 = 404, message = "The specified resource could not be found."), + @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 getSnippet( + @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, + @ApiParam( + value = "Whether to include configuration details for the components specified in the snippet.", + required = false + ) + @QueryParam("verbose") @DefaultValue(VERBOSE) Boolean verbose, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The snippet id.", + required = true + ) + @PathParam("snippet-id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the snippet + final SnippetDTO snippet = serviceFacade.getSnippet(id); + + // prune the response if necessary + if (!verbose) { + snippet.setContents(null); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final SnippetEntity entity = new SnippetEntity(); + entity.setRevision(revision); + entity.setSnippet(populateRemainingSnippetContent(snippet)); + + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Updates the specified snippet. The contents of the snippet (component + * ids) cannot be updated once the snippet is created. + * + * @param httpServletRequest request + * @param id The id of the snippet. + * @param snippetEntity A snippetEntity + * @return A snippetEntity + */ + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/snippets/{snippet-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Updates a snippet", + response = SnippetEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 updateSnippet( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The snippet id.", + required = true + ) + @PathParam("snippet-id") String id, + @ApiParam( + value = "The snippet configuration details.", + required = true + ) final SnippetEntity snippetEntity) { + + if (snippetEntity == null || snippetEntity.getSnippet() == null) { + throw new IllegalArgumentException("Snippet details must be specified."); + } + + if (snippetEntity.getRevision() == null) { + throw new IllegalArgumentException("Revision must be specified."); + } + + // ensure the ids are the same + final SnippetDTO requestSnippetDTO = snippetEntity.getSnippet(); + if (!id.equals(requestSnippetDTO.getId())) { + throw new IllegalArgumentException(String.format("The snippet id (%s) in the request body does not equal the " + + "snippet id of the requested resource (%s).", requestSnippetDTO.getId(), id)); + } + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(snippetEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + serviceFacade.verifyUpdateSnippet(requestSnippetDTO); + return generateContinueResponse().build(); + } + + // update the snippet + final RevisionDTO revision = snippetEntity.getRevision(); + final ConfigurationSnapshot controllerResponse = serviceFacade.updateSnippet( + new Revision(revision.getVersion(), revision.getClientId()), snippetEntity.getSnippet()); + + // get the results + final SnippetDTO snippet = controllerResponse.getConfiguration(); + + // always prune update responses + snippet.setContents(null); + + // get the updated revision + final RevisionDTO updatedRevision = new RevisionDTO(); + updatedRevision.setClientId(revision.getClientId()); + updatedRevision.setVersion(controllerResponse.getVersion()); + + // build the response entity + SnippetEntity entity = new SnippetEntity(); + entity.setRevision(updatedRevision); + entity.setSnippet(populateRemainingSnippetContent(snippet)); + + if (controllerResponse.isNew()) { + return clusterContext(generateCreatedResponse(URI.create(snippet.getUri()), entity)).build(); + } else { + return clusterContext(generateOkResponse(entity)).build(); + } + } + + /** + * Removes the specified snippet. + * + * @param httpServletRequest request + * @param version The revision is used to verify the client is working with + * the latest version of the flow. + * @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. + * @param id The id of the snippet to remove. + * @return A entity containing the client id and an updated revision. + */ + @DELETE + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/snippets/{snippet-id}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Deletes a snippet", + response = SnippetEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 removeSnippet( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The revision is used to verify the client is working with the latest version of the flow.", + required = false + ) + @QueryParam(VERSION) LongParameter version, + @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, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The snippet id.", + required = true + ) + @PathParam("snippet-id") String id) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + serviceFacade.verifyDeleteSnippet(id); + return generateContinueResponse().build(); + } + + // determine the specified version + Long clientVersion = null; + if (version != null) { + clientVersion = version.getLong(); + } + + // delete the specified snippet + final ConfigurationSnapshot controllerResponse = serviceFacade.deleteSnippet(new Revision(clientVersion, clientId.getClientId()), id); + + // get the updated revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + revision.setVersion(controllerResponse.getVersion()); + + // build the response entity + SnippetEntity entity = new SnippetEntity(); + entity.setRevision(revision); + + return clusterContext(generateOkResponse(entity)).build(); + } + + // ---------------- + // snippet instance + // ---------------- + + /** + * Copies the specified snippet within this ProcessGroup. The snippet instance that is instantiated cannot be referenced at a later time, therefore there is no + * corresponding URI. Instead the request URI is returned. + * + * Alternatively, we could have performed a PUT request. However, PUT requests are supposed to be idempotent and this endpoint is certainly not. + * + * @param httpServletRequest request + * @param groupId The group id + * @param copySnippetEntity The copy snippet request + * @return A flowSnippetEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/snippet-instance") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Copies a snippet", + response = FlowSnippetEntity.class, + authorizations = { + @Authorization(value = "ROLE_DFM", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 copySnippet( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The copy snippet request.", + required = true + ) CopySnippetRequestEntity copySnippetEntity) { + + // ensure the position has been specified + if (copySnippetEntity == null || copySnippetEntity.getOriginX() == null || copySnippetEntity.getOriginY() == null) { + throw new IllegalArgumentException("The origin position (x, y) must be specified"); + } + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(copySnippetEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // copy the specified snippet + final RevisionDTO requestRevision = copySnippetEntity.getRevision(); + final ConfigurationSnapshot controllerResponse = serviceFacade.copySnippet( + new Revision(requestRevision.getVersion(), requestRevision.getClientId()), + groupId, copySnippetEntity.getSnippetId(), copySnippetEntity.getOriginX(), copySnippetEntity.getOriginY()); + + // get the snippet + final FlowSnippetDTO flowSnippet = controllerResponse.getConfiguration(); + + // prune response as necessary + for (ProcessGroupDTO group : flowSnippet.getProcessGroups()) { + if (group.getContents() != null) { + group.setContents(null); + } + } + + // get the updated revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(requestRevision.getClientId()); + revision.setVersion(controllerResponse.getVersion()); + + // create the response entity + final FlowSnippetEntity entity = new FlowSnippetEntity(); + entity.setRevision(revision); + entity.setContents(populateRemainingSnippetContent(flowSnippet)); + + // generate the response + return clusterContext(generateCreatedResponse(getAbsolutePath(), entity)).build(); + } + + // ----------------- + // template instance + // ----------------- + + /** + * Instantiates the specified template within this ProcessGroup. The template instance that is instantiated cannot be referenced at a later time, therefore there is no + * corresponding URI. Instead the request URI is returned. + * + * Alternatively, we could have performed a PUT request. However, PUT requests are supposed to be idempotent and this endpoint is certainly not. + * + * @param httpServletRequest request + * @param groupId The group id + * @param instantiateTemplateRequestEntity The instantiate template request + * @return A flowSnippetEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/template-instance") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Instantiates a template", + response = FlowSnippetEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 instantiateTemplate( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The instantiate template request.", + required = true + ) InstantiateTemplateRequestEntity instantiateTemplateRequestEntity) { + + // ensure the position has been specified + if (instantiateTemplateRequestEntity == null || instantiateTemplateRequestEntity.getOriginX() == null || instantiateTemplateRequestEntity.getOriginY() == null) { + throw new IllegalArgumentException("The origin position (x, y) must be specified"); + } + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(instantiateTemplateRequestEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // create the template and generate the json + final RevisionDTO requestRevision = instantiateTemplateRequestEntity.getRevision(); + final ConfigurationSnapshot response = serviceFacade.createTemplateInstance( + new Revision(requestRevision.getVersion(), requestRevision.getClientId()), groupId, instantiateTemplateRequestEntity.getOriginX(), + instantiateTemplateRequestEntity.getOriginY(), instantiateTemplateRequestEntity.getTemplateId()); + + final FlowSnippetDTO flowSnippet = response.getConfiguration(); + + // prune response as necessary + for (ProcessGroupDTO group : flowSnippet.getProcessGroups()) { + if (group.getContents() != null) { + group.setContents(null); + } + } + + // get the updated revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(requestRevision.getClientId()); + revision.setVersion(response.getVersion()); + + // create the response entity + final FlowSnippetEntity entity = new FlowSnippetEntity(); + entity.setRevision(revision); + entity.setContents(populateRemainingSnippetContent(flowSnippet)); + + // generate the response + return clusterContext(generateCreatedResponse(getAbsolutePath(), entity)).build(); + } + + // --------- + // templates + // --------- + + /** + * Retrieves all the of templates 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 templatesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/templates") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets all templates", + response = TemplatesEntity.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 getTemplates( + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @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 templates + final Set templates = templateResource.populateRemainingTemplatesContent(serviceFacade.getTemplates()); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final TemplatesEntity entity = new TemplatesEntity(); + entity.setRevision(revision); + entity.setTemplates(templates); + entity.setGenerated(new Date()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /** + * Creates a new template based off of the specified template. + * + * @param httpServletRequest request + * @param createTemplateRequestEntity request to create the template + * @return A templateEntity + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/templates") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Creates a template", + response = TemplateEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 = 404, message = "The specified resource could not be found."), + @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 createTemplate( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "The create template request.", + required = true + ) CreateTemplateRequestEntity createTemplateRequestEntity) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(createTemplateRequestEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // create the template and generate the json + final RevisionDTO revisionDTO = createTemplateRequestEntity.getRevision(); + final TemplateDTO template = serviceFacade.createTemplate(createTemplateRequestEntity.getName(), createTemplateRequestEntity.getDescription(), createTemplateRequestEntity.getSnippetId()); + templateResource.populateRemainingTemplateContent(template); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(revisionDTO.getClientId()); + + // build the response entity + final TemplateEntity entity = new TemplateEntity(); + entity.setRevision(revision); + entity.setTemplate(template); + + // build the response + return clusterContext(generateCreatedResponse(URI.create(template.getUri()), entity)).build(); + } + + /** + * Imports the specified template. + * + * @param httpServletRequest request + * @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. + * @param in The template stream + * @return A templateEntity or an errorResponse XML snippet. + */ + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_XML) + @Path("{id}/templates/upload") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + public Response uploadTemplate( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @FormDataParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, + @FormDataParam("template") InputStream in) { + + // unmarshal the template + final TemplateDTO template; + try { + JAXBContext context = JAXBContext.newInstance(TemplateDTO.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + JAXBElement templateElement = unmarshaller.unmarshal(new StreamSource(in), TemplateDTO.class); + template = templateElement.getValue(); + } catch (JAXBException jaxbe) { + logger.warn("An error occurred while parsing a template.", jaxbe); + String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode()); + return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); + } catch (IllegalArgumentException iae) { + logger.warn("Unable to import template.", iae); + String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode(), iae.getMessage()); + return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); + } catch (Exception e) { + logger.warn("An error occurred while importing a template.", e); + String responseXml = String.format("", + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage()); + return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); + } + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // build the response entity + TemplateEntity entity = new TemplateEntity(); + entity.setRevision(revision); + entity.setTemplate(template); + + // replicate if cluster manager + if (properties.isClusterManager()) { + // convert request accordingly + URI importUri = null; + try { + importUri = new URI(generateResourceUri("process-groups", groupId, "templates", "import")); + } catch (final URISyntaxException e) { + throw new WebApplicationException(e); + } + + // change content type to JSON for serializing entity + final Map headersToOverride = new HashMap<>(); + headersToOverride.put("content-type", MediaType.APPLICATION_XML); + + // replicate the request + return clusterManager.applyRequest(HttpMethod.POST, importUri, updateClientId(entity), getHeaders(headersToOverride)).getResponse(); + } + + // otherwise import the template locally + return importTemplate(httpServletRequest, groupId, entity); + } + + /** + * Imports the specified template. + * + * @param httpServletRequest request + * @param templateEntity A templateEntity. + * @return A templateEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.APPLICATION_XML) + @Path("{id}/templates/import") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + public Response importTemplate( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + TemplateEntity templateEntity) { + + // replicate if cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(templateEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + try { + // verify the template was specified + if (templateEntity == null || templateEntity.getTemplate() == null) { + throw new IllegalArgumentException("Template details must be specified."); + } + + // import the template + final TemplateDTO template = serviceFacade.importTemplate(templateEntity.getTemplate()); + templateResource.populateRemainingTemplateContent(template); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + if (templateEntity.getRevision() == null) { + revision.setClientId(new ClientIdParameter().getClientId()); + } else { + revision.setClientId(templateEntity.getRevision().getClientId()); + } + + // build the response entity + TemplateEntity entity = new TemplateEntity(); + entity.setRevision(revision); + entity.setTemplate(template); + + // build the response + return clusterContext(generateCreatedResponse(URI.create(template.getUri()), entity)).build(); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.info("Unable to import template: " + e); + String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode(), e.getMessage()); + return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); + } catch (Exception e) { + logger.warn("An error occurred while importing a template.", e); + String responseXml + = String.format("", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage()); + return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); + } + } + + // ------------------- + // controller services + // ------------------- + + /** + * Creates a new Controller Service. + * + * @param httpServletRequest request + * @param availability Whether the controller service is available on the + * NCM only (ncm) or on the nodes only (node). If this instance is not + * clustered all services should use the node availability. + * @param controllerServiceEntity A controllerServiceEntity. + * @return A controllerServiceEntity. + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/controller-services/{availability}") + // TODO - @PreAuthorize("hasRole('ROLE_DFM')") + @ApiOperation( + value = "Creates a new controller service", + response = ControllerServiceEntity.class, + authorizations = { + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") + } + ) + @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 createControllerService( + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "Whether the controller service is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", + allowableValues = "NCM, NODE", + required = true + ) + @PathParam("availability") String availability, + @ApiParam( + value = "The controller service configuration details.", + required = true + ) ControllerServiceEntity controllerServiceEntity) { + + final Availability avail = controllerServiceResource.parseAvailability(availability); + + if (controllerServiceEntity == null || controllerServiceEntity.getControllerService() == null) { + throw new IllegalArgumentException("Controller service details must be specified."); + } + + if (controllerServiceEntity.getRevision() == null) { + throw new IllegalArgumentException("Revision must be specified."); + } + + if (controllerServiceEntity.getControllerService().getId() != null) { + throw new IllegalArgumentException("Controller service ID cannot be specified."); + } + + if (StringUtils.isBlank(controllerServiceEntity.getControllerService().getType())) { + throw new IllegalArgumentException("The type of controller service to create must be specified."); + } + + // get the revision + final RevisionDTO revision = controllerServiceEntity.getRevision(); + + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(controllerServiceEntity), getHeaders()).getResponse(); + } + + // handle expects request (usually from the cluster manager) + final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); + if (expects != null) { + return generateContinueResponse().build(); + } + + // set the processor id as appropriate + final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); + if (clusterContext != null) { + controllerServiceEntity.getControllerService().setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString()); + } else { + controllerServiceEntity.getControllerService().setId(UUID.randomUUID().toString()); + } + + // create the controller service and generate the json + final ConfigurationSnapshot controllerResponse = serviceFacade.createControllerService( + new Revision(revision.getVersion(), revision.getClientId()), controllerServiceEntity.getControllerService()); + final ControllerServiceDTO controllerService = controllerResponse.getConfiguration(); + + // get the updated revision + final RevisionDTO updatedRevision = new RevisionDTO(); + updatedRevision.setClientId(revision.getClientId()); + updatedRevision.setVersion(controllerResponse.getVersion()); + + // build the response entity + final ControllerServiceEntity entity = new ControllerServiceEntity(); + entity.setRevision(updatedRevision); + entity.setControllerService(controllerServiceResource.populateRemainingControllerServiceContent(availability, controllerService)); + + // build the response + return clusterContext(generateCreatedResponse(URI.create(controllerService.getUri()), entity)).build(); + } + + /** + * Retrieves all the of controller services 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. + * @param availability Whether the controller service is available on the + * NCM only (ncm) or on the nodes only (node). If this instance is not + * clustered all services should use the node availability. + * @return A controllerServicesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}/controller-services/{availability}") + // 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 getControllerServices( + @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, + @ApiParam( + value = "The process group id.", + required = true + ) + @PathParam("id") String groupId, + @ApiParam( + value = "Whether the controller service is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", + allowableValues = "NCM, NODE", + required = true + ) + @PathParam("availability") String availability) { + + final Availability avail = controllerServiceResource.parseAvailability(availability); + + // replicate if cluster manager + if (properties.isClusterManager() && Availability.NODE.equals(avail)) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get all the controller services + final Set controllerServices = controllerServiceResource.populateRemainingControllerServicesContent(availability, serviceFacade.getControllerServices()); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response entity + final ControllerServicesEntity entity = new ControllerServicesEntity(); + entity.setRevision(revision); + entity.setControllerServices(controllerServices); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + // setters public void setServiceFacade(NiFiServiceFacade serviceFacade) { this.serviceFacade = serviceFacade; @@ -2194,6 +2867,14 @@ public class ProcessGroupResource extends ApplicationResource { this.connectionResource = connectionResource; } + public void setTemplateResource(TemplateResource templateResource) { + this.templateResource = templateResource; + } + + public void setControllerServiceResource(ControllerServiceResource controllerServiceResource) { + this.controllerServiceResource = controllerServiceResource; + } + public void setProperties(NiFiProperties properties) { this.properties = properties; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java index 076b3c24a5..620256143e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java @@ -16,18 +16,16 @@ */ package org.apache.nifi.web.api; +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.cluster.manager.NodeResponse; import org.apache.nifi.cluster.manager.exception.IllegalClusterStateException; -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.scheduling.SchedulingStrategy; import org.apache.nifi.ui.extension.UiExtension; import org.apache.nifi.ui.extension.UiExtensionMapping; @@ -41,14 +39,10 @@ import org.apache.nifi.web.api.dto.ProcessorConfigDTO; import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO; -import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.entity.ComponentStateEntity; import org.apache.nifi.web.api.entity.Entity; import org.apache.nifi.web.api.entity.ProcessorEntity; -import org.apache.nifi.web.api.entity.ProcessorStatusEntity; import org.apache.nifi.web.api.entity.PropertyDescriptorEntity; -import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.LongParameter; @@ -70,14 +64,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; /** * RESTful endpoint for managing a Processor. */ -@Path("processors") +@Path("/processors") +@Api( + value = "/processors", + description = "Endpoint for managing a Processor." +) public class ProcessorResource extends ApplicationResource { private static final List POSSIBLE_RUN_DURATIONS = Arrays.asList(0L, 25L, 50L, 100L, 250L, 500L, 1000L, 2000L); @@ -196,169 +193,6 @@ public class ProcessorResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the specified processor status. - * - * @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. - * @param id The id of the processor history to retrieve. - * @return A processorStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status for a processor", - response = ProcessorStatusEntity.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 = 404, message = "The specified resource could not be found."), - @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 getProcessorStatus( - @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, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The processor id.", - required = true - ) - @PathParam("id") String id) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final ProcessorStatusEntity entity = (ProcessorStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getProcessorStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the specified processor status - final ProcessorStatusDTO processorStatus = serviceFacade.getProcessorStatus(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final ProcessorStatusEntity entity = new ProcessorStatusEntity(); - entity.setRevision(revision); - entity.setProcessorStatus(processorStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the specified processor status history. - * - * @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. - * @param id The id of the processor history to retrieve. - * @return A statusHistoryEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status/history") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status history for a processor", - response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), - @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 getProcessorStatusHistory( - @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, - @ApiParam( - value = "The processor id.", - required = true - ) - @PathParam("id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the specified processor status history - final StatusHistoryDTO processorStatusHistory = serviceFacade.getProcessorStatusHistory(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final StatusHistoryEntity entity = new StatusHistoryEntity(); - entity.setRevision(revision); - entity.setStatusHistory(processorStatusHistory); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Returns the descriptor for the specified property. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java index 670b16ab3e..bc9bcc8d7d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProvenanceResource.java @@ -38,17 +38,14 @@ import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceEventDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceOptionsDTO; -import org.apache.nifi.web.api.dto.provenance.ProvenanceRequestDTO; import org.apache.nifi.web.api.dto.provenance.lineage.LineageDTO; import org.apache.nifi.web.api.dto.provenance.lineage.LineageRequestDTO; -import org.apache.nifi.web.api.dto.provenance.lineage.LineageRequestDTO.LineageRequestType; import org.apache.nifi.web.api.entity.LineageEntity; import org.apache.nifi.web.api.entity.ProvenanceEntity; import org.apache.nifi.web.api.entity.ProvenanceEventEntity; import org.apache.nifi.web.api.entity.ProvenanceOptionsEntity; +import org.apache.nifi.web.api.entity.SubmitReplayRequestEntity; import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.api.request.DateTimeParameter; -import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.LongParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +54,6 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; -import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; import javax.ws.rs.POST; @@ -68,7 +64,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.IOException; @@ -78,7 +73,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -86,7 +80,11 @@ import java.util.UUID; /** * RESTful endpoint for querying data provenance. */ -@Api(hidden = true) +@Path("/provenance") +@Api( + value = "/provenance", + description = "Endpoint for accessing data flow provenance." +) public class ProvenanceResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(ProvenanceResource.class); @@ -100,7 +98,7 @@ public class ProvenanceResource extends ApplicationResource { * Populates the uri for the specified provenance. */ private ProvenanceDTO populateRemainingProvenanceContent(ProvenanceDTO provenance) { - provenance.setUri(generateResourceUri("controller", "provenance", provenance.getId())); + provenance.setUri(generateResourceUri("provenance", provenance.getId())); return provenance; } @@ -108,7 +106,7 @@ public class ProvenanceResource extends ApplicationResource { * Populates the uri for the specified lineage. */ private LineageDTO populateRemainingLineageContent(LineageDTO lineage) { - lineage.setUri(generateResourceUri("controller", "provenance", "lineage", lineage.getId())); + lineage.setUri(generateResourceUri("provenance", "lineage", lineage.getId())); return lineage; } @@ -120,8 +118,8 @@ public class ProvenanceResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/search-options") + @Produces(MediaType.APPLICATION_JSON) + @Path("search-options") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets the searchable attributes for provenance events", @@ -170,15 +168,13 @@ public class ProvenanceResource extends ApplicationResource { * Creates a new replay request for the content associated with the specified provenance event id. * * @param httpServletRequest request - * @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. - * @param clusterNodeId The id of the node in the cluster that has the specified event. Required if clustered. - * @param eventId The provenance event id. + * @param replayRequestEntity The replay request * @return A provenanceEventEntity */ @POST - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/replays") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("replays") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE') and hasRole('ROLE_DFM')") @ApiOperation( value = "Replays content from a provenance event", @@ -197,36 +193,25 @@ public class ProvenanceResource extends ApplicationResource { } ) public Response submitReplay( - @Context 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 - ) - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The id of the node where the content exists if clustered.", - required = false - ) - @FormParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The provenance event id.", - required = true - ) - @FormParam("eventId") LongParameter eventId) { + @Context HttpServletRequest httpServletRequest, + @ApiParam( + value = "The replay request.", + required = true + ) SubmitReplayRequestEntity replayRequestEntity) { // ensure the event id is specified - if (eventId == null) { + if (replayRequestEntity == null || replayRequestEntity.getEventId() == null) { throw new IllegalArgumentException("The id of the event must be specified."); } // replicate if cluster manager if (properties.isClusterManager()) { // determine where this request should be sent - if (clusterNodeId == null) { + if (replayRequestEntity.getClusterNodeId() == null) { throw new IllegalArgumentException("The id of the node in the cluster is required."); } else { // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); + final Node targetNode = clusterManager.getNode(replayRequestEntity.getClusterNodeId()); if (targetNode == null) { throw new UnknownNodeException("The specified cluster node does not exist."); } @@ -235,7 +220,7 @@ public class ProvenanceResource extends ApplicationResource { targetNodes.add(targetNode.getNodeId()); // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); + return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), replayRequestEntity, getHeaders(), targetNodes).getResponse(); } } @@ -246,11 +231,12 @@ public class ProvenanceResource extends ApplicationResource { } // submit the provenance replay request - final ProvenanceEventDTO event = serviceFacade.submitReplay(eventId.getLong()); + final RevisionDTO requestRevision = replayRequestEntity.getRevision(); + final ProvenanceEventDTO event = serviceFacade.submitReplay(replayRequestEntity.getEventId()); // create a revision to return final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); + revision.setClientId(requestRevision.getClientId()); // create a response entity final ProvenanceEventEntity entity = new ProvenanceEventEntity(); @@ -258,7 +244,7 @@ public class ProvenanceResource extends ApplicationResource { entity.setRevision(revision); // generate the response - URI uri = URI.create(generateResourceUri("controller", "provenance", "events", event.getId())); + URI uri = URI.create(generateResourceUri("provenance", "events", event.getId())); return clusterContext(generateCreatedResponse(uri, entity)).build(); } @@ -273,7 +259,7 @@ public class ProvenanceResource extends ApplicationResource { @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) - @Path("/events/{id}/content/input") + @Path("events/{id}/content/input") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets the input content for a provenance event", @@ -333,7 +319,7 @@ public class ProvenanceResource extends ApplicationResource { } // get the uri of the request - final String uri = generateResourceUri("controller", "provenance", "events", String.valueOf(id.getLong()), "content", "input"); + final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "input"); // get an input stream to the content final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.INPUT); @@ -372,7 +358,7 @@ public class ProvenanceResource extends ApplicationResource { @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) - @Path("/events/{id}/content/output") + @Path("events/{id}/content/output") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets the output content for a provenance event", @@ -432,7 +418,7 @@ public class ProvenanceResource extends ApplicationResource { } // get the uri of the request - final String uri = generateResourceUri("controller", "provenance", "events", String.valueOf(id.getLong()), "content", "output"); + final String uri = generateResourceUri("provenance", "events", String.valueOf(id.getLong()), "content", "output"); // get an input stream to the content final DownloadableContent content = serviceFacade.getContent(id.getLong(), uri, ContentDirection.OUTPUT); @@ -460,100 +446,6 @@ public class ProvenanceResource extends ApplicationResource { return generateOkResponse(response).type(contentType).header("Content-Disposition", String.format("attachment; filename=\"%s\"", content.getFilename())).build(); } - /** - * Creates provenance using the specified query criteria. - * - * @param httpServletRequest request - * @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. - * @param startDate The start date. - * @param endDate The end date. - * @param minimumFileSize The minimum size of the content after the event. - * @param maximumFileSize The maximum size of the content after the event. - * @param maxResults The maximum number of results to return. - * @param clusterNodeId The id of node in the cluster to search. This is optional and only relevant when clustered. If clustered and it is not specified the entire cluster is searched. - * @param formParams Additionally, the search parameters are specified in the form parameters. Because the search parameters differ based on configuration they are specified in a map-like fashion: - *
- *
    - *
  • search[filename]=myFile.txt
  • - *
  • search[eventType]=RECEIVED
  • - *
- * - * @return A provenanceEntity - */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") - public Response submitProvenanceRequest( - @Context HttpServletRequest httpServletRequest, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormParam("startDate") DateTimeParameter startDate, - @FormParam("endDate") DateTimeParameter endDate, - @FormParam("minimumFileSize") String minimumFileSize, - @FormParam("maximumFileSize") String maximumFileSize, - @FormParam("maxResults") IntegerParameter maxResults, - @FormParam("clusterNodeId") String clusterNodeId, - MultivaluedMap formParams) { - - // ensure the max results has been specified - if (maxResults == null) { - throw new IllegalArgumentException("Max results must be specified."); - } else if (maxResults.getInteger() > MAX_MAX_RESULTS) { - throw new IllegalArgumentException("The maximum number of results cannot be greater than " + MAX_MAX_RESULTS); - } - - // create collections for holding the search terms - final Map searchTerms = new LinkedHashMap<>(); - - // go through each parameter and look for processor properties - for (String parameterName : formParams.keySet()) { - if (StringUtils.isNotBlank(parameterName)) { - // see if the parameter name starts with an expected parameter type... - // if so, store the parameter name and value in the corresponding collection - if (parameterName.startsWith("search[")) { - final int startIndex = StringUtils.indexOf(parameterName, "["); - final int endIndex = StringUtils.lastIndexOf(parameterName, "]"); - if (startIndex != -1 && endIndex != -1) { - final String searchTerm = StringUtils.substring(parameterName, startIndex + 1, endIndex); - searchTerms.put(searchTerm, formParams.getFirst(parameterName)); - } - } - } - } - - // Build request object from all params - final ProvenanceRequestDTO request = new ProvenanceRequestDTO(); - request.setSearchTerms(searchTerms); - request.setMinimumFileSize(minimumFileSize); - request.setMaximumFileSize(maximumFileSize); - request.setMaxResults(maxResults.getInteger()); - - // add date range - if (startDate != null) { - request.setStartDate(startDate.getDateTime()); - } - if (endDate != null) { - request.setEndDate(endDate.getDateTime()); - } - - // build the provenance object - final ProvenanceDTO provenanceDto = new ProvenanceDTO(); - provenanceDto.setClusterNodeId(clusterNodeId); - provenanceDto.setRequest(request); - - // create a revision to return - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the request entity - final ProvenanceEntity entity = new ProvenanceEntity(); - entity.setRevision(revision); - entity.setProvenance(provenanceDto); - - return submitProvenanceRequest(httpServletRequest, entity); - } - /** * Creates provenance using the specified query criteria. * @@ -562,8 +454,8 @@ public class ProvenanceResource extends ApplicationResource { * @return A provenanceEntity */ @POST - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) @Path("") // necessary due to bug in swagger // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( @@ -679,8 +571,8 @@ public class ProvenanceResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets a provenance query", @@ -765,8 +657,8 @@ public class ProvenanceResource extends ApplicationResource { */ @DELETE @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("{id}") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Deletes a provenance query", @@ -854,8 +746,8 @@ public class ProvenanceResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/events/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("events/{id}") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets a provenance event", @@ -934,72 +826,6 @@ public class ProvenanceResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Submits a lineage request based on an event or a flowfile uuid. - * - * When querying for the lineage of an event you must specify the eventId and the eventDirection. The eventDirection must be 'parents' or 'children' and specifies whether we are going up or down - * the flowfile ancestry. The uuid cannot be specified in these cases. - * - * When querying for the lineage of a flowfile you must specify the uuid. The eventId and eventDirection cannot be specified in this case. - * - * @param httpServletRequest request - * @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. - * @param eventId The id of an event to get the lineage for. Must also specify the eventDirection and not the uuid. - * @param lineageRequest Either 'PARENTS', 'CHILDREN', or 'FLOWFILE'. PARENTS will return the lineage for the flowfiles that are parents of the specified event. CHILDREN will return the lineage of - * for the flowfiles that are children of the specified event. FLOWFILE will return the lineage for the specified flowfile. - * @param uuid The uuid of the flowfile to get the lineage for. Must not specify the eventId or eventDirection. - * @param clusterNodeId The id of node in the cluster that the event/flowfile originated from. This is only required when clustered. - * @return A lineageEntity - */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/lineage") - // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") - public Response submitLineageRequest( - @Context HttpServletRequest httpServletRequest, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormParam("lineageRequestType") String lineageRequest, - @FormParam("eventId") LongParameter eventId, - @FormParam("uuid") String uuid, - @FormParam("clusterNodeId") String clusterNodeId) { - - // create the lineage request - final LineageRequestDTO request = new LineageRequestDTO(); - - // ensure the lineage request type is specified - try { - final LineageRequestType direction = LineageRequestType.valueOf(lineageRequest); - request.setLineageRequestType(direction); - } catch (final IllegalArgumentException iae) { - throw new IllegalArgumentException(String.format("The event direction must be one of %s", StringUtils.join(LineageRequestType.values()))); - } - - // set the uuid (may be null if based on event) - request.setUuid(uuid); - - // set the event id (may be null is based on flowfile) - if (eventId != null) { - request.setEventId(eventId.getLong()); - } - - // create the lineage - final LineageDTO lineage = new LineageDTO(); - lineage.setClusterNodeId(clusterNodeId); - lineage.setRequest(request); - - // create a revision to return - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create a response entity - final LineageEntity entity = new LineageEntity(); - entity.setLineage(lineage); - entity.setRevision(revision); - - return submitLineageRequest(httpServletRequest, entity); - } - /** * Submits a lineage request based on an event or a flowfile uuid. * @@ -1013,9 +839,9 @@ public class ProvenanceResource extends ApplicationResource { * @return A lineageEntity */ @POST - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/lineage") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("lineage") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Submits a lineage query", @@ -1136,8 +962,8 @@ public class ProvenanceResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/lineage/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("lineage/{id}") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Gets a lineage query", @@ -1220,8 +1046,8 @@ public class ProvenanceResource extends ApplicationResource { */ @DELETE @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/lineage/{id}") + @Produces(MediaType.APPLICATION_JSON) + @Path("lineage/{id}") // TODO - @PreAuthorize("hasRole('ROLE_PROVENANCE')") @ApiOperation( value = "Deletes a lineage query", diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java index 868e647626..d1999d9b5b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java @@ -16,17 +16,14 @@ */ package org.apache.nifi.web.api; +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.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; @@ -34,22 +31,15 @@ import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO; import org.apache.nifi.web.api.dto.RevisionDTO; -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.ProcessorStatusEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupEntity; import org.apache.nifi.web.api.entity.RemoteProcessGroupPortEntity; -import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusEntity; -import org.apache.nifi.web.api.entity.StatusHistoryEntity; import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.LongParameter; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; -import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; import javax.ws.rs.PUT; @@ -62,14 +52,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; /** * RESTful endpoint for managing a Remote group. */ -@Path("remote-process-groups") +@Path("/remote-process-groups") +@Api( + value = "/remote-process-groups", + description = "Endpoint for managing a Remote Process Group." +) public class RemoteProcessGroupResource extends ApplicationResource { private static final String VERBOSE_DEFAULT_VALUE = "false"; @@ -177,169 +170,6 @@ public class RemoteProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the specified remote process group status. - * - * @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. - * @param id The id of the processor history to retrieve. - * @return A remoteProcessGroupStatusEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets status for a remote process group", - response = ProcessorStatusEntity.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 = 404, message = "The specified resource could not be found."), - @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 getRemoteProcessGroupStatus( - @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, - @ApiParam( - value = "Whether or not to include the breakdown per node. Optional, defaults to false", - required = false - ) - @QueryParam("nodewise") @DefaultValue(NODEWISE) Boolean nodewise, - @ApiParam( - value = "The id of the node where to get the status.", - required = false - ) - @QueryParam("clusterNodeId") String clusterNodeId, - @ApiParam( - value = "The remote process group id.", - required = true - ) - @PathParam("id") String id) { - - // ensure a valid request - if (Boolean.TRUE.equals(nodewise) && clusterNodeId != null) { - throw new IllegalArgumentException("Nodewise requests cannot be directed at a specific node."); - } - - if (properties.isClusterManager()) { - // determine where this request should be sent - if (clusterNodeId == null) { - final NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()); - final RemoteProcessGroupStatusEntity entity = (RemoteProcessGroupStatusEntity) nodeResponse.getUpdatedEntity(); - - // ensure there is an updated entity (result of merging) and prune the response as necessary - if (entity != null && !nodewise) { - entity.getRemoteProcessGroupStatus().setNodeSnapshots(null); - } - - return nodeResponse.getResponse(); - } else { - // get the target node and ensure it exists - final Node targetNode = clusterManager.getNode(clusterNodeId); - if (targetNode == null) { - throw new UnknownNodeException("The specified cluster node does not exist."); - } - - final Set targetNodes = new HashSet<>(); - targetNodes.add(targetNode.getNodeId()); - - // replicate the request to the specific node - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders(), targetNodes).getResponse(); - } - } - - // get the specified remote process group status - final RemoteProcessGroupStatusDTO remoteProcessGroupStatus = serviceFacade.getRemoteProcessGroupStatus(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final RemoteProcessGroupStatusEntity entity = new RemoteProcessGroupStatusEntity(); - entity.setRevision(revision); - entity.setRemoteProcessGroupStatus(remoteProcessGroupStatus); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Retrieves the specified remote process groups status history. - * - * @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. - * @param id The id of the remote process group to retrieve the status fow. - * @return A statusHistoryEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/status/history") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets the status history", - response = StatusHistoryEntity.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 = 404, message = "The specified resource could not be found."), - @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 getRemoteProcessGroupStatusHistory( - @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, - @ApiParam( - value = "The remote process group id.", - required = true - ) - @PathParam("id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the specified processor status history - final StatusHistoryDTO remoteProcessGroupStatusHistory = serviceFacade.getRemoteProcessGroupStatusHistory(id); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // generate the response entity - final StatusHistoryEntity entity = new StatusHistoryEntity(); - entity.setRevision(revision); - entity.setStatusHistory(remoteProcessGroupStatusHistory); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Removes the specified remote process group. * @@ -352,7 +182,7 @@ public class RemoteProcessGroupResource extends ApplicationResource { @DELETE @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}") + @Path("{id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Deletes a remote process group", @@ -421,61 +251,6 @@ public class RemoteProcessGroupResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Updates the specified remote process group input port. - * - * @param httpServletRequest request - * @param version The revision is used to verify the client is working with the latest version of the flow. - * @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. - * @param id The id of the remote process group to update. - * @param portId The id of the input port to update. - * @param isTransmitting Whether or not this port is transmitting. - * @param isCompressed Whether or not this port should compress. - * @param concurrentlySchedulableTaskCount The number of concurrent tasks that should be supported - * - * @return A remoteProcessGroupPortEntity - */ - @PUT - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/input-ports/{port-id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response updateRemoteProcessGroupInputPort( - @Context HttpServletRequest httpServletRequest, - @FormParam(VERSION) LongParameter version, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @PathParam("id") String id, - @PathParam("port-id") String portId, - @FormParam("transmitting") Boolean isTransmitting, - @FormParam("compressed") Boolean isCompressed, - @FormParam("concurrentlySchedulableTaskCount") IntegerParameter concurrentlySchedulableTaskCount) { - - // create the remote group port dto - final RemoteProcessGroupPortDTO remotePort = new RemoteProcessGroupPortDTO(); - remotePort.setId(portId); - remotePort.setUseCompression(isCompressed); - remotePort.setTransmitting(isTransmitting); - - if (concurrentlySchedulableTaskCount != null) { - remotePort.setConcurrentlySchedulableTaskCount(concurrentlySchedulableTaskCount.getInteger()); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - if (version != null) { - revision.setVersion(version.getLong()); - } - - // create the remote group port entity - final RemoteProcessGroupPortEntity entity = new RemoteProcessGroupPortEntity(); - entity.setRevision(revision); - entity.setRemoteProcessGroupPort(remotePort); - - return updateRemoteProcessGroupInputPort(httpServletRequest, id, portId, entity); - } - /** * Updates the specified remote process group input port. * @@ -489,7 +264,7 @@ public class RemoteProcessGroupResource extends ApplicationResource { @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/input-ports/{port-id}") + @Path("{id}/input-ports/{port-id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Updates a remote port", @@ -578,7 +353,7 @@ public class RemoteProcessGroupResource extends ApplicationResource { @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}/output-ports/{port-id}") + @Path("{id}/output-ports/{port-id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Updates a remote port", @@ -665,7 +440,7 @@ public class RemoteProcessGroupResource extends ApplicationResource { @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Path("/{id}") + @Path("{id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( value = "Updates a remote process group", diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java index 4a746b3cd1..2a481838ca 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java @@ -16,14 +16,13 @@ */ package org.apache.nifi.web.api; +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.cluster.context.ClusterContext; -import org.apache.nifi.cluster.context.ClusterContextThreadLocal; import org.apache.nifi.cluster.manager.impl.WebClusterManager; import org.apache.nifi.ui.extension.UiExtension; import org.apache.nifi.ui.extension.UiExtensionMapping; @@ -40,7 +39,6 @@ import org.apache.nifi.web.api.entity.ComponentStateEntity; import org.apache.nifi.web.api.entity.Entity; import org.apache.nifi.web.api.entity.PropertyDescriptorEntity; 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 org.apache.nifi.web.api.request.LongParameter; import org.apache.nifi.web.util.Availability; @@ -62,15 +60,17 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; -import java.util.UUID; /** * RESTful endpoint for managing a Reporting Task. */ -@Path("reporting-tasks") +@Path("/reporting-tasks") +@Api( + value = "/reporting-tasks", + description = "Endpoint for managing a Reporting Task." +) public class ReportingTaskResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; @@ -86,7 +86,7 @@ public class ReportingTaskResource extends ApplicationResource { * @param reportingTasks tasks * @return tasks */ - private Set populateRemainingReportingTasksContent(final String availability, final Set reportingTasks) { + public Set populateRemainingReportingTasksContent(final String availability, final Set reportingTasks) { for (ReportingTaskDTO reportingTask : reportingTasks) { populateRemainingReportingTaskContent(availability, reportingTask); } @@ -96,7 +96,7 @@ public class ReportingTaskResource extends ApplicationResource { /** * Populates the uri for the specified reporting task. */ - private ReportingTaskDTO populateRemainingReportingTaskContent(final String availability, final ReportingTaskDTO reportingTask) { + public ReportingTaskDTO populateRemainingReportingTaskContent(final String availability, final ReportingTaskDTO reportingTask) { // populate the reporting task href reportingTask.setUri(generateResourceUri("reporting-tasks", availability, reportingTask.getId())); reportingTask.setAvailability(availability); @@ -119,7 +119,7 @@ public class ReportingTaskResource extends ApplicationResource { * Parses the availability and ensure that the specified availability makes * sense for the given NiFi instance. */ - private Availability parseAvailability(final String availability) { + public Availability parseAvailability(final String availability) { final Availability avail; try { avail = Availability.valueOf(availability.toUpperCase()); @@ -135,176 +135,6 @@ public class ReportingTaskResource extends ApplicationResource { return avail; } - /** - * 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. - * @param availability Whether the reporting task is available on the NCM - * only (ncm) or on the nodes only (node). If this instance is not clustered - * all tasks should use the node availability. - * @return A reportingTasksEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("{availability}") - // 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, - @ApiParam( - value = "Whether the reporting task is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", - allowableValues = "NCM, NODE", - required = true - ) - @PathParam("availability") String availability) { - - final Availability avail = parseAvailability(availability); - - // replicate if cluster manager - if (properties.isClusterManager() && Availability.NODE.equals(avail)) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get all the reporting tasks - final Set reportingTasks = populateRemainingReportingTasksContent(availability, serviceFacade.getReportingTasks()); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final ReportingTasksEntity entity = new ReportingTasksEntity(); - entity.setRevision(revision); - entity.setReportingTasks(reportingTasks); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Creates a new Reporting Task. - * - * @param httpServletRequest request - * @param availability Whether the reporting task is available on the NCM - * only (ncm) or on the nodes only (node). If this instance is not clustered - * all tasks should use the node availability. - * @param reportingTaskEntity A reportingTaskEntity. - * @return A reportingTaskEntity. - */ - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @Path("{availability}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Creates a new reporting task", - response = ReportingTaskEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 createReportingTask( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "Whether the reporting task is available on the NCM or nodes. If the NiFi is standalone the availability should be NODE.", - allowableValues = "NCM, NODE", - required = true - ) - @PathParam("availability") String availability, - @ApiParam( - value = "The reporting task configuration details.", - required = true - ) ReportingTaskEntity reportingTaskEntity) { - - final Availability avail = parseAvailability(availability); - - if (reportingTaskEntity == null || reportingTaskEntity.getReportingTask() == null) { - throw new IllegalArgumentException("Reporting task details must be specified."); - } - - if (reportingTaskEntity.getRevision() == null) { - throw new IllegalArgumentException("Revision must be specified."); - } - - if (reportingTaskEntity.getReportingTask().getId() != null) { - throw new IllegalArgumentException("Reporting task ID cannot be specified."); - } - - if (StringUtils.isBlank(reportingTaskEntity.getReportingTask().getType())) { - throw new IllegalArgumentException("The type of reporting task to create must be specified."); - } - - // get the revision - final RevisionDTO revision = reportingTaskEntity.getRevision(); - - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(reportingTaskEntity), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // set the processor id as appropriate - final ClusterContext clusterContext = ClusterContextThreadLocal.getContext(); - if (clusterContext != null) { - reportingTaskEntity.getReportingTask().setId(UUID.nameUUIDFromBytes(clusterContext.getIdGenerationSeed().getBytes(StandardCharsets.UTF_8)).toString()); - } else { - reportingTaskEntity.getReportingTask().setId(UUID.randomUUID().toString()); - } - - // create the reporting task and generate the json - final ConfigurationSnapshot controllerResponse = serviceFacade.createReportingTask( - new Revision(revision.getVersion(), revision.getClientId()), reportingTaskEntity.getReportingTask()); - final ReportingTaskDTO reportingTask = controllerResponse.getConfiguration(); - - // get the updated revision - final RevisionDTO updatedRevision = new RevisionDTO(); - updatedRevision.setClientId(revision.getClientId()); - updatedRevision.setVersion(controllerResponse.getVersion()); - - // build the response entity - final ReportingTaskEntity entity = new ReportingTaskEntity(); - entity.setRevision(updatedRevision); - entity.setReportingTask(populateRemainingReportingTaskContent(availability, reportingTask)); - - // build the response - return clusterContext(generateCreatedResponse(URI.create(reportingTask.getUri()), entity)).build(); - } - /** * Retrieves the specified reporting task. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java new file mode 100644 index 0000000000..ccdfda7d99 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ResourceResource.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +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.cluster.manager.impl.WebClusterManager; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.ResourceDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.ResourcesEntity; +import org.apache.nifi.web.api.request.ClientIdParameter; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * RESTful endpoint for retrieving system diagnostics. + */ +@Path("/resources") +@Api( + value = "/resources", + description = "Provides the resources in this NiFi that can have access/authorization policies." +) +public class ResourceResource extends ApplicationResource { + + private NiFiServiceFacade serviceFacade; + private WebClusterManager clusterManager; + private NiFiProperties properties; + + /** + * Gets the available resources that support access/authorization policies. + * + * @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 resourcesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Gets the available resources that support access/authorization policies", + response = ResourcesEntity.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 = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."),} + ) + public Response getResources( + @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 the cluster manager + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // TODO - if unsecure, return no resources? + final List resources = serviceFacade.getResources(); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // create the response + final ResourcesEntity entity = new ResourcesEntity(); + entity.setRevision(revision); + entity.setResources(resources); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + // setters + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + + public void setClusterManager(WebClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java new file mode 100644 index 0000000000..055d11c8d7 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SiteToSiteResource.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.web.api; + +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.cluster.manager.impl.WebClusterManager; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.NiFiServiceFacade; +import org.apache.nifi.web.api.dto.ControllerDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.ControllerEntity; +import org.apache.nifi.web.api.request.ClientIdParameter; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * RESTful endpoint for managing a Flow Controller. + */ +@Path("/site-to-site") +@Api( + value = "/site-to-site", + description = "Provide access to site to site with this NiFi" +) +public class SiteToSiteResource extends ApplicationResource { + + private NiFiServiceFacade serviceFacade; + private WebClusterManager clusterManager; + private NiFiProperties properties; + + @Context + private ResourceContext resourceContext; + + /** + * Returns the details of 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 controllerEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + // TODO - @PreAuthorize("hasRole('ROLE_NIFI')") + @ApiOperation( + value = "Returns the details about this NiFi necessary to communicate via site to site", + response = ControllerEntity.class, + authorizations = @Authorization(value = "NiFi", type = "ROLE_NIFI") + ) + @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 getController( + @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) { + + if (properties.isClusterManager()) { + return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); + } + + // get the controller dto + final ControllerDTO controller = serviceFacade.getController(); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(clientId.getClientId()); + + // build the response entity + final ControllerEntity entity = new ControllerEntity(); + entity.setRevision(revision); + entity.setController(controller); + + // generate the response + return clusterContext(noCache(Response.ok(entity))).build(); + } + + // setters + public void setServiceFacade(NiFiServiceFacade serviceFacade) { + this.serviceFacade = serviceFacade; + } + + public void setClusterManager(WebClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java deleted file mode 100644 index 5eddcf7f1c..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.api; - -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.cluster.manager.impl.WebClusterManager; -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.FlowSnippetDTO; -import org.apache.nifi.web.api.dto.RevisionDTO; -import org.apache.nifi.web.api.dto.SnippetDTO; -import org.apache.nifi.web.api.entity.SnippetEntity; -import org.apache.nifi.web.api.request.ClientIdParameter; -import org.apache.nifi.web.api.request.LongParameter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * RESTful endpoint for managing a Snippet. - */ -@Api(hidden = true) -public class SnippetResource extends ApplicationResource { - - private static final Logger logger = LoggerFactory.getLogger(SnippetResource.class); - private static final String LINKED = "false"; - private static final String VERBOSE = "false"; - - @Context - private ResourceContext resourceContext; - - private NiFiServiceFacade serviceFacade; - private WebClusterManager clusterManager; - private NiFiProperties properties; - - private ProcessorResource processorResource; - private InputPortResource inputPortResource; - private OutputPortResource outputPortResource; - private FunnelResource funnelResource; - private LabelResource labelResource; - private RemoteProcessGroupResource remoteProcessGroupResource; - private ConnectionResource connectionResource; - private ProcessGroupResource processGroupResource; - - /** - * Populates the uri for the specified snippet. - */ - private SnippetDTO populateRemainingSnippetContent(SnippetDTO snippet) { - // populate the snippet href - snippet.setUri(generateResourceUri("controller", "snippets", snippet.getId())); - - String snippetGroupId = snippet.getParentGroupId(); - FlowSnippetDTO snippetContents = snippet.getContents(); - - // populate the snippet content uris - if (snippet.getContents() != null) { - processorResource.populateRemainingProcessorsContent(snippetContents.getProcessors()); - connectionResource.populateRemainingConnectionsContent(snippetContents.getConnections()); - inputPortResource.populateRemainingInputPortsContent(snippetContents.getInputPorts()); - outputPortResource.populateRemainingOutputPortsContent(snippetContents.getOutputPorts()); - remoteProcessGroupResource.populateRemainingRemoteProcessGroupsContent(snippetContents.getRemoteProcessGroups()); - funnelResource.populateRemainingFunnelsContent(snippetContents.getFunnels()); - labelResource.populateRemainingLabelsContent(snippetContents.getLabels()); - processGroupResource.populateRemainingProcessGroupsContent(snippetContents.getProcessGroups()); - } - - return snippet; - } - - /** - * Creates a new snippet based on the specified contents. - * - * @param httpServletRequest request - * @param version The revision is used to verify the client is working with - * the latest version of the flow. - * @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. - * @param parentGroupId The id of the process group the components in this - * snippet belong to. - * @param linked Whether or not this snippet is linked to the underlying - * data flow. If a linked snippet is deleted, the components that comprise - * the snippet are also deleted. - * @param processorIds The ids of any processors in this snippet. - * @param processGroupIds The ids of any process groups in this snippet. - * @param remoteProcessGroupIds The ids of any remote process groups in this - * snippet. - * @param inputPortIds The ids of any input ports in this snippet. - * @param outputPortIds The ids of any output ports in this snippet. - * @param connectionIds The ids of any connections in this snippet. - * @param labelIds The ids of any labels in this snippet. - * @param funnelIds The ids of any funnels in this snippet. - * @return A snippetEntity - */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response createSnippet( - @Context HttpServletRequest httpServletRequest, - @FormParam(VERSION) LongParameter version, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormParam("parentGroupId") String parentGroupId, - @FormParam("linked") @DefaultValue(LINKED) Boolean linked, - @FormParam("processorIds[]") List processorIds, - @FormParam("processGroupIds[]") List processGroupIds, - @FormParam("remoteProcessGroupIds[]") List remoteProcessGroupIds, - @FormParam("inputPortIds[]") List inputPortIds, - @FormParam("outputPortIds[]") List outputPortIds, - @FormParam("connectionIds[]") List connectionIds, - @FormParam("labelIds[]") List labelIds, - @FormParam("funnelIds[]") List funnelIds) { - - // create the snippet dto - final SnippetDTO snippetDTO = new SnippetDTO(); - snippetDTO.setParentGroupId(parentGroupId); - snippetDTO.setLinked(linked); - snippetDTO.setProcessors(new HashSet<>(processorIds)); - snippetDTO.setProcessGroups(new HashSet<>(processGroupIds)); - snippetDTO.setRemoteProcessGroups(new HashSet<>(remoteProcessGroupIds)); - snippetDTO.setInputPorts(new HashSet<>(inputPortIds)); - snippetDTO.setOutputPorts(new HashSet<>(outputPortIds)); - snippetDTO.setConnections(new HashSet<>(connectionIds)); - snippetDTO.setLabels(new HashSet<>(labelIds)); - snippetDTO.setFunnels(new HashSet<>(funnelIds)); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - if (version != null) { - revision.setVersion(version.getLong()); - } - - // create the snippet entity - final SnippetEntity snippetEntity = new SnippetEntity(); - snippetEntity.setRevision(revision); - snippetEntity.setSnippet(snippetDTO); - - // build the response - return createSnippet(httpServletRequest, snippetEntity); - } - - /** - * Creates a snippet based off the specified configuration. - * - * @param httpServletRequest request - * @param snippetEntity A snippetEntity - * @return A snippetEntity - */ - @POST - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Creates a snippet", - response = SnippetEntity.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 = 404, message = "The specified resource could not be found."), - @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 createSnippet( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The snippet configuration details.", - required = true - ) - final SnippetEntity snippetEntity) { - - if (snippetEntity == null || snippetEntity.getSnippet() == null) { - throw new IllegalArgumentException("Snippet details must be specified."); - } - - if (snippetEntity.getRevision() == null) { - throw new IllegalArgumentException("Revision must be specified."); - } - - if (snippetEntity.getSnippet().getId() != null) { - throw new IllegalArgumentException("Snippet ID cannot be specified."); - } - - // ensure the group id has been specified - if (snippetEntity.getSnippet().getParentGroupId() == null) { - throw new IllegalArgumentException("The group id must be specified when creating a snippet."); - } - - // if cluster manager, convert POST to PUT (to maintain same ID across nodes) and replicate - if (properties.isClusterManager()) { - - // create ID for resource - final String id = UUID.randomUUID().toString(); - - // set ID for resource - snippetEntity.getSnippet().setId(id); - - // convert POST request to PUT request to force entity ID to be the same across nodes - URI putUri = null; - try { - putUri = new URI(getAbsolutePath().toString() + "/" + id); - } catch (final URISyntaxException e) { - throw new WebApplicationException(e); - } - - // change content type to JSON for serializing entity - final Map headersToOverride = new HashMap<>(); - headersToOverride.put("content-type", MediaType.APPLICATION_JSON); - - // replicate put request - return (Response) clusterManager.applyRequest(HttpMethod.PUT, putUri, updateClientId(snippetEntity), getHeaders(headersToOverride)).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // create the snippet - final RevisionDTO revision = snippetEntity.getRevision(); - final ConfigurationSnapshot response = serviceFacade.createSnippet(new Revision(revision.getVersion(), revision.getClientId()), snippetEntity.getSnippet()); - - // get the snippet - final SnippetDTO snippet = response.getConfiguration(); - - // always prune the response when creating - snippet.setContents(null); - - // get the updated revision - final RevisionDTO updatedRevision = new RevisionDTO(); - updatedRevision.setClientId(revision.getClientId()); - updatedRevision.setVersion(response.getVersion()); - - // build the response entity - SnippetEntity entity = new SnippetEntity(); - entity.setRevision(updatedRevision); - entity.setSnippet(populateRemainingSnippetContent(snippet)); - - // build the response - return clusterContext(generateCreatedResponse(URI.create(snippet.getUri()), entity)).build(); - } - - /** - * Retrieves the specified snippet. - * - * @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. - * @param verbose Whether or not to include the contents of the snippet in - * the response. - * @param id The id of the snippet to retrieve. - * @return A snippetEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("{id}") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets a snippet", - response = SnippetEntity.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 = 404, message = "The specified resource could not be found."), - @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 getSnippet( - @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, - @ApiParam( - value = "Whether to include configuration details for the components specified in the snippet.", - required = false - ) - @QueryParam("verbose") @DefaultValue(VERBOSE) Boolean verbose, - @ApiParam( - value = "The snippet id.", - required = true - ) - @PathParam("id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // get the snippet - final SnippetDTO snippet = serviceFacade.getSnippet(id); - - // prune the response if necessary - if (!verbose) { - snippet.setContents(null); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final SnippetEntity entity = new SnippetEntity(); - entity.setRevision(revision); - entity.setSnippet(populateRemainingSnippetContent(snippet)); - - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Updates the specified snippet. - * - * @param httpServletRequest request - * @param version The revision is used to verify the client is working with - * the latest version of the flow. - * @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. - * @param verbose Whether or not to include the contents of the snippet in - * the response. - * @param id The id of the snippet to update. - * @param parentGroupId The id of the process group to move the contents of - * this snippet to. - * @param linked Whether or not this snippet is linked to the underlying - * data flow. If a linked snippet is deleted, the components that comprise - * the snippet are also deleted. - * @return A snippetEntity. - */ - @PUT - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("{id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response updateSnippet( - @Context HttpServletRequest httpServletRequest, - @FormParam(VERSION) LongParameter version, - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormParam("verbose") @DefaultValue(VERBOSE) Boolean verbose, - @PathParam("id") String id, - @FormParam("parentGroupId") String parentGroupId, - @FormParam("linked") Boolean linked) { - - // create the snippet dto - final SnippetDTO snippetDTO = new SnippetDTO(); - snippetDTO.setId(id); - snippetDTO.setParentGroupId(parentGroupId); - snippetDTO.setLinked(linked); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - if (version != null) { - revision.setVersion(version.getLong()); - } - - // create the snippet entity - final SnippetEntity entity = new SnippetEntity(); - entity.setRevision(revision); - entity.setSnippet(snippetDTO); - - // build the response - return updateSnippet(httpServletRequest, id, entity); - } - - /** - * Updates the specified snippet. The contents of the snippet (component - * ids) cannot be updated once the snippet is created. - * - * @param httpServletRequest request - * @param id The id of the snippet. - * @param snippetEntity A snippetEntity - * @return A snippetEntity - */ - @PUT - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("{id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Updates a snippet", - response = SnippetEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 updateSnippet( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The snippet id.", - required = true - ) - @PathParam("id") String id, - @ApiParam( - value = "The snippet configuration details.", - required = true - ) - final SnippetEntity snippetEntity) { - - if (snippetEntity == null || snippetEntity.getSnippet() == null) { - throw new IllegalArgumentException("Snippet details must be specified."); - } - - if (snippetEntity.getRevision() == null) { - throw new IllegalArgumentException("Revision must be specified."); - } - - // ensure the ids are the same - final SnippetDTO requestSnippetDTO = snippetEntity.getSnippet(); - if (!id.equals(requestSnippetDTO.getId())) { - throw new IllegalArgumentException(String.format("The snippet id (%s) in the request body does not equal the " - + "snippet id of the requested resource (%s).", requestSnippetDTO.getId(), id)); - } - - // replicate if cluster manager - if (properties.isClusterManager()) { - // change content type to JSON for serializing entity - final Map headersToOverride = new HashMap<>(); - headersToOverride.put("content-type", MediaType.APPLICATION_JSON); - - // replicate the request - return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(snippetEntity), getHeaders(headersToOverride)).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - serviceFacade.verifyUpdateSnippet(requestSnippetDTO); - return generateContinueResponse().build(); - } - - // update the snippet - final RevisionDTO revision = snippetEntity.getRevision(); - final ConfigurationSnapshot controllerResponse = serviceFacade.updateSnippet( - new Revision(revision.getVersion(), revision.getClientId()), snippetEntity.getSnippet()); - - // get the results - final SnippetDTO snippet = controllerResponse.getConfiguration(); - - // always prune update responses - snippet.setContents(null); - - // get the updated revision - final RevisionDTO updatedRevision = new RevisionDTO(); - updatedRevision.setClientId(revision.getClientId()); - updatedRevision.setVersion(controllerResponse.getVersion()); - - // build the response entity - SnippetEntity entity = new SnippetEntity(); - entity.setRevision(updatedRevision); - entity.setSnippet(populateRemainingSnippetContent(snippet)); - - if (controllerResponse.isNew()) { - return clusterContext(generateCreatedResponse(URI.create(snippet.getUri()), entity)).build(); - } else { - return clusterContext(generateOkResponse(entity)).build(); - } - } - - /** - * Removes the specified snippet. - * - * @param httpServletRequest request - * @param version The revision is used to verify the client is working with - * the latest version of the flow. - * @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. - * @param id The id of the snippet to remove. - * @return A entity containing the client id and an updated revision. - */ - @DELETE - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("{id}") - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Deletes a snippet", - response = SnippetEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 removeSnippet( - @Context HttpServletRequest httpServletRequest, - @ApiParam( - value = "The revision is used to verify the client is working with the latest version of the flow.", - required = false - ) - @QueryParam(VERSION) LongParameter version, - @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, - @ApiParam( - value = "The snippet id.", - required = true - ) - @PathParam("id") String id) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - serviceFacade.verifyDeleteSnippet(id); - return generateContinueResponse().build(); - } - - // determine the specified version - Long clientVersion = null; - if (version != null) { - clientVersion = version.getLong(); - } - - // delete the specified snippet - final ConfigurationSnapshot controllerResponse = serviceFacade.deleteSnippet(new Revision(clientVersion, clientId.getClientId()), id); - - // get the updated revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - revision.setVersion(controllerResponse.getVersion()); - - // build the response entity - SnippetEntity entity = new SnippetEntity(); - entity.setRevision(revision); - - return clusterContext(generateOkResponse(entity)).build(); - } - - // setters - public void setServiceFacade(NiFiServiceFacade serviceFacade) { - this.serviceFacade = serviceFacade; - } - - public void setClusterManager(WebClusterManager clusterManager) { - this.clusterManager = clusterManager; - } - - public void setProcessorResource(ProcessorResource processorResource) { - this.processorResource = processorResource; - } - - public void setInputPortResource(InputPortResource inputPortResource) { - this.inputPortResource = inputPortResource; - } - - public void setOutputPortResource(OutputPortResource outputPortResource) { - this.outputPortResource = outputPortResource; - } - - public void setFunnelResource(FunnelResource funnelResource) { - this.funnelResource = funnelResource; - } - - public void setLabelResource(LabelResource labelResource) { - this.labelResource = labelResource; - } - - public void setRemoteProcessGroupResource(RemoteProcessGroupResource remoteProcessGroupResource) { - this.remoteProcessGroupResource = remoteProcessGroupResource; - } - - public void setConnectionResource(ConnectionResource connectionResource) { - this.connectionResource = connectionResource; - } - - public void setProcessGroupResource(ProcessGroupResource processGroupResource) { - this.processGroupResource = processGroupResource; - } - - public void setProperties(NiFiProperties properties) { - this.properties = properties; - } -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java index 213190a453..3d74f2e1ce 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java @@ -23,6 +23,7 @@ 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.Authorizer; import org.apache.nifi.cluster.manager.NodeResponse; import org.apache.nifi.cluster.manager.exception.UnknownNodeException; import org.apache.nifi.cluster.manager.impl.WebClusterManager; @@ -52,15 +53,15 @@ import java.util.Set; */ @Path("/system-diagnostics") @Api( - value = "/system-diagnostics", - description = "Provides diagnostics for the system NiFi is running on" + value = "/system-diagnostics", + description = "Endpoint for accessing system diagnostics." ) public class SystemDiagnosticsResource extends ApplicationResource { private NiFiServiceFacade serviceFacade; private WebClusterManager clusterManager; private NiFiProperties properties; - + private Authorizer authorizer; /** * Gets the system diagnostics for this NiFi instance. @@ -72,7 +73,7 @@ public class SystemDiagnosticsResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Produces(MediaType.APPLICATION_JSON) // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") @ApiOperation( value = "Gets the diagnostics for the system NiFi is running on", @@ -161,6 +162,10 @@ public class SystemDiagnosticsResource extends ApplicationResource { this.clusterManager = clusterManager; } + public void setAuthorizer(Authorizer authorizer) { + this.authorizer = authorizer; + } + public void setProperties(NiFiProperties properties) { this.properties = properties; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java index 673373adc6..5657fff656 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.web.api; -import com.sun.jersey.multipart.FormDataParam; import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; @@ -30,7 +29,6 @@ import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.api.dto.RevisionDTO; import org.apache.nifi.web.api.dto.TemplateDTO; import org.apache.nifi.web.api.entity.TemplateEntity; -import org.apache.nifi.web.api.entity.TemplatesEntity; import org.apache.nifi.web.api.request.ClientIdParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,10 +37,8 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; -import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -50,22 +46,16 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.transform.stream.StreamSource; -import java.io.InputStream; -import java.net.URI; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; import java.util.Set; /** * RESTful endpoint for managing a Template. */ -@Api(hidden = true) +@Path("/templates") +@Api( + value = "/templates", + description = "Endpoint for managing a Template." +) public class TemplateResource extends ApplicationResource { private static final Logger logger = LoggerFactory.getLogger(TemplateResource.class); @@ -90,279 +80,12 @@ public class TemplateResource extends ApplicationResource { /** * Populates the uri for the specified template. */ - private TemplateDTO populateRemainingTemplateContent(TemplateDTO template) { + public TemplateDTO populateRemainingTemplateContent(TemplateDTO template) { // populate the template uri - template.setUri(generateResourceUri("controller", "templates", template.getId())); + template.setUri(generateResourceUri("templates", template.getId())); return template; } - /** - * Retrieves all the of templates 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 templatesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Gets all templates", - response = TemplatesEntity.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 getTemplates( - @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 templates - final Set templates = populateRemainingTemplatesContent(serviceFacade.getTemplates()); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // create the response entity - final TemplatesEntity entity = new TemplatesEntity(); - entity.setRevision(revision); - entity.setTemplates(templates); - entity.setGenerated(new Date()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - - /** - * Creates a new template based off of the specified template. - * - * @param httpServletRequest request - * @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. - * @param name The name of the template. - * @param description The description of the template. - * @param snippetId The id of the snippet this template is based on. - * @return A templateEntity - */ - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - @ApiOperation( - value = "Creates a template", - response = TemplateEntity.class, - authorizations = { - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM") - } - ) - @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 = 404, message = "The specified resource could not be found."), - @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 createTemplate( - @Context 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 - ) - @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @ApiParam( - value = "The template name.", - required = true - ) - @FormParam("name") String name, - @ApiParam( - value = "The template description.", - required = false - ) - @FormParam("description") String description, - @ApiParam( - value = "The id of the snippet whose contents will comprise the template.", - required = true - ) - @FormParam("snippetId") String snippetId) { - - // replicate if cluster manager - if (properties.isClusterManager()) { - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse(); - } - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - // create the template and generate the json - final TemplateDTO template = serviceFacade.createTemplate(name, description, snippetId); - populateRemainingTemplateContent(template); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // build the response entity - final TemplateEntity entity = new TemplateEntity(); - entity.setRevision(revision); - entity.setTemplate(template); - - // build the response - return clusterContext(generateCreatedResponse(URI.create(template.getUri()), entity)).build(); - } - - /** - * Imports the specified template. - * - * @param httpServletRequest request - * @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. - * @param in The template stream - * @return A templateEntity or an errorResponse XML snippet. - */ - @POST - @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces(MediaType.APPLICATION_XML) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response importTemplate( - @Context HttpServletRequest httpServletRequest, - @FormDataParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, - @FormDataParam("template") InputStream in) { - - // unmarshal the template - final TemplateDTO template; - try { - JAXBContext context = JAXBContext.newInstance(TemplateDTO.class); - Unmarshaller unmarshaller = context.createUnmarshaller(); - JAXBElement templateElement = unmarshaller.unmarshal(new StreamSource(in), TemplateDTO.class); - template = templateElement.getValue(); - } catch (JAXBException jaxbe) { - logger.warn("An error occurred while parsing a template.", jaxbe); - String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode()); - return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); - } catch (IllegalArgumentException iae) { - logger.warn("Unable to import template.", iae); - String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode(), iae.getMessage()); - return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); - } catch (Exception e) { - logger.warn("An error occurred while importing a template.", e); - String responseXml = String.format("", - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage()); - return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); - } - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - revision.setClientId(clientId.getClientId()); - - // build the response entity - TemplateEntity entity = new TemplateEntity(); - entity.setRevision(revision); - entity.setTemplate(template); - - // replicate if cluster manager - if (properties.isClusterManager()) { - // change content type to JSON for serializing entity - final Map headersToOverride = new HashMap<>(); - headersToOverride.put("content-type", MediaType.APPLICATION_JSON); - - // replicate the request - return clusterManager.applyRequest(HttpMethod.POST, getAbsolutePath(), updateClientId(entity), getHeaders(headersToOverride)).getResponse(); - } - - // otherwise import the template locally - return importTemplate(httpServletRequest, entity); - } - - /** - * Imports the specified template. - * - * @param httpServletRequest request - * @param templateEntity A templateEntity. - * @return A templateEntity. - */ - @POST - @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces(MediaType.APPLICATION_XML) - @Path("") // necessary due to bug in swagger - // TODO - @PreAuthorize("hasRole('ROLE_DFM')") - public Response importTemplate( - @Context HttpServletRequest httpServletRequest, - TemplateEntity templateEntity) { - - // handle expects request (usually from the cluster manager) - final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER); - if (expects != null) { - return generateContinueResponse().build(); - } - - try { - // verify the template was specified - if (templateEntity == null || templateEntity.getTemplate() == null) { - throw new IllegalArgumentException("Template details must be specified."); - } - - // import the template - final TemplateDTO template = serviceFacade.importTemplate(templateEntity.getTemplate()); - populateRemainingTemplateContent(template); - - // create the revision - final RevisionDTO revision = new RevisionDTO(); - if (templateEntity.getRevision() == null) { - revision.setClientId(new ClientIdParameter().getClientId()); - } else { - revision.setClientId(templateEntity.getRevision().getClientId()); - } - - // build the response entity - TemplateEntity entity = new TemplateEntity(); - entity.setRevision(revision); - entity.setTemplate(template); - - // build the response - return clusterContext(generateCreatedResponse(URI.create(template.getUri()), entity)).build(); - } catch (IllegalArgumentException | IllegalStateException e) { - logger.info("Unable to import template: " + e); - String responseXml = String.format("", Response.Status.BAD_REQUEST.getStatusCode(), e.getMessage()); - return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); - } catch (Exception e) { - logger.warn("An error occurred while importing a template.", e); - String responseXml - = String.format("", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getMessage()); - return Response.status(Response.Status.OK).entity(responseXml).type("application/xml").build(); - } - } - /** * Retrieves the specified template. * @@ -375,7 +98,7 @@ public class TemplateResource extends ApplicationResource { @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_XML) - @Path("{id}") + @Path("{id}/download") // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") @ApiOperation( value = "Exports a template", @@ -442,7 +165,7 @@ public class TemplateResource extends ApplicationResource { */ @DELETE @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + @Produces(MediaType.APPLICATION_JSON) @Path("{id}") // TODO - @PreAuthorize("hasRole('ROLE_DFM')") @ApiOperation( diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index 0ae7649ca9..90acfb813d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -34,6 +34,7 @@ import org.apache.nifi.action.details.PurgeDetails; import org.apache.nifi.annotation.behavior.Stateful; import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.authorization.Resource; import org.apache.nifi.cluster.HeartbeatPayload; import org.apache.nifi.cluster.event.Event; import org.apache.nifi.cluster.manager.StatusMerger; @@ -1479,6 +1480,7 @@ public final class DtoFactory { final FlowSnippetDTO dto = new FlowSnippetDTO(); for (final ProcessorNode procNode : group.getProcessors()) { + // authorization check dto.getProcessors().add(createProcessorDto(procNode)); } @@ -1960,6 +1962,19 @@ public final class DtoFactory { return dto; } + /** + * Creates a ResourceDTO from the specified Resource. + * + * @param resource resource + * @return dto + */ + public ResourceDTO createResourceDto(final Resource resource) { + final ResourceDTO dto = new ResourceDTO(); + dto.setIdentifier(resource.getIdentifier()); + dto.setName(resource.getName()); + return dto; + } + /** * Creates a ProcessorConfigDTO from the specified ProcessorNode. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index 7377985dbb..e8686a309c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -20,6 +20,9 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.admin.service.KeyService; +import org.apache.nifi.authorization.Resource; +import org.apache.nifi.authorization.resource.ResourceFactory; +import org.apache.nifi.authorization.resource.ResourceType; import org.apache.nifi.cluster.protocol.NodeIdentifier; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.connectable.Connectable; @@ -31,11 +34,15 @@ import org.apache.nifi.controller.ControllerService; 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; +import org.apache.nifi.controller.Template; +import org.apache.nifi.controller.label.Label; import org.apache.nifi.controller.queue.FlowFileQueue; import org.apache.nifi.controller.queue.QueueSize; import org.apache.nifi.controller.repository.ContentNotFoundException; import org.apache.nifi.controller.repository.claim.ContentDirection; +import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.controller.status.ConnectionStatus; import org.apache.nifi.controller.status.PortStatus; import org.apache.nifi.controller.status.ProcessGroupStatus; @@ -80,6 +87,7 @@ import org.apache.nifi.web.NiFiCoreException; import org.apache.nifi.web.ResourceNotFoundException; import org.apache.nifi.web.api.dto.DocumentedTypeDTO; import org.apache.nifi.web.api.dto.DtoFactory; +import org.apache.nifi.web.api.dto.TemplateDTO; import org.apache.nifi.web.api.dto.provenance.AttributeDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceDTO; import org.apache.nifi.web.api.dto.provenance.ProvenanceEventDTO; @@ -706,6 +714,79 @@ public class ControllerFacade { return flowController.getSystemDiagnostics(); } + public List getResources() { + final List resources = new ArrayList<>(); + resources.add(ResourceFactory.getSystemResource()); + resources.add(ResourceFactory.getControllerResource()); + resources.add(ResourceFactory.getFlowResource()); + resources.add(ResourceFactory.getProvenanceResource()); + resources.add(ResourceFactory.getProxyResource()); + resources.add(ResourceFactory.getResourceResource()); + + final ProcessGroup root = flowController.getGroup(flowController.getRootGroupId()); + + // add each processor + for (final ProcessorNode processor : root.findAllProcessors()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.Processor, processor.getIdentifier(), processor.getName())); + resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.Processor, processor.getIdentifier(), processor.getName())); + } + + // add each connection + for (final Connection connection : root.findAllConnections()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.Connection, connection.getIdentifier(), connection.getName())); + resources.add(ResourceFactory.getFlowFileQueueResource(connection.getIdentifier(), connection.getName())); + } + + // add each label + for (final Label label : root.findAllLabels()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.Label, label.getIdentifier(), label.getValue())); + } + + // add each process group + for (final ProcessGroup processGroup : root.findAllProcessGroups()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.ProcessGroup, processGroup.getIdentifier(), processGroup.getName())); + resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.ProcessGroup, processGroup.getIdentifier(), processGroup.getName())); + } + + // add each remote process group + for (final RemoteProcessGroup remoteProcessGroup : root.findAllRemoteProcessGroups()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.RemoteProcessGroup, remoteProcessGroup.getIdentifier(), remoteProcessGroup.getName())); + resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.RemoteProcessGroup, remoteProcessGroup.getIdentifier(), remoteProcessGroup.getName())); + } + + // add each input port + for (final Port inputPort : root.findAllInputPorts()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.InputPort, inputPort.getIdentifier(), inputPort.getName())); + resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.InputPort, inputPort.getIdentifier(), inputPort.getName())); + } + + // add each output port + for (final Port outputPort : root.findAllOutputPorts()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.OutputPort, outputPort.getIdentifier(), outputPort.getName())); + resources.add(ResourceFactory.getComponentProvenanceResource(ResourceType.OutputPort, outputPort.getIdentifier(), outputPort.getName())); + } + + // add each controller service + for (final ControllerServiceNode controllerService : flowController.getAllControllerServices()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.ControllerService, controllerService.getIdentifier(), controllerService.getName())); + } + + // add each reporting task + for (final ReportingTaskNode reportingTask : flowController.getAllReportingTasks()) { + resources.add(ResourceFactory.getComponentResource(ResourceType.ReportingTask, reportingTask.getIdentifier(), reportingTask.getName())); + } + + // add each template + for (final Template template : flowController.getTemplates()) { + final TemplateDTO details = template.getDetails(); + resources.add(ResourceFactory.getComponentResource(ResourceType.Template, details.getId(), details.getName())); + } + + // TODO - need token resource? + // resources.add(ResourceFactory.getTokenResource()); + return resources; + } + /** * Gets the available options for searching provenance. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/NodeRequestFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/NodeRequestFilter.java index 68a693cbb5..a07229331a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/NodeRequestFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/filter/NodeRequestFilter.java @@ -65,16 +65,6 @@ public class NodeRequestFilter implements Filter { HttpServletRequest httpReq = (HttpServletRequest) req; HttpServletResponse httpResp = (HttpServletResponse) resp; - /* - * If we're the cluster manager or we're sent head requests, continue. - * Head requests are included because there exists a AJAX/XHR race - * condition between the following requests: - * HEAD /nifi-api/cluster - * GET /nifi-api/controller/config - * If the head request finishes first, then the UI JavaScript will display - * a default error message and not display the error message given in this - * filter for directly accessing connected nodes. - */ if (properties.isClusterManager() || "HEAD".equalsIgnoreCase(httpReq.getMethod())) { filterChain.doFilter(req, resp); } else { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index a73e0b00ab..0f79369165 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -156,12 +156,23 @@ - + - + + + + + + + + + + + + @@ -192,6 +203,8 @@ + + @@ -203,6 +216,11 @@ + + + + + @@ -228,19 +246,6 @@ - - - - - - - - - - - - - diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js index cdc062ef0d..f22477fe71 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js @@ -135,7 +135,7 @@ $(document).ready(function () { if (top === window) { $.ajax({ type: 'GET', - url: '../nifi-api/controller/banners', + url: '../nifi-api/flow/banners', dataType: 'json' }).then(function (response) { // ensure the banners response is specified @@ -184,7 +184,7 @@ $(document).ready(function () { // get the about details var about = $.ajax({ type: 'GET', - url: '../nifi-api/controller/about', + url: '../nifi-api/flow/about', dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java index 3f4562924f..3de5a2d13a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java @@ -16,7 +16,6 @@ */ package org.apache.nifi.web.security.anonymous; -import org.apache.nifi.admin.service.KeyService; import org.apache.nifi.user.NiFiUser; import org.apache.nifi.web.security.token.NiFiAuthenticationToken; import org.apache.nifi.web.security.user.NiFiUserDetails; @@ -28,7 +27,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi import javax.servlet.http.HttpServletRequest; /** - * Custom AnonymouseAuthenticationFilter used to grant additional authorities depending on the current operating mode. + * Custom AnonymousAuthenticationFilter used to grant additional authorities depending on the current operating mode. */ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { @@ -36,8 +35,6 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { private static final String ANONYMOUS_KEY = "anonymousNifiKey"; - private KeyService keyService; - public NiFiAnonymousUserFilter() { super(ANONYMOUS_KEY); } @@ -47,9 +44,4 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { return new NiFiAuthenticationToken(new NiFiUserDetails(NiFiUser.ANONYMOUS)); } - /* setters */ - public void setKeyService(KeyService keyService) { - this.keyService = keyService; - } - } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml index c798191142..ab2fa80269 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml @@ -347,6 +347,7 @@ ${staging.dir}/js/nf/nf-ajax-setup.js ${staging.dir}/js/nf/provenance/nf-provenance.js ${staging.dir}/js/nf/provenance/nf-provenance-table.js + ${staging.dir}/js/nf/provenance/nf-provenance-lineage.js @@ -361,6 +362,7 @@ ${staging.dir}/js/nf/nf-ajax-setup.js ${staging.dir}/js/nf/nf-processor-details.js ${staging.dir}/js/nf/nf-connection-details.js + ${staging.dir}/js/nf/nf-status-history.js ${staging.dir}/js/nf/summary/nf-summary.js ${staging.dir}/js/nf/summary/nf-summary-table.js ${staging.dir}/js/nf/summary/nf-cluster-search.js diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/java/org/apache/nifi/web/filter/IeEdgeHeader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/java/org/apache/nifi/web/filter/IeEdgeHeader.java deleted file mode 100644 index f45682bf40..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/java/org/apache/nifi/web/filter/IeEdgeHeader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.web.filter; - -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; - -/** - * A filter to set a response header directing IE to use the most recent - * document mode. - * - */ -public class IeEdgeHeader implements Filter { - - @Override - public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain) - throws IOException, ServletException { - - // add the header - final HttpServletResponse response = (HttpServletResponse) resp; - response.addHeader("X-UA-Compatible", "IE=edge"); - - // continue the chain - filterChain.doFilter(req, resp); - } - - @Override - public void init(final FilterConfig config) { - } - - @Override - public void destroy() { - } - -} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties index dee70b883c..a87195ca7d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties @@ -20,7 +20,8 @@ nf.provenance.script.tags=\n\ \n\ \n\ - +\n\ + nf.provenance.style.tags=\n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties index 752ca68abd..e130655fd7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties @@ -21,6 +21,7 @@ nf.summary.script.tags=\n\ \n\ \n\ +\n\ \n\ \n\ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index fbab590a5c..6015e18495 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -38,6 +38,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp index 496bfd1775..eebe3e409c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp @@ -30,6 +30,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp index 45dff93380..68ca6ab8ec 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp @@ -36,6 +36,7 @@ + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp index 4752e4ad20..f4f7bab3e0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/templates/templates-content.jsp @@ -15,7 +15,9 @@ limitations under the License. --%> <%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
NiFi Templates
@@ -39,7 +41,7 @@
Browse -
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml index e577209c13..e940b6e574 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/web.xml @@ -136,15 +136,6 @@ /login - - IeEdgeHeader - org.apache.nifi.web.filter.IeEdgeHeader - - - IeEdgeHeader - /* - - canvas.jsp /WEB-INF/pages/canvas.jsp diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js index 4de1e1c63e..c334103c63 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js @@ -810,7 +810,7 @@ var promptForNewControllerService = function (gridContainer, grid, item, serviceType, configurationOptions) { $.ajax({ type: 'GET', - url: '../nifi-api/controller/controller-service-types', + url: '../nifi-api/flow/controller-service-types', data: { serviceType: serviceType }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js index d318d27a30..7db1b0e063 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js @@ -32,9 +32,9 @@ nf.BulletinBoard = (function () { maxBulletins: 1000, defaultFilterText: 'Filter', urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', - bulletinBoard: '../nifi-api/bulletin-board' + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', + bulletinBoard: '../nifi-api/flow/bulletin-board' }, styles: { filterList: 'bulletin-board-filter-list', @@ -113,7 +113,7 @@ nf.BulletinBoard = (function () { // get the about details var getTitle = $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js index b5f664f26c..e950080b70 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js @@ -21,6 +21,7 @@ nf.Actions = (function () { var config = { urls: { + api: '../nifi-api', controller: '../nifi-api/controller' } }; @@ -516,7 +517,7 @@ nf.Actions = (function () { } }; - updateResource(config.urls.controller + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()), entity).done(updateProcessGroup); + updateResource(config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()), entity).done(updateProcessGroup); } else { var componentsToStart = selection.filter(function (d) { return nf.CanvasUtils.isRunnable(d3.select(this)); @@ -593,7 +594,7 @@ nf.Actions = (function () { } }; - updateResource(config.urls.controller + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()), entity).done(updateProcessGroup); + updateResource(config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()), entity).done(updateProcessGroup); } else { var componentsToStop = selection.filter(function (d) { return nf.CanvasUtils.isStoppable(d3.select(this)); @@ -1064,7 +1065,7 @@ nf.Actions = (function () { // issue the request to delete the flow files $.ajax({ type: 'POST', - url: connection.component.uri + '/drop-requests', + url: '../nifi-api/flowfile-queues/' + connection.component.id + '/drop-requests', dataType: 'json', contentType: 'application/json' }).done(function(response) { @@ -1251,17 +1252,19 @@ nf.Actions = (function () { // create the snippet nf.Snippet.create(snippetDetails).done(function (response) { var snippet = response.snippet; + var createSnippetEntity = { + 'name': templateName, + 'description': templateDescription, + 'snippetId': snippet.id + }; // create the template $.ajax({ type: 'POST', - url: config.urls.controller + '/templates', - data: { - name: templateName, - description: templateDescription, - snippetId: snippet.id - }, - dataType: 'json' + url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/templates', + data: JSON.stringify(createSnippetEntity), + dataType: 'json', + contentType: 'application/json' }).done(function () { // show the confirmation dialog nf.Dialog.showOkDialog({ @@ -1361,7 +1364,7 @@ nf.Actions = (function () { } // copy the snippet to the new location - nf.Snippet.copy(snippet.id, nf.Canvas.getGroupId(), origin).done(function (copyResponse) { + nf.Snippet.copy(snippet.id, origin).done(function (copyResponse) { var snippetContents = copyResponse.contents; // update the graph accordingly diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js index 03e2513c31..6ed6c5d4bb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js @@ -23,8 +23,7 @@ nf.CanvasHeader = (function () { var config = { urls: { - helpDocument: '../nifi-docs/documentation', - controllerAbout: '../nifi-api/controller/about' + helpDocument: '../nifi-docs/documentation' } }; @@ -61,7 +60,9 @@ nf.CanvasHeader = (function () { // mouse over for the templates link nf.Common.addHoverEffect('#templates-link', 'templates-link', 'templates-link-hover').click(function () { - nf.Shell.showPage('templates'); + nf.Shell.showPage('templates?' + $.param({ + groupId: nf.Canvas.getGroupId() + })); }); // mouse over for the flow settings link @@ -93,18 +94,6 @@ nf.CanvasHeader = (function () { nf.CanvasHeader.reloadAndClearWarnings(); }); - // get the about details - $.ajax({ - type: 'GET', - url: config.urls.controllerAbout, - dataType: 'json' - }).done(function (response) { - var aboutDetails = response.about; - // set the document title and the about title - document.title = aboutDetails.title; - $('#nf-version').text(aboutDetails.version); - }).fail(nf.Common.handleAjaxError); - // configure the about dialog $('#nf-about').modal({ overlayBackground: true, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js index 3d6a33fcba..de40653002 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js @@ -38,8 +38,7 @@ nf.CanvasToolbox = (function () { urls: { api: '../nifi-api', controller: '../nifi-api/controller', - processorTypes: '../nifi-api/controller/processor-types', - templates: '../nifi-api/controller/templates' + processorTypes: '../nifi-api/flow/processor-types' } }; @@ -758,7 +757,7 @@ nf.CanvasToolbox = (function () { var promptForTemplate = function (pt) { $.ajax({ type: 'GET', - url: config.urls.templates, + url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/templates', dataType: 'json' }).done(function (response) { var templates = response.templates; @@ -823,20 +822,20 @@ nf.CanvasToolbox = (function () { * @argument {object} pt The point that the template was dropped */ var createTemplate = function (templateId, pt) { - var revision = nf.Client.getRevision(); + var instantiateTemplateInstance = { + 'revision': nf.Client.getRevision(), + 'templateId': templateId, + 'originX': pt.x, + 'originY': pt.y + }; // create a new instance of the new template $.ajax({ type: 'POST', url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/template-instance', - data: { - version: revision.version, - clientId: revision.clientId, - templateId: templateId, - originX: pt.x, - originY: pt.y - }, - dataType: 'json' + data: JSON.stringify(instantiateTemplateInstance), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index 84cc2c4045..175a598544 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -77,28 +77,22 @@ nf.Canvas = (function () { var config = { urls: { api: '../nifi-api', - identity: '../nifi-api/controller/identity', + identity: '../nifi-api/flow/identity', authorities: '../nifi-api/controller/authorities', kerberos: '../nifi-api/access/kerberos', - revision: '../nifi-api/controller/revision', - status: '../nifi-api/controller/status', - bulletinBoard: '../nifi-api/bulletin-board', - banners: '../nifi-api/controller/banners', + revision: '../nifi-api/flow/revision', + status: '../nifi-api/flow/status', + bulletinBoard: '../nifi-api/flow/bulletin-board', + banners: '../nifi-api/flow/banners', controller: '../nifi-api/controller', controllerConfig: '../nifi-api/controller/config', + about: '../nifi-api/flow/about', accessConfig: '../nifi-api/access/config', cluster: '../nifi-api/cluster', d3Script: 'js/d3/d3.min.js' } }; - /** - * Loads D3. - */ - var loadD3 = function () { - return nf.Common.cachedScript(config.urls.d3Script); - }; - /** * Starts polling for the revision. * @@ -834,7 +828,7 @@ nf.Canvas = (function () { return $.Deferred(function (deferred) { $.ajax({ type: 'GET', - url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupId) + '/status', + url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupId) + '/status', data: { recursive: false }, @@ -1020,6 +1014,15 @@ nf.Canvas = (function () { dataType: 'json' }); + // get the about details + var aboutXhr = $.ajax({ + type: 'GET', + url: config.urls.about, + dataType: 'json' + }).done(function (response) { + + }).fail(nf.Common.handleAjaxError); + // get the login config var loginXhr = $.ajax({ type: 'GET', @@ -1046,9 +1049,10 @@ nf.Canvas = (function () { }).promise(); // ensure the config requests are loaded - $.when(configXhr, loginXhr, userXhr).done(function (configResult, loginResult) { + $.when(configXhr, loginXhr, aboutXhr, userXhr).done(function (configResult, loginResult, aboutResult) { var configResponse = configResult[0]; var loginResponse = loginResult[0]; + var aboutResponse = aboutResult[0]; // calculate the canvas offset var canvasContainer = $('#canvas-container'); @@ -1057,10 +1061,15 @@ nf.Canvas = (function () { // get the config details var configDetails = configResponse.config; var loginDetails = loginResponse.config; - + var aboutDetails = aboutResponse.about; + + // set the document title and the about title + document.title = aboutDetails.title; + $('#nf-version').text(aboutDetails.version); + // store the content viewer url if available - if (!nf.Common.isBlank(configDetails.contentViewerUrl)) { - $('#nifi-content-viewer-url').text(configDetails.contentViewerUrl); + if (!nf.Common.isBlank(aboutDetails.contentViewerUrl)) { + $('#nifi-content-viewer-url').text(aboutDetails.contentViewerUrl); } // when both request complete, load the application @@ -1071,64 +1080,62 @@ nf.Canvas = (function () { // initialize whether site to site is secure secureSiteToSite = configDetails.siteToSiteSecure; - // load d3 - loadD3().done(function () { - nf.Storage.init(); + // init storage + nf.Storage.init(); - // initialize the application - initCanvas(); - nf.Canvas.View.init(); - nf.ContextMenu.init(); - nf.CanvasToolbar.init(); - nf.CanvasToolbox.init(); - nf.CanvasHeader.init(loginDetails.supportsLogin); - nf.GraphControl.init(); - nf.Search.init(); - nf.Settings.init(); - nf.Actions.init(); - nf.QueueListing.init(); - nf.ComponentState.init(); + // initialize the application + initCanvas(); + nf.Canvas.View.init(); + nf.ContextMenu.init(); + nf.CanvasToolbar.init(); + nf.CanvasToolbox.init(); + nf.CanvasHeader.init(loginDetails.supportsLogin); + nf.GraphControl.init(); + nf.Search.init(); + nf.Settings.init(); + nf.Actions.init(); + nf.QueueListing.init(); + nf.ComponentState.init(); - // initialize the component behaviors - nf.Draggable.init(); - nf.Selectable.init(); - nf.Connectable.init(); + // initialize the component behaviors + nf.Draggable.init(); + nf.Selectable.init(); + nf.Connectable.init(); - // initialize the chart - nf.StatusHistory.init(configDetails.timeOffset); + // initialize the chart + nf.StatusHistory.init(configDetails.timeOffset); - // initialize the birdseye - nf.Birdseye.init(); + // initialize the birdseye + nf.Birdseye.init(); - // initialize components - nf.ConnectionConfiguration.init(); - nf.ControllerService.init(); - nf.ReportingTask.init(); - nf.ProcessorConfiguration.init(); - nf.ProcessGroupConfiguration.init(); - nf.RemoteProcessGroupConfiguration.init(); - nf.RemoteProcessGroupPorts.init(); - nf.PortConfiguration.init(); - nf.LabelConfiguration.init(); - nf.ProcessorDetails.init(); - nf.ProcessGroupDetails.init(); - nf.PortDetails.init(); - nf.ConnectionDetails.init(); - nf.RemoteProcessGroupDetails.init(); - nf.GoTo.init(); - nf.Graph.init().done(function () { - // determine the split between the polling - var pollingSplit = autoRefreshIntervalSeconds / 2; + // initialize components + nf.ConnectionConfiguration.init(); + nf.ControllerService.init(); + nf.ReportingTask.init(); + nf.ProcessorConfiguration.init(); + nf.ProcessGroupConfiguration.init(); + nf.RemoteProcessGroupConfiguration.init(); + nf.RemoteProcessGroupPorts.init(); + nf.PortConfiguration.init(); + nf.LabelConfiguration.init(); + nf.ProcessorDetails.init(); + nf.ProcessGroupDetails.init(); + nf.PortDetails.init(); + nf.ConnectionDetails.init(); + nf.RemoteProcessGroupDetails.init(); + nf.GoTo.init(); + nf.Graph.init().done(function () { + // determine the split between the polling + var pollingSplit = autoRefreshIntervalSeconds / 2; - // register the revision and status polling - startRevisionPolling(autoRefreshIntervalSeconds); - setTimeout(function () { - startStatusPolling(autoRefreshIntervalSeconds); - }, pollingSplit * 1000); + // register the revision and status polling + startRevisionPolling(autoRefreshIntervalSeconds); + setTimeout(function () { + startStatusPolling(autoRefreshIntervalSeconds); + }, pollingSplit * 1000); - // hide the splash screen - nf.Canvas.hideSplash(); - }).fail(nf.Common.handleAjaxError); + // hide the splash screen + nf.Canvas.hideSplash(); }).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js index 73ea71bbc0..be2cb6cf88 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js @@ -25,7 +25,7 @@ nf.ConnectionConfiguration = (function () { var config = { urls: { api: '../nifi-api', - prioritizers: '../nifi-api/controller/prioritizers' + prioritizers: '../nifi-api/flow/prioritizers' } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js index 2cf0fc0fa9..f5ed34ee30 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js @@ -548,7 +548,7 @@ nf.ControllerService = (function () { return $.ajax({ type: 'GET', - url: '../nifi-api/bulletin-board', + url: '../nifi-api/flow/bulletin-board', data: { sourceId: ids }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js index 89126626e9..5f9de0cac2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js @@ -380,7 +380,7 @@ nf.QueueListing = (function () { // issue the request to list the flow files $.ajax({ type: 'POST', - url: connection.component.uri + '/listing-requests', + url: '../nifi-api/flowfile-queues/' + connection.component.id + '/listing-requests', dataType: 'json', contentType: 'application/json' }).done(function(response) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-search.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-search.js index 2f7529e627..eef5b5d6fa 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-search.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-search.js @@ -19,7 +19,7 @@ nf.Search = (function () { var config = { search: 'Search', urls: { - search: '../nifi-api/controller/search-results' + search: '../nifi-api/flow/search-results' } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js index a4e627eae3..0670e8ff6c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js @@ -27,12 +27,12 @@ nf.Settings = (function () { filterList: 'filter-list' }, urls: { + api: '../nifi-api', controllerConfig: '../nifi-api/controller/config', controllerArchive: '../nifi-api/controller/archive', - controllerServiceTypes: '../nifi-api/controller/controller-service-types', - controllerServices: '../nifi-api/controller-services', - reportingTaskTypes: '../nifi-api/controller/reporting-task-types', - reportingTasks: '../nifi-api/reporting-tasks' + controllerServiceTypes: '../nifi-api/flow/controller-service-types', + reportingTaskTypes: '../nifi-api/flow/reporting-task-types', + reportingTasks: '../nifi-api/controller/reporting-tasks' } }; @@ -80,19 +80,20 @@ nf.Settings = (function () { // register the click listener for the save button $('#settings-save').click(function () { - var revision = nf.Client.getRevision(); - // marshal the configuration details var configuration = marshalConfiguration(); - configuration['version'] = revision.version; - configuration['clientId'] = revision.clientId; + var entity = { + 'revision': nf.Client.getRevision(), + 'config': configuration + }; // save the new configuration details $.ajax({ type: 'PUT', url: config.urls.controllerConfig, - data: configuration, - dataType: 'json' + data: JSON.stringify(entity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); @@ -316,7 +317,7 @@ nf.Settings = (function () { // add the new controller service var addService = $.ajax({ type: 'POST', - url: config.urls.controllerServices + '/' + encodeURIComponent(availability), + url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId) + '/controller-services/' + encodeURIComponent(availability), data: JSON.stringify(controllerServiceEntity), dataType: 'json', contentType: 'application/json' @@ -863,7 +864,7 @@ nf.Settings = (function () { // get the controller services that are running on the nodes var nodeControllerServices = $.ajax({ type: 'GET', - url: config.urls.controllerServices + '/' + encodeURIComponent(config.node), + url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId) + '/controller-services/' + encodeURIComponent(config.node), dataType: 'json' }).done(function (response) { var nodeServices = response.controllerServices; @@ -881,7 +882,7 @@ nf.Settings = (function () { if (nf.Canvas.isClustered()) { $.ajax({ type: 'GET', - url: config.urls.controllerServices + '/' + encodeURIComponent(config.ncm), + url: config.urls.api + '/process-groups/' + encodeURIComponent(nf.Canvas.getGroupId) + '/controller-services/' + encodeURIComponent(config.ncm), dataType: 'json' }).done(function (response) { var ncmServices = response.controllerServices; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js index 750b36b09a..ecea569c15 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js @@ -21,7 +21,6 @@ nf.Snippet = (function () { var config = { urls: { - snippets: '../nifi-api/controller/snippets', processGroups: '../nifi-api/process-groups' } }; @@ -37,14 +36,14 @@ nf.Snippet = (function () { var snippet = { parentGroupId: nf.Canvas.getGroupId(), linked: nf.Common.isDefinedAndNotNull(linked) ? linked : false, - processorIds: [], - funnelIds: [], - inputPortIds: [], - outputPortIds: [], - remoteProcessGroupIds: [], - processGroupIds: [], - connectionIds: [], - labelIds: [] + processors: [], + funnels: [], + inputPorts: [], + outputPorts: [], + remoteProcessGroups: [], + processGroups: [], + connections: [], + labels: [] }; // go through each component and identify its type @@ -52,21 +51,21 @@ nf.Snippet = (function () { var selected = d3.select(this); if (nf.CanvasUtils.isProcessor(selected)) { - snippet.processorIds.push(d.component.id); + snippet.processors.push(d.component.id); } else if (nf.CanvasUtils.isFunnel(selected)) { - snippet.funnelIds.push(d.component.id); + snippet.funnels.push(d.component.id); } else if (nf.CanvasUtils.isLabel(selected)) { - snippet.labelIds.push(d.component.id); + snippet.labels.push(d.component.id); } else if (nf.CanvasUtils.isInputPort(selected)) { - snippet.inputPortIds.push(d.component.id); + snippet.inputPorts.push(d.component.id); } else if (nf.CanvasUtils.isOutputPort(selected)) { - snippet.outputPortIds.push(d.component.id); + snippet.outputPorts.push(d.component.id); } else if (nf.CanvasUtils.isProcessGroup(selected)) { - snippet.processGroupIds.push(d.component.id); + snippet.processGroups.push(d.component.id); } else if (nf.CanvasUtils.isRemoteProcessGroup(selected)) { - snippet.remoteProcessGroupIds.push(d.component.id); + snippet.remoteProcessGroups.push(d.component.id); } else if (nf.CanvasUtils.isConnection(selected)) { - snippet.connectionIds.push(d.component.id); + snippet.connections.push(d.component.id); } }); @@ -79,16 +78,17 @@ nf.Snippet = (function () { * @argument {object} snippet The snippet */ create: function (snippet) { - var revision = nf.Client.getRevision(); + var snippetEntity = { + 'revision': nf.Client.getRevision(), + 'snippet': snippet + }; return $.ajax({ type: 'POST', - url: config.urls.snippets, - data: $.extend({ - version: revision.version, - clientId: revision.clientId - }, snippet), - dataType: 'json' + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippets', + data: JSON.stringify(snippetEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); @@ -99,23 +99,22 @@ nf.Snippet = (function () { * Copies the snippet to the specified group and origin. * * @argument {string} snippetId The snippet id - * @argument {string} groupId The group id * @argument {object} origin The origin */ - copy: function (snippetId, groupId, origin) { - var revision = nf.Client.getRevision(); + copy: function (snippetId, origin) { + var copySnippetRequestEntity = { + 'revision': nf.Client.getRevision(), + 'snippetId': snippetId, + 'originX': origin.x, + 'originY': origin.y + }; return $.ajax({ type: 'POST', - url: config.urls.processGroups + '/' + encodeURIComponent(groupId) + '/snippet-instance', - data: { - version: revision.version, - clientId: revision.clientId, - snippetId: snippetId, - originX: origin.x, - originY: origin.y - }, - dataType: 'json' + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippet-instance', + data: JSON.stringify(copySnippetRequestEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); @@ -132,7 +131,7 @@ nf.Snippet = (function () { return $.ajax({ type: 'DELETE', - url: config.urls.snippets + '/' + encodeURIComponent(snippetId) + '?' + $.param({ + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippets/' + encodeURIComponent(snippetId) + '?' + $.param({ version: revision.version, clientId: revision.clientId }) @@ -149,17 +148,20 @@ nf.Snippet = (function () { * @argument {string} newGroupId The new group id */ move: function (snippetId, newGroupId) { - var revision = nf.Client.getRevision(); + var snippetEntity = { + 'revision': nf.Client.getRevision(), + 'snippet': { + 'id': snippetId, + 'parentGroupId': newGroupId + } + }; return $.ajax({ type: 'PUT', - url: config.urls.snippets + '/' + encodeURIComponent(snippetId), - data: { - version: revision.version, - clientId: revision.clientId, - parentGroupId: newGroupId - }, - dataType: 'json' + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippets/' + encodeURIComponent(snippetId), + data: JSON.stringify(snippetEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); @@ -172,17 +174,20 @@ nf.Snippet = (function () { * @argument {string} snippetId The snippet id */ unlink: function (snippetId) { - var revision = nf.Client.getRevision(); + var snippetEntity = { + 'revision': nf.Client.getRevision(), + 'snippet': { + 'id': snippetId, + 'linked': false + } + }; return $.ajax({ type: 'PUT', - url: config.urls.snippets + '/' + encodeURIComponent(snippetId), - data: { - version: revision.version, - clientId: revision.clientId, - linked: false - }, - dataType: 'json' + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippets/' + encodeURIComponent(snippetId), + data: JSON.stringify(snippetEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); @@ -195,17 +200,20 @@ nf.Snippet = (function () { * @argument {string} snippetId The snippet id */ link: function (snippetId) { - var revision = nf.Client.getRevision(); + var snippetEntity = { + 'revision': nf.Client.getRevision(), + 'snippet': { + 'id': snippetId, + 'linked': true + } + }; return $.ajax({ type: 'PUT', - url: config.urls.snippets + '/' + encodeURIComponent(snippetId), - data: { - version: revision.version, - clientId: revision.clientId, - linked: true - }, - dataType: 'json' + url: config.urls.processGroups + '/' + encodeURIComponent(nf.Canvas.getGroupId()) + '/snippets/' + encodeURIComponent(snippetId), + data: JSON.stringify(snippetEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { // update the revision nf.Client.setRevision(response.revision); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js index 1eafa67c95..da90946d9f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js @@ -29,8 +29,8 @@ nf.Cluster = (function () { */ var config = { urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', authorities: '../nifi-api/controller/authorities' } }; @@ -140,7 +140,7 @@ nf.Cluster = (function () { // get the about details $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js index 6af3d20eb3..e6e369086a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js @@ -29,8 +29,8 @@ nf.Counters = (function () { */ var config = { urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', authorities: '../nifi-api/controller/authorities' } }; @@ -140,7 +140,7 @@ nf.Counters = (function () { // get the about details $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js index 8f749c0102..eac7c0462f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js @@ -29,8 +29,8 @@ nf.History = (function () { */ var config = { urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', authorities: '../nifi-api/controller/authorities' } }; @@ -142,7 +142,7 @@ nf.History = (function () { // get the about details $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js index 697794ca97..d9821d6ab7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js @@ -27,7 +27,7 @@ nf.Login = (function () { var config = { urls: { - identity: '../nifi-api/controller/identity', + identity: '../nifi-api/flow/identity', users: '../nifi-api/controller/users', token: '../nifi-api/access/token', accessStatus: '../nifi-api/access', diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js index 7bf401e330..879fb56ffe 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js @@ -123,19 +123,6 @@ nf.Common = (function () { nf.Common.authorities = roles; }, - /** - * Loads a script at the specified URL. Supports caching the script on the browser. - * - * @param {string} url - */ - cachedScript: function (url) { - return $.ajax({ - dataType: 'script', - cache: true, - url: url - }); - }, - /** * Automatically refresh tokens by checking once an hour if its going to expire soon. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js index 8396ee0627..69dd4b0ec9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js @@ -33,8 +33,7 @@ nf.StatusHistory = (function () { label: 'Label' }, urls: { - api: '../nifi-api', - processGroups: '../nifi-api/process-groups/' + api: '../nifi-api' } }; @@ -1100,7 +1099,7 @@ nf.StatusHistory = (function () { showConnectionChart: function (groupId, connectionId, selectedDescriptor) { $.ajax({ type: 'GET', - url: config.urls.api + '/connections/' + encodeURIComponent(connectionId) + '/status/history', + url: config.urls.api + '/flow/connections/' + encodeURIComponent(connectionId) + '/status/history', dataType: 'json' }).done(function (response) { handleStatusHistoryResponse(groupId, connectionId, response.statusHistory, config.type.connection, selectedDescriptor); @@ -1117,7 +1116,7 @@ nf.StatusHistory = (function () { showProcessorChart: function (groupId, processorId, selectedDescriptor) { $.ajax({ type: 'GET', - url: config.urls.api + '/processors/' + encodeURIComponent(processorId) + '/status/history', + url: config.urls.api + '/flow/processors/' + encodeURIComponent(processorId) + '/status/history', dataType: 'json' }).done(function (response) { handleStatusHistoryResponse(groupId, processorId, response.statusHistory, config.type.processor, selectedDescriptor); @@ -1134,7 +1133,7 @@ nf.StatusHistory = (function () { showProcessGroupChart: function (groupId, processGroupId, selectedDescriptor) { $.ajax({ type: 'GET', - url: config.urls.processGroups + encodeURIComponent(processGroupId) + '/status/history', + url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupId) + '/status/history', dataType: 'json' }).done(function (response) { handleStatusHistoryResponse(groupId, processGroupId, response.statusHistory, config.type.processGroup, selectedDescriptor); @@ -1151,7 +1150,7 @@ nf.StatusHistory = (function () { showRemoteProcessGroupChart: function (groupId, remoteProcessGroupId, selectedDescriptor) { $.ajax({ type: 'GET', - url: config.urls.api + '/remote-process-groups/' + encodeURIComponent(remoteProcessGroupId) + '/status/history', + url: config.urls.api + '/flow/remote-process-groups/' + encodeURIComponent(remoteProcessGroupId) + '/status/history', dataType: 'json' }).done(function (response) { handleStatusHistoryResponse(groupId, remoteProcessGroupId, response.statusHistory, config.type.remoteProcessGroup, selectedDescriptor); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js index eb7c6de001..a8d04bacc8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js @@ -25,8 +25,8 @@ nf.ProvenanceLineage = (function () { var config = { sliderTickCount: 75, urls: { - lineage: '../nifi-api/controller/provenance/lineage', - events: '../nifi-api/controller/provenance/events/' + lineage: '../nifi-api/provenance/lineage', + events: '../nifi-api/provenance/events/' } }; @@ -123,11 +123,18 @@ nf.ProvenanceLineage = (function () { * @returns {deferred} */ var submitLineage = function (lineageRequest) { + var lineageEntity = { + 'lineage': { + 'request': lineageRequest + } + }; + return $.ajax({ type: 'POST', url: config.urls.lineage, - data: lineageRequest, - dataType: 'json' + data: JSON.stringify(lineageEntity), + dataType: 'json', + contentType: 'application/json' }).fail(nf.Common.handleAjaxError); }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js index a977e08764..cf034b961e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js @@ -32,9 +32,9 @@ nf.ProvenanceTable = (function () { hidden: 'hidden' }, urls: { - searchOptions: '../nifi-api/controller/provenance/search-options', - replays: '../nifi-api/controller/provenance/replays', - provenance: '../nifi-api/controller/provenance', + searchOptions: '../nifi-api/provenance/search-options', + replays: '../nifi-api/provenance/replays', + provenance: '../nifi-api/provenance', cluster: '../nifi-api/cluster', d3Script: 'js/d3/d3.min.js', lineageScript: 'js/nf/provenance/nf-provenance-lineage.js', @@ -48,29 +48,6 @@ nf.ProvenanceTable = (function () { */ var cachedQuery = {}; - /** - * Loads the lineage capabilities when the current browser supports SVG. - */ - var loadLineageCapabilities = function () { - return $.Deferred(function (deferred) { - if (nf.Common.SUPPORTS_SVG) { - nf.Common.cachedScript(config.urls.d3Script).done(function () { - nf.Common.cachedScript(config.urls.lineageScript).done(function () { - // initialize the lineage graph - nf.ProvenanceLineage.init(); - deferred.resolve(); - }).fail(function () { - deferred.reject(); - }); - }).fail(function () { - deferred.reject(); - }); - } else { - deferred.resolve(); - } - }).promise(); - }; - /** * Downloads the content for the provenance event that is currently loaded in the specified direction. * @@ -121,7 +98,7 @@ nf.ProvenanceTable = (function () { var eventId = $('#provenance-event-id').text(); // build the uri to the data - var dataUri = controllerUri + '/provenance/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); + var dataUri = controllerUri + 'provenance/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction); // generate tokens as necessary var getAccessTokens = $.Deferred(function (deferred) { @@ -280,21 +257,22 @@ nf.ProvenanceTable = (function () { if (nf.Common.isDFM()) { // replay $('#replay-content').on('click', function () { - var parameters = { - eventId: $('#provenance-event-id').text() + var replayEntity = { + 'eventId': $('#provenance-event-id').text() }; // conditionally include the cluster node id var clusterNodeId = $('#provenance-event-cluster-node-id').text(); if (!nf.Common.isBlank(clusterNodeId)) { - parameters['clusterNodeId'] = clusterNodeId; + replayEntity['clusterNodeId'] = clusterNodeId; } $.ajax({ type: 'POST', url: config.urls.replays, - data: parameters, - dataType: 'json' + data: JSON.stringify(replayEntity), + dataType: 'json', + contentType: 'application/json' }).done(function (response) { nf.Dialog.showOkDialog({ dialogContent: 'Successfully submitted replay request.', @@ -432,7 +410,7 @@ nf.ProvenanceTable = (function () { } // add the search criteria - search = $.extend(getSearchCriteria(), search); + search['searchTerms'] = getSearchCriteria(); // reload the table nf.ProvenanceTable.loadProvenanceTable(search); @@ -522,7 +500,7 @@ nf.ProvenanceTable = (function () { // if the field isn't blank include it in the search if (!nf.Common.isBlank(searchValue)) { - searchCriteria['search[' + fieldId + ']'] = searchValue; + searchCriteria[fieldId] = searchValue; } }); return searchCriteria; @@ -861,13 +839,21 @@ nf.ProvenanceTable = (function () { * @returns {deferred} */ var submitProvenance = function (provenance) { + var provenanceEntity = { + 'provenance': { + 'request': $.extend({ + maxResults: config.maxResults + }, provenance) + } + }; + + // submit the provenance request return $.ajax({ type: 'POST', url: config.urls.provenance, - data: $.extend({ - maxResults: config.maxResults - }, provenance), - dataType: 'json' + data: JSON.stringify(provenanceEntity), + dataType: 'json', + contentType: 'application/json' }).fail(nf.Common.handleAjaxError); }; @@ -1020,15 +1006,16 @@ nf.ProvenanceTable = (function () { deferred.reject(); nf.Common.handleAjaxError(xhr, status, error); }; - - // load the lineage capabilities - loadLineageCapabilities().done(function () { - initDetailsDialog(); - initProvenanceQueryDialog(); - initProvenanceTable(isClustered); - initSearchDialog(isClustered).done(function () { - deferred.resolve(); - }).fail(failure); + + // initialize the lineage view + nf.ProvenanceLineage.init(); + + // initialize the table view + initDetailsDialog(); + initProvenanceQueryDialog(); + initProvenanceTable(isClustered); + initSearchDialog(isClustered).done(function () { + deferred.resolve(); }).fail(failure); }).promise(); }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js index 1756c54c41..667d8a3c96 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js @@ -30,9 +30,8 @@ nf.Provenance = (function () { var config = { urls: { cluster: '../nifi-api/cluster', - banners: '../nifi-api/controller/banners', - config: '../nifi-api/controller/config', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', authorities: '../nifi-api/controller/authorities' } }; @@ -68,21 +67,27 @@ nf.Provenance = (function () { /** * Loads the controller configuration. */ - var loadControllerConfig = function () { - return $.ajax({ + var loadAbout = function () { + // get the about details + $.ajax({ type: 'GET', - url: config.urls.config, + url: config.urls.about, dataType: 'json' }).done(function (response) { - var config = response.config; + var aboutDetails = response.about; + var provenanceTitle = aboutDetails.title + ' Data Provenance'; // store the controller name - $('#nifi-controller-uri').text(config.uri); + $('#nifi-controller-uri').text(aboutDetails.uri); // store the content viewer url if available - if (!nf.Common.isBlank(config.contentViewerUrl)) { - $('#nifi-content-viewer-url').text(config.contentViewerUrl); + if (!nf.Common.isBlank(aboutDetails.contentViewerUrl)) { + $('#nifi-content-viewer-url').text(aboutDetails.contentViewerUrl); } + + // set the document title and the about title + document.title = provenanceTitle; + $('#provenance-header-text').text(provenanceTitle); }).fail(nf.Common.handleAjaxError); }; @@ -177,10 +182,10 @@ nf.Provenance = (function () { nf.Storage.init(); // load the users authorities and detect if the NiFi is clustered - $.when(loadControllerConfig(), loadAuthorities(), detectedCluster()).done(function () { + $.when(loadAbout(), loadAuthorities(), detectedCluster()).done(function () { // create the provenance table nf.ProvenanceTable.init(isClustered).done(function () { - var search = {}; + var searchTerms = {}; // look for a processor id in the query search var initialComponentId = $('#intial-component-query').text(); @@ -189,9 +194,7 @@ nf.Provenance = (function () { $('input.searchable-component-id').val(initialComponentId); // build the search criteria - search = $.extend(search, { - 'search[ProcessorID]': initialComponentId - }); + searchTerms['ProcessorID'] = initialComponentId; } // look for a flowfile uuid in the query search @@ -201,32 +204,24 @@ nf.Provenance = (function () { $('input.searchable-flowfile-uuid').val(initialFlowFileUuid); // build the search criteria - search = $.extend(search, { - 'search[FlowFileUUID]': initialFlowFileUuid + searchTerms['FlowFileUUID'] = initialFlowFileUuid; + } + + // load the provenance table + if ($.isEmptyObject(searchTerms)) { + // load the provenance table + nf.ProvenanceTable.loadProvenanceTable(); + } else { + // load the provenance table + nf.ProvenanceTable.loadProvenanceTable({ + 'searchTerms': searchTerms }); } - // load the provenance table - nf.ProvenanceTable.loadProvenanceTable(search); - // once the table is initialized, finish initializing the page initializeProvenancePage().done(function () { // configure the initial grid height nf.ProvenanceTable.resetTableSize(); - - // get the about details - $.ajax({ - type: 'GET', - url: config.urls.controllerAbout, - dataType: 'json' - }).done(function (response) { - var aboutDetails = response.about; - var provenanceTitle = aboutDetails.title + ' Data Provenance'; - - // set the document title and the about title - document.title = provenanceTitle; - $('#provenance-header-text').text(provenanceTitle); - }).fail(nf.Common.handleAjaxError); }); }); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js index 0c9fe36098..d355f19463 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-cluster-search.js @@ -21,9 +21,7 @@ nf.ClusterSearch = (function () { var config = { search: 'Search nodes', urls: { - clusterSearch: '../nifi-api/cluster/search-results', - status: '../nifi-api/process-groups/root/status', - systemDiagnostics: '../nifi-api/system-diagnostics' + clusterSearch: '../nifi-api/cluster/search-results' } }; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js index fcd59aae3a..8c1a32de25 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js @@ -28,49 +28,12 @@ nf.SummaryTable = (function () { }, urls: { api: '../nifi-api', - status: '../nifi-api/process-groups/root/status', - processGroups: '../nifi-api/process-groups/', + status: '../nifi-api/flow/process-groups/root/status', systemDiagnostics: '../nifi-api/system-diagnostics', - controllerConfig: '../nifi-api/controller/config', - d3Script: 'js/d3/d3.min.js', - statusHistory: 'js/nf/nf-status-history.js' + controllerConfig: '../nifi-api/controller/config' } }; - /** - * Loads the lineage capabilities when the current browser supports SVG. - */ - var loadChartCapabilities = function () { - return $.Deferred(function (deferred) { - if (nf.Common.SUPPORTS_SVG) { - nf.Common.cachedScript(config.urls.d3Script).done(function () { - // get the controller config to get the server offset - var configRequest = $.ajax({ - type: 'GET', - url: config.urls.controllerConfig, - dataType: 'json' - }); - - // get the config details and load the chart script - $.when(configRequest, nf.Common.cachedScript(config.urls.statusHistory)).done(function (response) { - var configResponse = response[0]; - var configDetails = configResponse.config; - - // initialize the chart - nf.StatusHistory.init(configDetails.timeOffset); - deferred.resolve(); - }).fail(function () { - deferred.reject(); - }); - }).fail(function () { - deferred.reject(); - }); - } else { - deferred.resolve(); - } - }).promise(); - }; - /** * Goes to the specified component if possible. * @@ -2162,7 +2125,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.api + '/processors/' + encodeURIComponent(processorId) + '/status', + url: config.urls.api + '/flow/processors/' + encodeURIComponent(processorId) + '/status', data: { nodewise: true }, @@ -2219,7 +2182,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.api + '/connections/' + encodeURIComponent(connectionId) + '/status', + url: config.urls.api + '/flow/connections/' + encodeURIComponent(connectionId) + '/status', data: { nodewise: true }, @@ -2272,7 +2235,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.processGroups + encodeURIComponent(processGroupId) + '/status', + url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupId) + '/status', data: { nodewise: true, recursive: false @@ -2332,7 +2295,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.api + '/input-ports/' + encodeURIComponent(inputPortId) + '/status', + url: config.urls.api + '/flow/input-ports/' + encodeURIComponent(inputPortId) + '/status', data: { nodewise: true }, @@ -2384,7 +2347,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.api + '/output-ports/' + encodeURIComponent(outputPortId) + '/status', + url: config.urls.api + '/flow/output-ports/' + encodeURIComponent(outputPortId) + '/status', data: { nodewise: true }, @@ -2436,7 +2399,7 @@ nf.SummaryTable = (function () { // get the summary $.ajax({ type: 'GET', - url: config.urls.api + '/remote-process-groups/' + encodeURIComponent(remoteProcessGroupId) + '/status', + url: config.urls.api + '/flow/remote-process-groups/' + encodeURIComponent(remoteProcessGroupId) + '/status', data: { nodewise: true }, @@ -2492,7 +2455,17 @@ nf.SummaryTable = (function () { */ init: function (isClustered) { return $.Deferred(function (deferred) { - loadChartCapabilities().done(function () { + // get the controller config to get the server offset + var configRequest = $.ajax({ + type: 'GET', + url: config.urls.controllerConfig, + dataType: 'json' + }).done(function (configResponse) { + var configDetails = configResponse.config; + + // initialize the chart + nf.StatusHistory.init(configDetails.timeOffset); + // initialize the processor/connection details dialog nf.ProcessorDetails.init(false); nf.ConnectionDetails.init(false); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js index c16a6700c8..412336640f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js @@ -29,8 +29,8 @@ nf.Summary = (function () { */ var config = { urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', cluster: '../nifi-api/cluster' } }; @@ -142,7 +142,7 @@ nf.Summary = (function () { // get the about details $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js index 0f9ea8a244..f12f976763 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js @@ -28,11 +28,16 @@ nf.TemplatesTable = (function () { filterList: 'templates-filter-list' }, urls: { - templates: '../nifi-api/controller/templates', + api: '../nifi-api', downloadToken: '../nifi-api/access/download-token' } }; + /** + * the current group id + */ + var groupId; + /** * Sorts the specified data using the specified sort details. * @@ -68,7 +73,7 @@ nf.TemplatesTable = (function () { dialogContent: 'Delete template \'' + nf.Common.escapeHtml(template.name) + '\'?', overlayBackground: false, yesHandler: function () { - deleteTemplate(template.id); + deleteTemplate(template); } }); }; @@ -76,17 +81,17 @@ nf.TemplatesTable = (function () { /** * Deletes the template with the specified id. * - * @argument {string} templateId The template id + * @argument {string} template The template */ - var deleteTemplate = function (templateId) { + var deleteTemplate = function (template) { $.ajax({ type: 'DELETE', - url: config.urls.templates + '/' + encodeURIComponent(templateId), + url: template.uri, dataType: 'json' }).done(function () { var templatesGrid = $('#templates-table').data('gridInstance'); var templatesData = templatesGrid.getData(); - templatesData.deleteItem(templateId); + templatesData.deleteItem(template.id); // update the total number of templates $('#total-templates').text(templatesData.getItems().length); @@ -167,9 +172,9 @@ nf.TemplatesTable = (function () { // open the url if ($.isEmptyObject(parameters)) { - window.open(config.urls.templates + '/' + encodeURIComponent(template.id)); + window.open(template.uri + '/download'); } else { - window.open(config.urls.templates + '/' + encodeURIComponent(template.id) + '?' + $.param(parameters)); + window.open(template.uri + '/download' + '?' + $.param(parameters)); } }).fail(function () { nf.Dialog.showOkDialog({ @@ -332,9 +337,17 @@ nf.TemplatesTable = (function () { * Load the processor templates table. */ loadTemplatesTable: function () { + groupId = $('#template-group-id').text(); + if (nf.Common.isUndefined(groupId) || nf.Common.isNull(groupId)) { + nf.Dialog.showOkDialog({ + overlayBackground: false, + content: 'Group id not specified.' + }); + } + return $.ajax({ type: 'GET', - url: config.urls.templates, + url: config.urls.api + '/process-groups/' + encodeURIComponent(groupId) + '/templates', data: { verbose: false }, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js index 678e352c88..ecde656536 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js @@ -29,12 +29,17 @@ nf.Templates = (function () { */ var config = { urls: { - banners: '../nifi-api/controller/banners', - controllerAbout: '../nifi-api/controller/about', + banners: '../nifi-api/flow/banners', + about: '../nifi-api/flow/about', authorities: '../nifi-api/controller/authorities' } }; + /** + * the current group id + */ + var groupId; + /** * Loads the current users authorities. */ @@ -102,6 +107,7 @@ nf.Templates = (function () { // initialize the form var templateForm = $('#template-upload-form').ajaxForm({ + url: '../nifi-api/process-groups/' + encodeURIComponent(groupId) + '/templates/upload', dataType: 'xml', success: function (response, statusText, xhr, form) { // see if the import was successful @@ -205,6 +211,15 @@ nf.Templates = (function () { */ init: function () { nf.Storage.init(); + + // ensure the group id is specified + groupId = $('#template-group-id').text(); + if (nf.Common.isUndefined(groupId) || nf.Common.isNull(groupId)) { + nf.Dialog.showOkDialog({ + overlayBackground: false, + content: 'Group id not specified.' + }); + } // load the users authorities loadAuthorities().done(function () { @@ -222,7 +237,7 @@ nf.Templates = (function () { // get the about details $.ajax({ type: 'GET', - url: config.urls.controllerAbout, + url: config.urls.about, dataType: 'json' }).done(function (response) { var aboutDetails = response.about;