diff --git a/.github/workflows/api-test.yml b/.github/workflows/api-test.yml index 2364c874fe..3e4478548a 100644 --- a/.github/workflows/api-test.yml +++ b/.github/workflows/api-test.yml @@ -94,6 +94,8 @@ jobs: class: org.apache.dolphinscheduler.api.test.cases.WorkerGroupAPITest - name: Project class: org.apache.dolphinscheduler.api.test.cases.ProjectAPITest + - name: Workflow + class: org.apache.dolphinscheduler.api.test.cases.ProcessDefinitionAPITest env: RECORDING_PATH: /tmp/recording-${{ matrix.case.name }} steps: diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProcessDefinitionAPITest.java b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProcessDefinitionAPITest.java new file mode 100644 index 0000000000..2443d71d3b --- /dev/null +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProcessDefinitionAPITest.java @@ -0,0 +1,180 @@ +/* + * Licensed to 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. Apache Software Foundation (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.dolphinscheduler.api.test.cases; + +import org.apache.dolphinscheduler.api.test.core.DolphinScheduler; +import org.apache.dolphinscheduler.api.test.entity.HttpResponse; +import org.apache.dolphinscheduler.api.test.entity.LoginResponseData; +import org.apache.dolphinscheduler.api.test.pages.LoginPage; +import org.apache.dolphinscheduler.api.test.pages.project.ProjectPage; +import org.apache.dolphinscheduler.api.test.pages.workflow.ProcessDefinitionPage; +import org.apache.dolphinscheduler.api.test.utils.JSONUtils; +import org.apache.dolphinscheduler.common.enums.ReleaseState; +import org.apache.dolphinscheduler.common.enums.UserType; +import org.apache.dolphinscheduler.dao.entity.User; + +import java.io.File;; +import java.util.LinkedHashMap; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.util.EntityUtils; + +@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml") +@Slf4j +public class ProcessDefinitionAPITest { + + private static final String username = "admin"; + + private static final String password = "dolphinscheduler123"; + + private static String sessionId; + + private static User loginUser; + + private static ProcessDefinitionPage processDefinitionPage; + + private static ProjectPage projectPage; + + private static long projectCode; + + private static long processDefinitionCode; + + private static String processDefinitionName; + + + @BeforeAll + public static void setup() { + LoginPage loginPage = new LoginPage(); + HttpResponse loginHttpResponse = loginPage.login(username, password); + sessionId = JSONUtils.convertValue(loginHttpResponse.getBody().getData(), LoginResponseData.class).getSessionId(); + processDefinitionPage = new ProcessDefinitionPage(sessionId); + projectPage = new ProjectPage(sessionId); + loginUser = new User(); + loginUser.setId(123); + loginUser.setUserType(UserType.GENERAL_USER); + } + + @AfterAll + public static void cleanup() { + log.info("success cleanup"); + } + + @Test + @Order(1) + public void testImportProcessDefinition() { + try { + HttpResponse createProjectResponse = projectPage.createProject(loginUser, "project-test"); + HttpResponse queryAllProjectListResponse = projectPage.queryAllProjectList(loginUser); + Assertions.assertTrue(queryAllProjectListResponse.getBody().getSuccess()); + + projectCode = (long) ((LinkedHashMap) ((List) queryAllProjectListResponse.getBody().getData()).get(0)).get("code"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("workflow-json/test.json").getFile()); + CloseableHttpResponse importProcessDefinitionResponse = processDefinitionPage + .importProcessDefinition(loginUser, projectCode, file); + String data = EntityUtils.toString(importProcessDefinitionResponse.getEntity()); + Assertions.assertTrue(data.contains("\"success\":true")); + } catch (Exception e) { + log.error("failed", e); + Assertions.fail(); + } + } + + @Test + @Order(2) + public void testQueryAllProcessDefinitionByProjectCode() { + HttpResponse queryAllProcessDefinitionByProjectCodeResponse = processDefinitionPage.queryAllProcessDefinitionByProjectCode(loginUser, projectCode); + Assertions.assertTrue(queryAllProcessDefinitionByProjectCodeResponse.getBody().getSuccess()); + Assertions.assertTrue(queryAllProcessDefinitionByProjectCodeResponse.getBody().getData().toString().contains("hello world")); + processDefinitionCode = (long) ((LinkedHashMap) ((LinkedHashMap) ((List) queryAllProcessDefinitionByProjectCodeResponse.getBody().getData()).get(0)).get("processDefinition")).get("code"); + processDefinitionName = (String) ((LinkedHashMap) ((LinkedHashMap) ((List) queryAllProcessDefinitionByProjectCodeResponse.getBody().getData()).get(0)).get("processDefinition")).get("name"); + } + + @Test + @Order(3) + public void testQueryProcessDefinitionByCode() { + HttpResponse queryProcessDefinitionByCodeResponse = processDefinitionPage.queryProcessDefinitionByCode(loginUser, projectCode, processDefinitionCode); + Assertions.assertTrue(queryProcessDefinitionByCodeResponse.getBody().getSuccess()); + Assertions.assertTrue(queryProcessDefinitionByCodeResponse.getBody().getData().toString().contains("hello world")); + } + + @Test + @Order(4) + public void testGetProcessListByProjectCodes() { + HttpResponse getProcessListByProjectCodesResponse = processDefinitionPage.getProcessListByProjectCodes(loginUser, projectCode); + Assertions.assertTrue(getProcessListByProjectCodesResponse.getBody().getSuccess()); + Assertions.assertTrue(getProcessListByProjectCodesResponse.getBody().getData().toString().contains("test_import")); + } + + @Test + @Order(5) + public void testQueryProcessDefinitionByName() { + HttpResponse queryProcessDefinitionByNameResponse = processDefinitionPage.queryProcessDefinitionByName(loginUser, projectCode, processDefinitionName); + Assertions.assertTrue(queryProcessDefinitionByNameResponse.getBody().getSuccess()); + Assertions.assertTrue(queryProcessDefinitionByNameResponse.getBody().getData().toString().contains("hello world")); + } + + @Test + @Order(6) + public void testQueryProcessDefinitionList() { + HttpResponse queryProcessDefinitionListResponse = processDefinitionPage.queryProcessDefinitionList(loginUser, projectCode); + Assertions.assertTrue(queryProcessDefinitionListResponse.getBody().getSuccess()); + Assertions.assertTrue(queryProcessDefinitionListResponse.getBody().getData().toString().contains("hello world")); + } + + @Test + @Order(7) + public void testReleaseProcessDefinition() { + HttpResponse releaseProcessDefinitionResponse = processDefinitionPage.releaseProcessDefinition(loginUser, projectCode, processDefinitionCode, ReleaseState.ONLINE); + Assertions.assertTrue(releaseProcessDefinitionResponse.getBody().getSuccess()); + + HttpResponse queryProcessDefinitionByCodeResponse = processDefinitionPage.queryProcessDefinitionByCode(loginUser, projectCode, processDefinitionCode); + Assertions.assertTrue(queryProcessDefinitionByCodeResponse.getBody().getSuccess()); + Assertions.assertTrue(queryProcessDefinitionByCodeResponse.getBody().getData().toString().contains("releaseState=ONLINE")); + } + + @Test + @Order(8) + public void testDeleteProcessDefinitionByCode() { + HttpResponse deleteProcessDefinitionByCodeResponse = processDefinitionPage.deleteProcessDefinitionByCode(loginUser, projectCode, processDefinitionCode); + Assertions.assertFalse(deleteProcessDefinitionByCodeResponse.getBody().getSuccess()); + + HttpResponse releaseProcessDefinitionResponse = processDefinitionPage.releaseProcessDefinition(loginUser, projectCode, processDefinitionCode, ReleaseState.OFFLINE); + Assertions.assertTrue(releaseProcessDefinitionResponse.getBody().getSuccess()); + + deleteProcessDefinitionByCodeResponse = processDefinitionPage.deleteProcessDefinitionByCode(loginUser, projectCode, processDefinitionCode); + Assertions.assertTrue(deleteProcessDefinitionByCodeResponse.getBody().getSuccess()); + + HttpResponse queryProcessDefinitionListResponse = processDefinitionPage.queryProcessDefinitionList(loginUser, projectCode); + Assertions.assertTrue(queryProcessDefinitionListResponse.getBody().getSuccess()); + Assertions.assertFalse(queryProcessDefinitionListResponse.getBody().getData().toString().contains("hello world")); + } +} + + diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProjectAPITest.java b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProjectAPITest.java index bb6e1455aa..40caa45a31 100644 --- a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProjectAPITest.java +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/cases/ProjectAPITest.java @@ -32,7 +32,6 @@ import org.apache.dolphinscheduler.dao.entity.User; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/pages/workflow/ProcessDefinitionPage.java b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/pages/workflow/ProcessDefinitionPage.java new file mode 100644 index 0000000000..9a66722e4a --- /dev/null +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/pages/workflow/ProcessDefinitionPage.java @@ -0,0 +1,134 @@ +/* + * Licensed to 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. Apache Software Foundation (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.dolphinscheduler.api.test.pages.workflow; + +import org.apache.dolphinscheduler.api.test.core.Constants; +import org.apache.dolphinscheduler.api.test.entity.HttpResponse; +import org.apache.dolphinscheduler.api.test.utils.RequestClient; +import org.apache.dolphinscheduler.common.enums.ReleaseState; +import org.apache.dolphinscheduler.dao.entity.User; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.http.client.methods.CloseableHttpResponse; + + +@Slf4j +@AllArgsConstructor +public class ProcessDefinitionPage { + + private String sessionId; + + public CloseableHttpResponse importProcessDefinition(User loginUser, long projectCode, File file) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/import", projectCode); + return requestClient.postWithFile(url, headers, params, file); + } + + public HttpResponse queryAllProcessDefinitionByProjectCode(User loginUser, long projectCode) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/all", projectCode); + return requestClient.get(url, headers, params); + } + + public HttpResponse queryProcessDefinitionByCode(User loginUser, long projectCode, long processDefinitionCode) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/%s", projectCode, processDefinitionCode); + return requestClient.get(url, headers, params); + } + + public HttpResponse getProcessListByProjectCodes(User loginUser, long projectCode) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/query-process-definition-list", projectCode); + return requestClient.get(url, headers, params); + } + + public HttpResponse queryProcessDefinitionByName(User loginUser, long projectCode, String name) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + params.put("name", name); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/query-by-name", projectCode); + return requestClient.get(url, headers, params); + } + + public HttpResponse queryProcessDefinitionList(User loginUser, long projectCode) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/list", projectCode); + return requestClient.get(url, headers, params); + } + + public HttpResponse releaseProcessDefinition(User loginUser, long projectCode, long code, ReleaseState releaseState) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + params.put("code", code); + params.put("releaseState", releaseState); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/%s/release", projectCode, code); + return requestClient.post(url, headers, params); + } + + public HttpResponse deleteProcessDefinitionByCode(User loginUser, long projectCode, long code) { + Map params = new HashMap<>(); + params.put("loginUser", loginUser); + params.put("code", code); + Map headers = new HashMap<>(); + headers.put(Constants.SESSION_ID_KEY, sessionId); + + RequestClient requestClient = new RequestClient(); + String url = String.format("/projects/%s/process-definition/%s", projectCode, code); + return requestClient.delete(url, headers, params); + } +} diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/utils/RequestClient.java b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/utils/RequestClient.java index bcc8631ac5..2993ea8de3 100644 --- a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/utils/RequestClient.java +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api/test/utils/RequestClient.java @@ -23,6 +23,8 @@ import org.apache.dolphinscheduler.api.test.core.Constants; import org.apache.dolphinscheduler.api.test.entity.HttpResponse; import org.apache.dolphinscheduler.api.test.entity.HttpResponseBody; +import java.io.File; +import java.io.FileInputStream; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -37,6 +39,17 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; + + @Slf4j public class RequestClient { @@ -158,6 +171,34 @@ public class RequestClient { return httpResponse; } + public CloseableHttpResponse postWithFile(String url, Map headers, Map params, File file) { + try { + Headers headersBuilder = Headers.of(headers); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.addTextBody("json", getParams(params), ContentType.MULTIPART_FORM_DATA); + builder.addBinaryBody( + "file", + new FileInputStream(file), + ContentType.APPLICATION_OCTET_STREAM, + file.getName() + ); + HttpEntity multipart = builder.build(); + String requestUrl = String.format("%s%s", Constants.DOLPHINSCHEDULER_API_URL, url); + log.info("POST request to {}, Headers: {}, Params: {}", requestUrl, headersBuilder, params); + HttpPost httpPost = new HttpPost(requestUrl); + for (Map.Entry header : headers.entrySet()) { + httpPost.setHeader(new BasicHeader(header.getKey(), header.getValue())); + } + httpPost.setEntity(multipart); + CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = client.execute(httpPost); + return response; + } catch (Exception e) { + log.error("error", e); + } + return null; + } + @SneakyThrows public HttpResponse delete(String url, Map headers, Map params) { diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/workflow-json/test.json b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/workflow-json/test.json new file mode 100644 index 0000000000..ae11e05f38 --- /dev/null +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/workflow-json/test.json @@ -0,0 +1,81 @@ +[ { + "processDefinition" : { + "id" : 1, + "code" : 9752686452032, + "name" : "test", + "version" : 1, + "releaseState" : "OFFLINE", + "projectCode" : 9752680865600, + "description" : "", + "globalParams" : "[]", + "globalParamList" : [ ], + "globalParamMap" : { }, + "createTime" : "2023-06-01 20:41:02", + "updateTime" : "2023-06-01 20:41:02", + "flag" : "YES", + "userId" : 1, + "userName" : null, + "projectName" : null, + "locations" : "[{\"taskCode\":9752684723008,\"x\":132,\"y\":112}]", + "scheduleReleaseState" : null, + "timeout" : 0, + "modifyBy" : null, + "warningGroupId" : null, + "executionType" : "PARALLEL" + }, + "processTaskRelationList" : [ { + "id" : 1, + "name" : "", + "processDefinitionVersion" : 1, + "projectCode" : 9752680865600, + "processDefinitionCode" : 9752686452032, + "preTaskCode" : 0, + "preTaskVersion" : 0, + "postTaskCode" : 9752684723008, + "postTaskVersion" : 1, + "conditionType" : "NONE", + "conditionParams" : {}, + "createTime" : "2023-06-01 20:41:02", + "updateTime" : "2023-06-01 20:41:02", + "operator" : 1, + "operateTime" : "2023-06-01 20:41:02" + } ], + "taskDefinitionList" : [ { + "id" : 1, + "code" : 9752684723008, + "name" : "test", + "version" : 1, + "description" : "", + "projectCode" : 9752680865600, + "userId" : 1, + "taskType" : "SHELL", + "taskParams" : {"localParams":[],"rawScript":"echo \"hello world\"","resourceList":[]}, + "taskParamList" : [ ], + "taskParamMap" : null, + "flag" : "YES", + "isCache" : "NO", + "taskPriority" : "MEDIUM", + "userName" : null, + "projectName" : null, + "workerGroup" : "default", + "environmentCode" : -1, + "failRetryTimes" : 0, + "failRetryInterval" : 1, + "timeoutFlag" : "CLOSE", + "timeoutNotifyStrategy" : null, + "timeout" : 0, + "delayTime" : 0, + "resourceIds" : null, + "createTime" : "2023-06-01 20:41:02", + "updateTime" : "2023-06-01 20:41:02", + "modifyBy" : null, + "taskGroupId" : 0, + "taskGroupPriority" : 0, + "cpuQuota" : -1, + "memoryMax" : -1, + "taskExecuteType" : "BATCH", + "operator" : 1, + "operateTime" : "2023-06-01 20:41:02" + } ], + "schedule" : null +} ] \ No newline at end of file diff --git a/dolphinscheduler-api-test/dolphinscheduler-api-test-core/src/main/java/org/apache/dolphinscheduler/api/test/core/Constants.java b/dolphinscheduler-api-test/dolphinscheduler-api-test-core/src/main/java/org/apache/dolphinscheduler/api/test/core/Constants.java index 78a163bd89..7784a08587 100644 --- a/dolphinscheduler-api-test/dolphinscheduler-api-test-core/src/main/java/org/apache/dolphinscheduler/api/test/core/Constants.java +++ b/dolphinscheduler-api-test/dolphinscheduler-api-test-core/src/main/java/org/apache/dolphinscheduler/api/test/core/Constants.java @@ -32,6 +32,8 @@ public final class Constants { */ public static final String REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded"; + public static final String MULTIPART_FORM_DATA = "application/x-www-form-urlencoded"; + /** * header's session id's key */