Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
Add a workflow task that uses REST
Browse files Browse the repository at this point in the history
FLPATH-144 https://issues.redhat.com/browse/FLPATH-214

Signed-off-by: Yaron Dayagi <ydayagi@redhat.com>
  • Loading branch information
ydayagi committed May 16, 2023
1 parent ed01a70 commit 8dad088
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.redhat.parodos.tasks.rest;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;

interface RestService {
ResponseEntity<String> exchange(String url, HttpMethod method, HttpEntity<String> requestEntity) throws RestClientException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.redhat.parodos.tasks.rest;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

class RestServiceImpl implements RestService {

@Override
public ResponseEntity<String> exchange(String url, HttpMethod method, HttpEntity<String> requestEntity) throws RestClientException {
return new RestTemplate().exchange(url, method, requestEntity, String.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.redhat.parodos.tasks.rest;

import java.util.LinkedList;
import java.util.List;

import com.redhat.parodos.workflow.exception.MissingParameterException;
import com.redhat.parodos.workflow.parameter.WorkParameter;
import com.redhat.parodos.workflow.parameter.WorkParameterType;
import com.redhat.parodos.workflow.task.BaseWorkFlowTask;
import com.redhat.parodos.workflow.task.enums.WorkFlowTaskOutput;
import com.redhat.parodos.workflows.work.DefaultWorkReport;
import com.redhat.parodos.workflows.work.WorkContext;
import com.redhat.parodos.workflows.work.WorkReport;
import com.redhat.parodos.workflows.work.WorkStatus;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@Slf4j
public class RestWorkFlowTask extends BaseWorkFlowTask {

private RestService restService;

public RestWorkFlowTask() {
restService = new RestServiceImpl();
}

RestWorkFlowTask(String beanName, RestService restService) {
this.restService = restService;
setBeanName(beanName);
}

@Override
public @NonNull List<WorkParameter> getWorkFlowTaskParameters() {
LinkedList<WorkParameter> params = new LinkedList<>();
params.add(WorkParameter.builder().key("url").type(WorkParameterType.TEXT).optional(false)
.description("URL to send request to").build());
params.add(WorkParameter.builder().key("method").type(WorkParameterType.TEXT).optional(false)
.description("The HTTP method").build());
params.add(WorkParameter.builder().key("content").type(WorkParameterType.TEXT).optional(true)
.description("The content of the HTTP request").build());
params.add(WorkParameter.builder().key("username").type(WorkParameterType.TEXT).optional(true)
.description("Username for basic HTTP authentication").build());
params.add(WorkParameter.builder().key("password").type(WorkParameterType.TEXT).optional(true)
.description("Password for basic HTTP authentication").build());
params.add(WorkParameter.builder().key("response-key").type(WorkParameterType.TEXT).optional(true)
.description("The content of the response will be stored in this key").build());
return params;
}

@Override
public @NonNull List<WorkFlowTaskOutput> getWorkFlowTaskOutputs() {
return List.of(WorkFlowTaskOutput.OTHER);
}

@Override
public WorkReport execute(WorkContext workContext) {
String url = "";
try {
url = getRequiredParameterValue(workContext, "url");
String method = getRequiredParameterValue(workContext, "method");

HttpMethod httpMethod = HttpMethod.valueOf(method.toUpperCase());

ResponseEntity<String> responseEntity = restService.exchange(url, httpMethod, buildRequestEntity(workContext));

if (!responseEntity.getStatusCode().is2xxSuccessful()) {
throw new RestClientException(
"Request failed with HTTP status code " + responseEntity.getStatusCodeValue());
}

processResponseEntity(workContext, responseEntity);

}
catch (MissingParameterException | IllegalArgumentException e) {
log.error("Rest task failed for url " + url, e);
return new DefaultWorkReport(WorkStatus.FAILED, workContext, e);
}

return new DefaultWorkReport(WorkStatus.COMPLETED, workContext);
}

protected void processResponseEntity(WorkContext workContext, ResponseEntity<String> responseEntity)
throws RestClientException {
String responseKey = getOptionalParameterValue(workContext, "response-key", "");

if (responseKey.isEmpty()) {
return;
}

workContext.put(responseKey, responseEntity.getBody());
}

protected HttpEntity<String> buildRequestEntity(WorkContext workContext) throws RestClientException {
String content = getOptionalParameterValue(workContext, "content", "");
return new HttpEntity<>(content, buildHttpHeaders(workContext));
}

protected HttpHeaders buildHttpHeaders(WorkContext workContext) throws RestClientException {
HttpHeaders httpHeaders = new HttpHeaders();

httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(List.of(MediaType.APPLICATION_JSON));

String username = getOptionalParameterValue(workContext, "username", "");
String password;

if (!username.isEmpty()) {
try {
password = getRequiredParameterValue(workContext, "password");
}
catch (MissingParameterException e) {
throw new RestClientException("Missing password", e);
}
httpHeaders.setBasicAuth(username, password);
}
return httpHeaders;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package com.redhat.parodos.tasks.rest;

import com.redhat.parodos.workflow.context.WorkContextDelegate;
import com.redhat.parodos.workflow.exception.MissingParameterException;
import com.redhat.parodos.workflows.work.WorkContext;
import com.redhat.parodos.workflows.work.WorkReport;
import com.redhat.parodos.workflows.work.WorkStatus;
import org.junit.Before;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;

import java.util.HashMap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;

public class RestWorkFlowTaskTest {
private RestService restService = Mockito.mock(RestService.class);
private RestWorkFlowTask task = new RestWorkFlowTask("Test", restService);
HashMap<String, String> map;

@Before
public void setUp() {
map = new HashMap<>();
}

@Test
public void missingUrl() {
map.put("method", "get");

WorkReport result = task.execute(createWorkContext(map));

assertEquals(WorkStatus.FAILED, result.getStatus());
assertEquals(MissingParameterException.class, result.getError().getClass());
}

@Test
public void missingMethod() {
map.put("url", "http://localhost");

WorkReport result = task.execute(createWorkContext(map));

assertEquals(WorkStatus.FAILED, result.getStatus());
assertEquals(MissingParameterException.class, result.getError().getClass());
}

@Test
public void invalidMethod() {
map.put("url", "http://localhost");
map.put("method", "drop");

WorkReport result = task.execute(createWorkContext(map));

assertEquals(WorkStatus.FAILED, result.getStatus());
assertEquals(IllegalArgumentException.class, result.getError().getClass());
}

@Test
public void get() {
map.put("url", "http://localhost");
map.put("method", "get");
map.put("response-key", "http-body");

ResponseEntity<String> obj = new ResponseEntity<>("body", HttpStatus.OK);

doReturn(obj).when(restService).exchange(any(), any(), any());

WorkContext ctx = createWorkContext(map);

WorkReport result = task.execute(ctx);

assertEquals(WorkStatus.COMPLETED, result.getStatus());
assertEquals(obj.getBody(), ctx.get("http-body"));
}

@Test
public void post() {
map.put("url", "http://localhost");
map.put("method", "post");
map.put("content", "{\"root\": \"value\"}");
map.put("username", "test");
map.put("password", "test");

ResponseEntity<String> obj = new ResponseEntity<>(HttpStatus.OK);

doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
HttpEntity<String> request = invocationOnMock.getArgument(2);
HttpHeaders headers = request.getHeaders();

assertEquals(map.get("content"), request.getBody());
assertEquals(1, headers.getAccept().size());
assertNotNull(headers.getContentType());
assertTrue(headers.containsKey("Authorization"));

return obj;
}
}).when(restService).exchange(any(),any(),any());

WorkContext ctx = createWorkContext(map);

WorkReport result = task.execute(ctx);

assertEquals(WorkStatus.COMPLETED, result.getStatus());
}

@Test
public void requestNot2xx() {
map.put("url", "http://localhost");
map.put("method", "get");

ResponseEntity<String> obj = new ResponseEntity<>(HttpStatus.MOVED_PERMANENTLY);

doReturn(obj).when(restService).exchange(any(),any(),any());

WorkContext ctx = createWorkContext(map);

assertThrows(RestClientException.class, () -> task.execute(ctx));
}

private WorkContext createWorkContext(HashMap<String, String> map) {
WorkContext ctx = new WorkContext();
WorkContextDelegate.write(ctx, WorkContextDelegate.ProcessType.WORKFLOW_TASK_EXECUTION, task.getName(),
WorkContextDelegate.Resource.ARGUMENTS, map);
return ctx;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.redhat.parodos.examples;

import com.redhat.parodos.tasks.rest.RestWorkFlowTask;
import com.redhat.parodos.workflow.annotation.Infrastructure;
import com.redhat.parodos.workflows.workflow.SequentialFlow;
import com.redhat.parodos.workflows.workflow.WorkFlow;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("rest")
public class RestWorkFlowConfiguration {

@Bean
RestWorkFlowTask restTask() {
return new RestWorkFlowTask();
}

@Bean
@Infrastructure
WorkFlow restWorkFlow(@Qualifier("restTask") RestWorkFlowTask restTask) {
return SequentialFlow.Builder.aNewSequentialFlow().named("restWorkFlow").execute(restTask).build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.redhat.parodos.examples.rest;

import java.util.Arrays;
import java.util.List;

import com.redhat.parodos.sdk.api.WorkflowApi;
import com.redhat.parodos.sdk.api.WorkflowDefinitionApi;
import com.redhat.parodos.sdk.invoker.ApiClient;
import com.redhat.parodos.sdk.invoker.ApiException;
import com.redhat.parodos.sdk.invoker.Configuration;
import com.redhat.parodos.sdk.model.ArgumentRequestDTO;
import com.redhat.parodos.sdk.model.ProjectResponseDTO;
import com.redhat.parodos.sdk.model.WorkFlowDefinitionResponseDTO;
import com.redhat.parodos.sdk.model.WorkFlowRequestDTO;
import com.redhat.parodos.sdk.model.WorkFlowResponseDTO;
import com.redhat.parodos.sdk.model.WorkRequestDTO;
import com.redhat.parodos.workflow.utils.CredUtils;
import org.junit.Test;

import org.springframework.http.HttpHeaders;

import static com.redhat.parodos.sdkutils.SdkUtils.getProjectAsync;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class RestWorkFlow {

private final String projectName = "project-1";

private final String projectDescription = "Rest example project";

private final String workflowName = "restWorkFlow";

private final String taskName = "restTask";

@Test
public void runFlow() throws InterruptedException, ApiException {
ApiClient defaultClient = Configuration.getDefaultApiClient();
defaultClient.addDefaultHeader(HttpHeaders.AUTHORIZATION, "Basic " + CredUtils.getBase64Creds("test", "test"));
ProjectResponseDTO testProject = getProjectAsync(defaultClient, projectName, projectDescription);

// GET workflow DEFINITIONS
WorkflowDefinitionApi workflowDefinitionApi = new WorkflowDefinitionApi(defaultClient);
List<WorkFlowDefinitionResponseDTO> simpleSequentialWorkFlowDefinitions = workflowDefinitionApi
.getWorkFlowDefinitions(workflowName);
assertEquals(1, simpleSequentialWorkFlowDefinitions.size());

// GET WORKFLOW DEFINITION BY Id
WorkFlowDefinitionResponseDTO simpleSequentialWorkFlowDefinition = workflowDefinitionApi
.getWorkFlowDefinitionById(simpleSequentialWorkFlowDefinitions.get(0).getId());

// EXECUTE WORKFLOW
WorkflowApi workflowApi = new WorkflowApi();

// Define WorkRequests
WorkRequestDTO workGet = new WorkRequestDTO().workName(taskName).arguments(
Arrays.asList(new ArgumentRequestDTO().key("url").value("http://localhost:8080/api/v1/workflowdefinitions"),
new ArgumentRequestDTO().key("method").value("get"),
new ArgumentRequestDTO().key("username").value("test"),
new ArgumentRequestDTO().key("password").value("test")));

WorkFlowRequestDTO workFlowRequestGet = new WorkFlowRequestDTO().projectId(testProject.getId())
.workFlowName(workflowName).works(Arrays.asList(workGet));
WorkFlowRequestDTO workFlowRequests[] = new WorkFlowRequestDTO[] { workFlowRequestGet };

for (WorkFlowRequestDTO workFlowRequest : workFlowRequests) {
WorkFlowResponseDTO execute = workflowApi.execute(workFlowRequest);

assertNotNull(execute.getWorkFlowExecutionId());
}
}

}

0 comments on commit 8dad088

Please sign in to comment.