diff --git a/src/main/java/org/opensearch/flowframework/FlowFrameworkPlugin.java b/src/main/java/org/opensearch/flowframework/FlowFrameworkPlugin.java index eadadc405..0fe7b724d 100644 --- a/src/main/java/org/opensearch/flowframework/FlowFrameworkPlugin.java +++ b/src/main/java/org/opensearch/flowframework/FlowFrameworkPlugin.java @@ -26,8 +26,33 @@ import org.opensearch.env.NodeEnvironment; import org.opensearch.flowframework.common.FlowFrameworkSettings; import org.opensearch.flowframework.indices.FlowFrameworkIndicesHandler; -import org.opensearch.flowframework.rest.*; -import org.opensearch.flowframework.transport.*; +import org.opensearch.flowframework.rest.RestCreateWorkflowAction; +import org.opensearch.flowframework.rest.RestDeleteWorkflowAction; +import org.opensearch.flowframework.rest.RestDeprovisionWorkflowAction; +import org.opensearch.flowframework.rest.RestGetWorkflowAction; +import org.opensearch.flowframework.rest.RestGetWorkflowStateAction; +import org.opensearch.flowframework.rest.RestGetWorkflowStepAction; +import org.opensearch.flowframework.rest.RestProvisionWorkflowAction; +import org.opensearch.flowframework.rest.RestSearchWorkflowAction; +import org.opensearch.flowframework.rest.RestSearchWorkflowStateAction; +import org.opensearch.flowframework.transport.CreateWorkflowAction; +import org.opensearch.flowframework.transport.CreateWorkflowTransportAction; +import org.opensearch.flowframework.transport.DeleteWorkflowAction; +import org.opensearch.flowframework.transport.DeleteWorkflowTransportAction; +import org.opensearch.flowframework.transport.DeprovisionWorkflowAction; +import org.opensearch.flowframework.transport.DeprovisionWorkflowTransportAction; +import org.opensearch.flowframework.transport.GetWorkflowAction; +import org.opensearch.flowframework.transport.GetWorkflowStateAction; +import org.opensearch.flowframework.transport.GetWorkflowStateTransportAction; +import org.opensearch.flowframework.transport.GetWorkflowStepAction; +import org.opensearch.flowframework.transport.GetWorkflowStepTransportAction; +import org.opensearch.flowframework.transport.GetWorkflowTransportAction; +import org.opensearch.flowframework.transport.ProvisionWorkflowAction; +import org.opensearch.flowframework.transport.ProvisionWorkflowTransportAction; +import org.opensearch.flowframework.transport.SearchWorkflowAction; +import org.opensearch.flowframework.transport.SearchWorkflowStateAction; +import org.opensearch.flowframework.transport.SearchWorkflowStateTransportAction; +import org.opensearch.flowframework.transport.SearchWorkflowTransportAction; import org.opensearch.flowframework.util.EncryptorUtils; import org.opensearch.flowframework.workflow.WorkflowProcessSorter; import org.opensearch.flowframework.workflow.WorkflowStepFactory; diff --git a/src/main/java/org/opensearch/flowframework/model/WorkflowStepValidator.java b/src/main/java/org/opensearch/flowframework/model/WorkflowStepValidator.java index c69ae329e..4673020dd 100644 --- a/src/main/java/org/opensearch/flowframework/model/WorkflowStepValidator.java +++ b/src/main/java/org/opensearch/flowframework/model/WorkflowStepValidator.java @@ -147,24 +147,26 @@ public TimeValue getTimeout() { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { XContentBuilder xContentBuilder = builder.startObject(); xContentBuilder.startArray(INPUTS_FIELD); - for (String input: this.inputs) { + for (String input : this.inputs) { xContentBuilder.value(input); } xContentBuilder.endArray(); xContentBuilder.startArray(OUTPUTS_FIELD); - for (String output: this.outputs) { + for (String output : this.outputs) { xContentBuilder.value(output); } xContentBuilder.endArray(); xContentBuilder.startArray(REQUIRED_PLUGINS); - for (String rp: this.requiredPlugins) { + for (String rp : this.requiredPlugins) { xContentBuilder.value(rp); } xContentBuilder.endArray(); - xContentBuilder.field(TIMEOUT, timeout); + if (timeout != null) { + xContentBuilder.field(TIMEOUT, timeout); + } return xContentBuilder.endObject(); } diff --git a/src/main/java/org/opensearch/flowframework/rest/RestGetWorkflowStepAction.java b/src/main/java/org/opensearch/flowframework/rest/RestGetWorkflowStepAction.java index 6b04e8e36..04f354371 100644 --- a/src/main/java/org/opensearch/flowframework/rest/RestGetWorkflowStepAction.java +++ b/src/main/java/org/opensearch/flowframework/rest/RestGetWorkflowStepAction.java @@ -1,5 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ package org.opensearch.flowframework.rest; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.ExceptionsHelper; import org.opensearch.client.node.NodeClient; import org.opensearch.core.action.ActionListener; @@ -8,12 +18,9 @@ import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.flowframework.common.FlowFrameworkSettings; import org.opensearch.flowframework.exception.FlowFrameworkException; -import org.opensearch.flowframework.transport.GetWorkflowAction; import org.opensearch.flowframework.transport.GetWorkflowStepAction; import org.opensearch.flowframework.transport.WorkflowRequest; import org.opensearch.rest.BaseRestHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestRequest; @@ -24,22 +31,23 @@ import static org.opensearch.flowframework.common.CommonValue.WORKFLOW_URI; import static org.opensearch.flowframework.common.FlowFrameworkSettings.FLOW_FRAMEWORK_ENABLED; +/** + * Rest Action to facilitate requests to get the workflow steps + */ public class RestGetWorkflowStepAction extends BaseRestHandler { private static final String GET_WORKFLOW_STEP_ACTION = "get_workflow_step"; private static final Logger logger = LogManager.getLogger(RestGetWorkflowStepAction.class); private FlowFrameworkSettings flowFrameworkFeatureEnabledSetting; - /** - * Instantiates a new RestGetWorkflowAction + * Instantiates a new RestGetWorkflowStepAction * @param flowFrameworkFeatureEnabledSetting Whether this API is enabled */ public RestGetWorkflowStepAction(FlowFrameworkSettings flowFrameworkFeatureEnabledSetting) { this.flowFrameworkFeatureEnabledSetting = flowFrameworkFeatureEnabledSetting; } - @Override public String getName() { return GET_WORKFLOW_STEP_ACTION; @@ -55,8 +63,8 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient try { if (!flowFrameworkFeatureEnabledSetting.isFlowFrameworkEnabled()) { throw new FlowFrameworkException( - "This API is disabled. To enable it, update the setting [" + FLOW_FRAMEWORK_ENABLED.getKey() + "] to true.", - RestStatus.FORBIDDEN + "This API is disabled. To enable it, update the setting [" + FLOW_FRAMEWORK_ENABLED.getKey() + "] to true.", + RestStatus.FORBIDDEN ); } @@ -67,8 +75,8 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient }, exception -> { try { FlowFrameworkException ex = exception instanceof FlowFrameworkException - ? (FlowFrameworkException) exception - : new FlowFrameworkException(exception.getMessage(), ExceptionsHelper.status(exception)); + ? (FlowFrameworkException) exception + : new FlowFrameworkException(exception.getMessage(), ExceptionsHelper.status(exception)); XContentBuilder exceptionBuilder = ex.toXContent(channel.newErrorBuilder(), ToXContent.EMPTY_PARAMS); channel.sendResponse(new BytesRestResponse(ex.getRestStatus(), exceptionBuilder)); @@ -80,7 +88,7 @@ protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient } catch (FlowFrameworkException ex) { return channel -> channel.sendResponse( - new BytesRestResponse(ex.getRestStatus(), ex.toXContent(channel.newErrorBuilder(), ToXContent.EMPTY_PARAMS)) + new BytesRestResponse(ex.getRestStatus(), ex.toXContent(channel.newErrorBuilder(), ToXContent.EMPTY_PARAMS)) ); } } diff --git a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepAction.java b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepAction.java index 45e88031f..0c7e1ca6f 100644 --- a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepAction.java +++ b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepAction.java @@ -1,11 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ package org.opensearch.flowframework.transport; import org.opensearch.action.ActionType; -import org.opensearch.core.common.io.stream.Writeable; -import org.opensearch.flowframework.model.WorkflowStepValidator; import static org.opensearch.flowframework.common.CommonValue.TRANSPORT_ACTION_NAME_PREFIX; +/** + * External Action for public facing RestGetWorkflowStepAction + */ public class GetWorkflowStepAction extends ActionType { /** The name of this action */ @@ -13,6 +22,9 @@ public class GetWorkflowStepAction extends ActionType { /** An instance of this action */ public static final GetWorkflowStepAction INSTANCE = new GetWorkflowStepAction(); + /** + * Instantiates this class + */ public GetWorkflowStepAction() { super(NAME, GetWorkflowStepResponse::new); } diff --git a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepResponse.java b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepResponse.java index 815e2962d..0d9ce9371 100644 --- a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepResponse.java +++ b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepResponse.java @@ -1,3 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ package org.opensearch.flowframework.transport; import org.opensearch.core.action.ActionResponse; @@ -5,25 +13,35 @@ import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.flowframework.model.WorkflowStepValidator; import org.opensearch.flowframework.model.WorkflowValidator; import java.io.IOException; +/** + * Transport Response from getting workflow step + */ public class GetWorkflowStepResponse extends ActionResponse implements ToXContentObject { private WorkflowValidator workflowValidator; + /** + * Instantiates a new GetWorkflowStepResponse from an input stream + * @param in the input stream to read from + * @throws IOException if the workflow json cannot be read from the input stream + */ public GetWorkflowStepResponse(StreamInput in) throws IOException { super(in); this.workflowValidator = WorkflowValidator.parse(in.readString()); } + /** + * Instantiates a new GetWorkflowStepResponse + * @param workflowValidator the workflow validator + */ public GetWorkflowStepResponse(WorkflowValidator workflowValidator) { this.workflowValidator = workflowValidator; } - @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(workflowValidator.toJson()); diff --git a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportAction.java b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportAction.java index 66382f287..8720d1b1f 100644 --- a/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportAction.java +++ b/src/main/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportAction.java @@ -1,3 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ package org.opensearch.flowframework.transport; import org.apache.logging.log4j.LogManager; @@ -5,45 +13,33 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; -import org.opensearch.client.Client; import org.opensearch.common.inject.Inject; -import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.action.ActionListener; import org.opensearch.flowframework.exception.FlowFrameworkException; -import org.opensearch.flowframework.indices.FlowFrameworkIndicesHandler; import org.opensearch.flowframework.model.WorkflowValidator; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +/** + * Transport action to retrieve a the workflow step json + */ public class GetWorkflowStepTransportAction extends HandledTransportAction { - private final Logger logger = LogManager.getLogger(GetWorkflowStepTransportAction.class); - private final FlowFrameworkIndicesHandler flowFrameworkIndicesHandler; - private final Client client; /** * Instantiates a new GetWorkflowStepTransportAction instance * @param transportService the transport service * @param actionFilters action filters - * @param flowFrameworkIndicesHandler The Flow Framework indices handler - * @param client the OpenSearch Client */ @Inject - public GetWorkflowStepTransportAction( - TransportService transportService, - ActionFilters actionFilters, - FlowFrameworkIndicesHandler flowFrameworkIndicesHandler, - Client client - ) { + public GetWorkflowStepTransportAction(TransportService transportService, ActionFilters actionFilters) { super(GetWorkflowStepAction.NAME, transportService, actionFilters, WorkflowRequest::new); - this.flowFrameworkIndicesHandler = flowFrameworkIndicesHandler; - this.client = client; } @Override protected void doExecute(Task task, WorkflowRequest request, ActionListener listener) { - try (ThreadContext.StoredContext context = client.threadPool().getThreadContext().stashContext()) { + try { listener.onResponse(new GetWorkflowStepResponse(WorkflowValidator.parse("mappings/workflow-steps.json"))); } catch (Exception e) { logger.error("Failed to retrieve workflow step json.", e); diff --git a/src/test/java/org/opensearch/flowframework/FlowFrameworkPluginTests.java b/src/test/java/org/opensearch/flowframework/FlowFrameworkPluginTests.java index 6970beb14..51321618e 100644 --- a/src/test/java/org/opensearch/flowframework/FlowFrameworkPluginTests.java +++ b/src/test/java/org/opensearch/flowframework/FlowFrameworkPluginTests.java @@ -82,8 +82,8 @@ public void testPlugin() throws IOException { 5, ffp.createComponents(client, clusterService, threadPool, null, null, null, environment, null, null, null, null).size() ); - assertEquals(8, ffp.getRestHandlers(settings, null, null, null, null, null, null).size()); - assertEquals(8, ffp.getActions().size()); + assertEquals(9, ffp.getRestHandlers(settings, null, null, null, null, null, null).size()); + assertEquals(9, ffp.getActions().size()); assertEquals(1, ffp.getExecutorBuilders(settings).size()); assertEquals(5, ffp.getSettings().size()); } diff --git a/src/test/java/org/opensearch/flowframework/FlowFrameworkRestTestCase.java b/src/test/java/org/opensearch/flowframework/FlowFrameworkRestTestCase.java index b70065cfc..89acae978 100644 --- a/src/test/java/org/opensearch/flowframework/FlowFrameworkRestTestCase.java +++ b/src/test/java/org/opensearch/flowframework/FlowFrameworkRestTestCase.java @@ -438,12 +438,12 @@ protected Response getWorkflowStatus(String workflowId, boolean all) throws Exce protected Response getWorkflowStep() throws Exception { return TestHelpers.makeRequest( - client(), - "GET", - String.format("%s/%s", WORKFLOW_URI, "_step"), - Collections.emptyMap(), - "", - null + client(), + "GET", + String.format(Locale.ROOT, "%s/%s", WORKFLOW_URI, "_step"), + Collections.emptyMap(), + "", + null ); } diff --git a/src/test/java/org/opensearch/flowframework/rest/FlowFrameworkRestApiIT.java b/src/test/java/org/opensearch/flowframework/rest/FlowFrameworkRestApiIT.java index 23f733205..3a5e36ff6 100644 --- a/src/test/java/org/opensearch/flowframework/rest/FlowFrameworkRestApiIT.java +++ b/src/test/java/org/opensearch/flowframework/rest/FlowFrameworkRestApiIT.java @@ -14,8 +14,14 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.flowframework.FlowFrameworkRestTestCase; import org.opensearch.flowframework.TestHelpers; -import org.opensearch.flowframework.model.*; -import org.opensearch.flowframework.transport.GetWorkflowStepResponse; +import org.opensearch.flowframework.model.ProvisioningProgress; +import org.opensearch.flowframework.model.ResourceCreated; +import org.opensearch.flowframework.model.State; +import org.opensearch.flowframework.model.Template; +import org.opensearch.flowframework.model.Workflow; +import org.opensearch.flowframework.model.WorkflowEdge; +import org.opensearch.flowframework.model.WorkflowNode; +import org.opensearch.flowframework.model.WorkflowState; import java.util.Collections; import java.util.HashMap; @@ -62,10 +68,6 @@ public void testSearchWorkflows() throws Exception { } } - public void testGetWorkflowStep() throws Exception { - getAndAssertWorkflowStep(); - } - public void testCreateAndProvisionLocalModelWorkflow() throws Exception { // Using a 1 step template to register a local model and deploy model Template template = TestHelpers.createTemplateFromFile("register-deploylocalsparseencodingmodel.json"); diff --git a/src/test/java/org/opensearch/flowframework/rest/RestGetWorkflowStepActionTests.java b/src/test/java/org/opensearch/flowframework/rest/RestGetWorkflowStepActionTests.java new file mode 100644 index 000000000..b4767776f --- /dev/null +++ b/src/test/java/org/opensearch/flowframework/rest/RestGetWorkflowStepActionTests.java @@ -0,0 +1,81 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.flowframework.rest; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.flowframework.common.FlowFrameworkSettings; +import org.opensearch.rest.RestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.rest.FakeRestChannel; +import org.opensearch.test.rest.FakeRestRequest; + +import java.util.List; +import java.util.Locale; + +import static org.opensearch.flowframework.common.CommonValue.WORKFLOW_URI; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RestGetWorkflowStepActionTests extends OpenSearchTestCase { + private RestGetWorkflowStepAction restGetWorkflowStepAction; + private String getPath; + private FlowFrameworkSettings flowFrameworkFeatureEnabledSetting; + private NodeClient nodeClient; + + @Override + public void setUp() throws Exception { + super.setUp(); + + this.getPath = String.format(Locale.ROOT, "%s/%s", WORKFLOW_URI, "_step"); + flowFrameworkFeatureEnabledSetting = mock(FlowFrameworkSettings.class); + when(flowFrameworkFeatureEnabledSetting.isFlowFrameworkEnabled()).thenReturn(true); + this.restGetWorkflowStepAction = new RestGetWorkflowStepAction(flowFrameworkFeatureEnabledSetting); + this.nodeClient = mock(NodeClient.class); + } + + public void testRestGetWorkflowStepActionName() { + String name = restGetWorkflowStepAction.getName(); + assertEquals("get_workflow_step", name); + } + + public void testRestGetWorkflowStepActionRoutes() { + List routes = restGetWorkflowStepAction.routes(); + assertEquals(1, routes.size()); + assertEquals(RestRequest.Method.GET, routes.get(0).getMethod()); + assertEquals(this.getPath, routes.get(0).getPath()); + } + + public void testInvalidRequestWithContent() { + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.GET) + .withPath(this.getPath) + .withContent(new BytesArray("request body"), MediaTypeRegistry.JSON) + .build(); + + FakeRestChannel channel = new FakeRestChannel(request, false, 1); + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> { + restGetWorkflowStepAction.handleRequest(request, channel, nodeClient); + }); + assertEquals("request [GET /_plugins/_flow_framework/workflow/_step] does not support having a body", ex.getMessage()); + } + + public void testFeatureFlagNotEnabled() throws Exception { + when(flowFrameworkFeatureEnabledSetting.isFlowFrameworkEnabled()).thenReturn(false); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.GET) + .withPath(this.getPath) + .build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 1); + restGetWorkflowStepAction.handleRequest(request, channel, nodeClient); + assertEquals(RestStatus.FORBIDDEN, channel.capturedResponse().status()); + assertTrue(channel.capturedResponse().content().utf8ToString().contains("This API is disabled.")); + } +} diff --git a/src/test/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportActionTests.java b/src/test/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportActionTests.java new file mode 100644 index 000000000..6e6e39f80 --- /dev/null +++ b/src/test/java/org/opensearch/flowframework/transport/GetWorkflowStepTransportActionTests.java @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.flowframework.transport; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.core.action.ActionListener; +import org.opensearch.tasks.Task; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +import org.mockito.ArgumentCaptor; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class GetWorkflowStepTransportActionTests extends OpenSearchTestCase { + + private GetWorkflowStepTransportAction getWorkflowStepTransportAction; + + @Override + public void setUp() throws Exception { + super.setUp(); + + this.getWorkflowStepTransportAction = new GetWorkflowStepTransportAction(mock(TransportService.class), mock(ActionFilters.class)); + } + + public void testGetWorkflowStepAction() throws IOException { + WorkflowRequest workflowRequest = new WorkflowRequest(null, null); + ActionListener listener = mock(ActionListener.class); + getWorkflowStepTransportAction.doExecute(mock(Task.class), workflowRequest, listener); + + ArgumentCaptor stepCaptor = ArgumentCaptor.forClass(GetWorkflowStepResponse.class); + verify(listener, times(1)).onResponse(stepCaptor.capture()); + + } +}