Commit 31689945 authored by Aalekh Jain's avatar Aalekh Jain
Browse files

Updated `WorkflowManagerApi`

Added create system workflow and delete system workflow APIs
parent d8556d5a
package org.opengroup.osdu.workflow.api;
import java.util.List;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.model.WorkflowRole;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
......@@ -20,6 +17,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/v1/workflow")
public class WorkflowManagerApi {
......@@ -37,6 +36,17 @@ public class WorkflowManagerApi {
return workflowManagerService.createWorkflow(request);
}
/**
* API to create a system workflow.
* @param request Request object which has information to create workflow.
* @return Workflow metadata.
*/
@PostMapping("/system")
@PreAuthorize("@authorizationFilterSP.hasPermissions()")
public WorkflowMetadata createSystemWorkflow(@RequestBody final CreateWorkflowRequest request) {
return workflowManagerService.createSystemWorkflow(request);
}
/**
* Returns workflow metadata based on workflowName
* @param workflowName Name of the workflow for which metadata should be retrieved.
......@@ -44,7 +54,7 @@ public class WorkflowManagerApi {
*/
@GetMapping("/{workflow_name}")
@PreAuthorize("@authorizationFilter.hasPermission('" + WorkflowRole.VIEWER + "','" + WorkflowRole.CREATOR + "','" + WorkflowRole.ADMIN + "')")
public WorkflowMetadata getWorkflowByName(@PathVariable("workflow_name") final String workflowName) {
public WorkflowMetadata getWorkflowByName(@PathVariable("workflow_name") final String workflowName) {
return workflowManagerService.getWorkflowByName(workflowName);
}
......@@ -59,6 +69,17 @@ public class WorkflowManagerApi {
workflowManagerService.deleteWorkflow(workflowName);
}
/**
* Deletes system workflow by workflowName
* @param workflowName Name of the workflow which needs to be deleted.
*/
@DeleteMapping("/system/{workflow_name}")
@PreAuthorize("@authorizationFilterSP.hasPermissions()")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteSystemWorkflowById(@PathVariable("workflow_name") final String workflowName) {
workflowManagerService.deleteSystemWorkflow(workflowName);
}
/**
* Get List all the workflows for the tenant.
* @param prefix Filter workflow names which start with the full prefix specified.
......
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.workflow.api;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowSystemManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/v1/workflow/system")
public class WorkflowSystemManagerApi {
@Autowired
private IWorkflowSystemManagerService workflowSystemManagerService;
/**
* API to create a workflow.
* @param request Request object which has information to create workflow.
* @return Workflow metadata.
*/
@PostMapping
@PreAuthorize("@authorizationFilterSP.hasPermissions()")
public WorkflowMetadata createSystemWorkflow(@RequestBody final CreateWorkflowRequest request) {
return workflowSystemManagerService.createSystemWorkflow(request);
}
/**
* Deletes workflow by workflowName
* @param workflowName Name of the workflow which needs to be deleted.
*/
@DeleteMapping("/{workflow_name}")
@PreAuthorize("@authorizationFilterSP.hasPermissions()")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteSystemWorkflowById(@PathVariable("workflow_name") final String workflowName) {
workflowSystemManagerService.deleteSystemWorkflow(workflowName);
}
}
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.workflow.provider.interfaces;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
public interface IWorkflowSystemManagerService {
/**
* Creates workflow with given request.
* @param request Request object which has information to create workflow.
* @return Workflow metadata.
*/
WorkflowMetadata createSystemWorkflow(final CreateWorkflowRequest request);
/**
* Deletes workflow based on workflowName
* @param workflowName Id of the workflow which needs to be deleted.
*/
void deleteSystemWorkflow(final String workflowName);
}
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.workflow.service;
import org.apache.commons.lang3.StringUtils;
import org.opengroup.osdu.core.common.exception.BadRequestException;
import org.opengroup.osdu.workflow.logging.AuditLogger;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
import org.opengroup.osdu.workflow.model.WorkflowEngineRequest;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowEngineService;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowManagerService;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowMetadataRepository;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowRunService;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowSystemManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
@Component
public class WorkflowSystemManagerServiceImpl implements IWorkflowSystemManagerService {
@Autowired
private IWorkflowMetadataRepository workflowMetadataRepository;
@Autowired
private IWorkflowManagerService workflowManagerService;
@Autowired
private IWorkflowEngineService workflowEngineService;
@Autowired
private IWorkflowRunService workflowRunService;
@Autowired
private AuditLogger auditLogger;
@Override
public WorkflowMetadata createSystemWorkflow(CreateWorkflowRequest request) {
if (StringUtils.isEmpty(request.getWorkflowName())) {
throw new BadRequestException("Invalid workflow name provided");
}
return workflowManagerService.createWorkflow(request);
}
@Override
public void deleteSystemWorkflow(String workflowName) {
final WorkflowMetadata workflowMetadata = workflowMetadataRepository.getWorkflow(workflowName);
workflowMetadataRepository.deleteWorkflow(workflowName);
WorkflowEngineRequest rq = new WorkflowEngineRequest(workflowName, workflowMetadata.isDeployedThroughWorkflowService());
workflowEngineService.deleteWorkflow(rq);
auditLogger.workflowDeleteEvent(Collections.singletonList(workflowName));
}
}
package org.opengroup.osdu.workflow.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.entitlements.AuthorizationResponse;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import org.opengroup.osdu.workflow.exception.ResourceConflictException;
import org.opengroup.osdu.workflow.exception.WorkflowNotFoundException;
import org.opengroup.osdu.workflow.exception.handler.ConflictApiError;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.provider.interfaces.IAuthorizationServiceSP;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowSystemManagerService;
import org.opengroup.osdu.workflow.security.AuthorizationFilterSP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link WorkflowSystemManagerApi}
*/
@WebMvcTest({WorkflowSystemManagerApi.class})
@AutoConfigureMockMvc
@Import({AuthorizationFilterSP.class, DpsHeaders.class})
@Disabled
class WorkflowSystemManagerMvcTest {
private static final String TEST_AUTH = "Bearer bla";
private static final String PARTITION = "partition";
private static final String CORRELATION_ID = "sample-correlation-id";
private static final String WORKFLOW_RESPONSE = "{\n" +
" \"workflowId\": \"2afccfb8-1351-41c6-9127-61f2d7f22ff8\",\n" +
" \"workflowName\": \"HelloWorld\",\n" +
" \"description\": \"This is a test workflow\",\n" +
" \"creationTimestamp\": 1600144876028,\n" +
" \"createdBy\": \"user@email.com\",\n" +
" \"version\": 1\n" +
"}";
private static final String WORKFLOW_METADATA_LIST_RESPONSE = "[\n" +
" {\n" +
" \"workflowId\": \"2afccfb8-1351-41c6-9127-61f2d7f22ff8\",\n" +
" \"workflowName\": \"HelloWorld\",\n" +
" \"description\": \"This is a test workflow\",\n" +
" \"creationTimestamp\": 1600144876028,\n" +
" \"version\": 1,\n" +
" \"registrationInstructions\": {\n" +
" \"active\": true,\n" +
" \"concurrentWorkflowRun\": 5,\n" +
" \"concurrentTaskRun\": 5\n" +
" }\n" +
" }\n" +
"]";
private static final String WORKFLOW_REQUEST = "{\n" +
" \"workflowName\": \"HelloWorld\",\n" +
" \"description\": \"This is a test workflow\"\n" +
"}";
private static final String WORKFLOW_ENDPOINT = "/v1/workflow/system";
private static final String EXISTING_WORKFLOW_ID = "existing-id";
private static final String WORKFLOW_NAME = "test-dag-name";
private static final String WORKFLOW_ID = "2afccfb8-1351-41c6-9127-61f2d7f22ff8";
private static final String EMPTY_PREFIX_ERROR =
"Prefix cannot be Null or Empty. Please provide a value.";
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper mapper;
@MockBean
private IWorkflowSystemManagerService workflowSystemManagerService;
@MockBean
private IAuthorizationServiceSP authorizationServiceSP;
@MockBean
private JaxRsDpsLog log;
@MockBean
private DpsHeaders dpsHeaders;
@Mock
private AuthorizationResponse authorizationResponse;
@Test
void testCreateApiWithSuccess() throws Exception {
final CreateWorkflowRequest request = mapper
.readValue(WORKFLOW_REQUEST, CreateWorkflowRequest.class);
final WorkflowMetadata metadata = mapper.readValue(WORKFLOW_RESPONSE, WorkflowMetadata.class);
when(workflowSystemManagerService.createSystemWorkflow(eq(request))).thenReturn(metadata);
when(authorizationServiceSP.isDomainAdminServiceAccount())
.thenReturn(true);
when(dpsHeaders.getAuthorization()).thenReturn(TEST_AUTH);
when(dpsHeaders.getPartitionId()).thenReturn("");
when(dpsHeaders.getCorrelationId()).thenReturn(CORRELATION_ID);
final MvcResult mvcResult = mockMvc.perform(
post(WORKFLOW_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON)
.headers(getHttpHeaders())
.with(SecurityMockMvcRequestPostProcessors.csrf())
.content(WORKFLOW_REQUEST))
.andExpect(status().isOk())
.andReturn();
verify(workflowSystemManagerService, times(1)).createSystemWorkflow(eq(request));
verify(authorizationServiceSP, times(1)).isDomainAdminServiceAccount();
verify(dpsHeaders).getAuthorization();
verify(dpsHeaders).getPartitionId();
final WorkflowMetadata responseMetadata =
mapper.readValue(mvcResult.getResponse().getContentAsByteArray(), WorkflowMetadata.class);
assertThat(metadata, equalTo(responseMetadata));
}
@Test
public void testCreateSystemApiWithConflict() throws Exception {
final CreateWorkflowRequest request = mapper.readValue(WORKFLOW_REQUEST, CreateWorkflowRequest.class);
when(workflowSystemManagerService.createSystemWorkflow(eq(request)))
.thenThrow(new ResourceConflictException(EXISTING_WORKFLOW_ID, "conflict"));
when(authorizationServiceSP.isDomainAdminServiceAccount())
.thenReturn(true);
when(dpsHeaders.getAuthorization()).thenReturn(TEST_AUTH);
when(dpsHeaders.getPartitionId()).thenReturn("");
when(dpsHeaders.getCorrelationId()).thenReturn(CORRELATION_ID);
final MvcResult mvcResult = mockMvc.perform(
post(WORKFLOW_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON)
.headers(getHttpHeaders())
.with(SecurityMockMvcRequestPostProcessors.csrf())
.content(WORKFLOW_REQUEST))
.andExpect(status().isConflict())
.andReturn();
verify(workflowSystemManagerService, times(1)).createSystemWorkflow(eq(request));
when(authorizationServiceSP.isDomainAdminServiceAccount())
.thenReturn(true);
verify(dpsHeaders).getAuthorization();
verify(dpsHeaders).getPartitionId();
final ConflictApiError response =
mapper.readValue(mvcResult.getResponse().getContentAsByteArray(), ConflictApiError.class);
Assertions.assertEquals(EXISTING_WORKFLOW_ID, response.getConflictId());
}
@Test
void testDeleteSystemApiWithSuccess() throws Exception {
doNothing().when(workflowSystemManagerService).deleteSystemWorkflow(eq(WORKFLOW_NAME));
when(authorizationServiceSP.isDomainAdminServiceAccount())
.thenReturn(true);
when(dpsHeaders.getAuthorization()).thenReturn(TEST_AUTH);
when(dpsHeaders.getPartitionId()).thenReturn("");
when(dpsHeaders.getCorrelationId()).thenReturn(CORRELATION_ID);
mockMvc.perform(
delete("/v1/workflow/system/{workflow_name}", WORKFLOW_NAME)
.contentType(MediaType.APPLICATION_JSON)
.headers(getHttpHeaders())
.with(SecurityMockMvcRequestPostProcessors.csrf()))
.andExpect(status().is(204))
.andReturn();
verify(workflowSystemManagerService).deleteSystemWorkflow(eq(WORKFLOW_NAME));
verify(authorizationServiceSP, times(1)).isDomainAdminServiceAccount();
verify(dpsHeaders).getAuthorization();
verify(dpsHeaders).getPartitionId();
}
@Test
void testDeleteApiWithError() throws Exception {
doThrow(new WorkflowNotFoundException("not found")).when(workflowSystemManagerService)
.deleteSystemWorkflow(eq(WORKFLOW_NAME));
when(authorizationServiceSP.isDomainAdminServiceAccount())
.thenReturn(true);
when(dpsHeaders.getAuthorization()).thenReturn(TEST_AUTH);
when(dpsHeaders.getPartitionId()).thenReturn("");
when(dpsHeaders.getCorrelationId()).thenReturn(CORRELATION_ID);
mockMvc.perform(
delete("/v1/workflow/system/{workflow_name}", WORKFLOW_NAME)
.contentType(MediaType.APPLICATION_JSON)
.headers(getHttpHeaders())
.with(SecurityMockMvcRequestPostProcessors.csrf()))
.andExpect(status().isNotFound())
.andReturn();
verify(workflowSystemManagerService).deleteSystemWorkflow(eq(WORKFLOW_NAME));
verify(authorizationServiceSP, times(1)).isDomainAdminServiceAccount();
verify(dpsHeaders).getAuthorization();
verify(dpsHeaders).getPartitionId();
}
private HttpHeaders getHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add(DpsHeaders.AUTHORIZATION, TEST_AUTH);
//headers.add(DpsHeaders.DATA_PARTITION_ID, PARTITION);
return headers;
}
@TestConfiguration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public static class TestSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.csrf().disable(); //disable default authN. AuthN handled by endpoints proxy
}
}
}
package org.opengroup.osdu.workflow.services;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opengroup.osdu.core.common.exception.BadRequestException;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import org.opengroup.osdu.workflow.exception.WorkflowNotFoundException;
import org.opengroup.osdu.workflow.logging.AuditLogger;
import org.opengroup.osdu.workflow.model.CreateWorkflowRequest;
import org.opengroup.osdu.workflow.model.WorkflowEngineRequest;
import org.opengroup.osdu.workflow.model.WorkflowMetadata;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowEngineService;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowManagerService;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowMetadataRepository;
import org.opengroup.osdu.workflow.provider.interfaces.IWorkflowRunService;
import org.opengroup.osdu.workflow.service.WorkflowSystemManagerServiceImpl;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link WorkflowSystemManagerServiceImpl}
*/
@ExtendWith(MockitoExtension.class)
class WorkflowSystemManagerServiceTest {
private static final String WORKFLOW_NAME = "test_dag_name";
private static final String INVALID_WORKFLOW_NAME = "invalid-workflow-name";
private static final String USER_EMAIL = "user@email.com";
private static final long SEED_VERSION = 1;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final String CREATE_WORKFLOW_REQUEST = "{\n" +
" \"workflowName\": \"HelloWorld\",\n" +
" \"description\": \"This is a test workflow\",\n" +
" \"registrationInstructions\": {\n" +
" \"dagContent\": \"sample-dag-content\"\n" +
" }\n" +
"}";
private static final String CREATE_WORKFLOW_REQUEST_WITH_INVALID_WORKFLOW_NAME = "{\n" +
" \"workflowName\": \"\",\n" +
" \"description\": \"This is a test workflow\",\n" +
" \"registrationInstructions\": {\n" +
" \"dagContent\": \"sample-dag-content\"\n" +
" }\n" +
"}";
private static final String GET_WORKFLOW_RESPONSE = "{\n" +
" \"workflowId\": \"2afccfb8-1351-41c6-9127-61f2d7f22ff8\",\n" +
" \"workflowName\": \"HelloWorld\",\n" +
" \"description\": \"This is a test workflow\",\n" +
" \"creationTimestamp\": 1600144876028,\n" +
" \"createdBy\": \"user@email.com\",\n" +
" \"version\": 1\n" +
"}";
private static final String PREFIX_INPUT = "hwoello";
@Mock
private IWorkflowMetadataRepository workflowMetadataRepository;
@Mock
private DpsHeaders dpsHeaders;
@Mock
private IWorkflowEngineService workflowEngineService;
@Mock
private IWorkflowManagerService workflowManagerService;
@Mock
private IWorkflowRunService workflowRunService;
@Mock
private AuditLogger auditLogger;
@InjectMocks
private WorkflowSystemManagerServiceImpl workflowSystemManagerService;
@BeforeEach
public void setup() {
}
@Test
void testCreateWorkflowWithValidData() throws Exception {
final CreateWorkflowRequest request =
OBJECT_MAPPER.readValue(CREATE_WORKFLOW_REQUEST, CreateWorkflowRequest.class);
final WorkflowMetadata responseMetadata = mock(WorkflowMetadata.class);
when(workflowManagerService.createWorkflow(request))
.thenReturn(responseMetadata);
workflowSystemManagerService.createSystemWorkflow(request);