diff --git a/client/migrationx-common/pom.xml b/client/migrationx-common/pom.xml index 6f8b743..d2ffbec 100644 --- a/client/migrationx-common/pom.xml +++ b/client/migrationx-common/pom.xml @@ -22,11 +22,11 @@ migrationx com.aliyun.dataworks - 1.1.4 + 1.1.5 migrationx-common - 1.1.4 + 1.1.5 8 diff --git a/client/migrationx-domain/migrationx-domain-airflow/pom.xml b/client/migrationx-domain/migrationx-domain-airflow/pom.xml index ae0276e..064a65f 100644 --- a/client/migrationx-domain/migrationx-domain-airflow/pom.xml +++ b/client/migrationx-domain/migrationx-domain-airflow/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -38,7 +38,7 @@ com.aliyun.dataworks migrationx-domain-dataworks - 1.1.4 + 1.1.5 diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/pom.xml b/client/migrationx-domain/migrationx-domain-aliyunemr/pom.xml index fd47d4b..0a0cc9b 100644 --- a/client/migrationx-domain/migrationx-domain-aliyunemr/pom.xml +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -46,7 +46,6 @@ ch.qos.logback logback-classic - 1.2.13 commons-collections @@ -101,7 +100,7 @@ com.aliyun aliyun-java-sdk-emr - 4.0.212-SNAPSHOT + 3.3.8 diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrExportRequest.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrExportRequest.java index 9a796a7..fe9f80c 100644 --- a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrExportRequest.java +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrExportRequest.java @@ -17,28 +17,18 @@ import java.io.File; import java.util.List; +import java.util.Map; + +import lombok.Data; /** * @author sam.liux * @date 2020/12/15 */ +@Data public class AliyunEmrExportRequest { private File folder; private List projects; - - public File getFolder() { - return folder; - } - - public void setFolder(File folder) { - this.folder = folder; - } - - public List getProjects() { - return projects; - } - - public void setProjects(List projects) { - this.projects = projects; - } + private Map> projectFlowList; + private String folderFilter; } diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrProject.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrProject.java index d7cbbe3..c8182d1 100644 --- a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrProject.java +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrProject.java @@ -15,48 +15,24 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; -import com.aliyuncs.emr.model.v20160408.DescribeFlowResponse; +import java.util.List; +import java.util.Map; + import com.aliyuncs.emr.model.v20160408.ListFlowJobResponse; import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse; import com.aliyuncs.emr.model.v20160408.ListFlowResponse; - -import java.util.List; -import java.util.Map; +import lombok.Data; /** * @author sam.liux * @date 2020/12/15 */ +@Data public class AliyunEmrProject { private ListFlowProjectResponse.Project project; - private Map flows; + private Map flows; private List jobs; - public ListFlowProjectResponse.Project getProject() { - return project; - } - - public void setProject(ListFlowProjectResponse.Project project) { - this.project = project; - } - - public Map getFlows() { - return flows; - } - - public void setFlows( - Map flows) { - this.flows = flows; - } - - public List getJobs() { - return jobs; - } - - public void setJobs(List jobs) { - this.jobs = jobs; - } - public ListFlowJobResponse.Job getJobById(String jobId) { return this.jobs.stream().filter( job -> job.getId().equals(jobId)).findFirst().orElse(null); diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrService.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrService.java index 72ec5c0..5effc13 100644 --- a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrService.java +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrService.java @@ -15,9 +15,34 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + import com.aliyun.migrationx.common.utils.GsonUtils; import com.aliyuncs.DefaultAcsClient; -import com.aliyuncs.emr.model.v20160408.*; +import com.aliyuncs.emr.model.v20160408.DescribeFlowCategoryRequest; +import com.aliyuncs.emr.model.v20160408.DescribeFlowCategoryResponse; +import com.aliyuncs.emr.model.v20160408.DescribeFlowJobRequest; +import com.aliyuncs.emr.model.v20160408.DescribeFlowJobResponse; +import com.aliyuncs.emr.model.v20160408.DescribeFlowRequest; +import com.aliyuncs.emr.model.v20160408.DescribeFlowResponse; +import com.aliyuncs.emr.model.v20160408.ListFlowJobRequest; +import com.aliyuncs.emr.model.v20160408.ListFlowJobResponse; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectRequest; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse.Project; +import com.aliyuncs.emr.model.v20160408.ListFlowRequest; +import com.aliyuncs.emr.model.v20160408.ListFlowResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; @@ -25,15 +50,13 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.*; +import org.springframework.beans.BeanUtils; /** * @author sam.liux @@ -42,7 +65,7 @@ public class AliyunEmrService { private static final Logger LOGGER = LoggerFactory.getLogger(AliyunEmrService.class); - private static Gson gson = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); public static final String FLOW_DETAIL_EXT = ".detail"; public static final String JSON_FILE_EXT = ".json"; public static final String FLOW_DIR_NAME = "flow"; @@ -51,11 +74,12 @@ public class AliyunEmrService { public static final String FILE_ENCODE = "utf-8"; public static final String FILE_PACKAGE_INFO = ".package_info.txt"; - private String accessId; - private String accessKey; - private String endpoint; - private String regionId; + private final String accessId; + private final String accessKey; + private final String endpoint; + private final String regionId; private DefaultAcsClient client; + private final Map flowCategoryResponseLocalCache = new HashMap<>(); public AliyunEmrService(String accessId, String accessKey, String endpoint, String regionId) { this.accessId = accessId; @@ -71,7 +95,7 @@ private DefaultAcsClient getDefaultAcsClient() throws ClientException { } synchronized (this) { - DefaultProfile.addEndpoint(regionId, regionId, EMR_POP_PRODUCT, endpoint); + DefaultProfile.addEndpoint(regionId, EMR_POP_PRODUCT, endpoint); IClientProfile profile = DefaultProfile.getProfile(regionId, accessId, accessKey); client = new DefaultAcsClient(profile); return client; @@ -101,33 +125,36 @@ public void dump(AliyunEmrExportRequest request) throws ClientException, IOExcep listFlowProjectRequest.setPageSize(pageSize); listFlowProjectRequest.setPageNumber(pageNumber); - Integer totalCount = 0; + int totalCount = 0; while (true) { listFlowProjectRequest.setPageNumber(pageNumber); ListFlowProjectResponse response = getDefaultAcsClient().getAcsResponse(listFlowProjectRequest); - for (ListFlowProjectResponse.Project project : response.getProjects()) - { + for (ListFlowProjectResponse.Project project : response.getProjects()) { if (CollectionUtils.isNotEmpty(request.getProjects()) && !request.getProjects().contains(project.getName())) { continue; } - LOGGER.info("exporting project: {}", project.getName()); + List projectFlowList = ListUtils.emptyIfNull(MapUtils.emptyIfNull(request.getProjectFlowList()).get(project.getName())); + LOGGER.info("exporting project: {}, projectFlowList: {}", project.getName(), projectFlowList); + File projectFolder = new File(folder.getAbsolutePath() + File.separator + project.getName()); if (!projectFolder.exists()) { - projectFolder.mkdirs(); + boolean res = projectFolder.mkdirs(); + LOGGER.info("mkdir project folder: {} {}", projectFolder, res); } + String projectJson = gson.toJson(project); File projectFile = new File(projectFolder.getAbsolutePath() + File.separator + project.getName() + JSON_FILE_EXT); FileUtils.write(projectFile, projectJson, Charset.forName(FILE_ENCODE)); - dumpProjectJobs(project.getId(), projectFolder.getAbsolutePath()); - dumpProjectFlows(project.getId(), projectFolder.getAbsolutePath()); + dumpProjectJobs(project, projectFolder.getAbsolutePath()); + dumpProjectFlows(project, projectFolder.getAbsolutePath(), projectFlowList, request.getFolderFilter()); } totalCount += response.getProjects().size(); - pageNumber ++; + pageNumber++; if (totalCount >= response.getTotal()) { break; } @@ -135,7 +162,8 @@ public void dump(AliyunEmrExportRequest request) throws ClientException, IOExcep writePackageInfo(folder); } - public void dumpProjectFlows(String projectId, String absolutePath) throws ClientException, IOException { + public void dumpProjectFlows(Project project, String absolutePath, List projectFlowList, String folderFilter) + throws ClientException, IOException { File projectFolder = new File(absolutePath); if (!projectFolder.exists()) { projectFolder.mkdirs(); @@ -147,7 +175,7 @@ public void dumpProjectFlows(String projectId, String absolutePath) throws Clien ListFlowRequest request = new ListFlowRequest(); request.setPageNumber(pageNumber); request.setPageNumber(pageSize); - request.setProjectId(projectId); + request.setProjectId(project.getId()); while (true) { request.setPageNumber(pageNumber); @@ -158,22 +186,40 @@ public void dumpProjectFlows(String projectId, String absolutePath) throws Clien } for (ListFlowResponse.FlowItem flow : response.getFlow()) { + if (CollectionUtils.isNotEmpty(projectFlowList) && !projectFlowList.contains(flow.getName())) { + LOGGER.info("skip flow name: {} that not in flow list expected to export", flow.getName()); + continue; + } + + flow.setCronExpr(CronUtil.cronToDwCron(flow.getCronExpr())); String json = gson.toJson(flow); File flowFile = new File(jobFolder.getAbsolutePath() + File.separator + flow.getId() + JSON_FILE_EXT); DescribeFlowRequest describeFlowRequest = new DescribeFlowRequest(); - describeFlowRequest.setProjectId(projectId); + describeFlowRequest.setProjectId(project.getId()); describeFlowRequest.setId(flow.getId()); DescribeFlowResponse res = getDefaultAcsClient().getAcsResponse(describeFlowRequest); - String flowDetailJson = gson.toJson(res); - File flowDetailFile = new File( - jobFolder.getAbsolutePath() + File.separator + flow.getId() + FLOW_DETAIL_EXT + JSON_FILE_EXT); + Flow theFlow = new Flow(); + BeanUtils.copyProperties(res, theFlow); + theFlow.setProject(project); + + List paths = new ArrayList<>(); + supplyFlowCategoryPath(paths, project, res.getCategoryId()); + theFlow.setPath(StringUtils.join(paths, File.separator)); + theFlow.setCronExpr(CronUtil.cronToDwCron(theFlow.getCronExpr())); + if (StringUtils.isNotBlank(folderFilter) && !StringUtils.startsWith(theFlow.getPath(), folderFilter)) { + LOGGER.info("ignore flow that not in path: {}, ignored flow path: {}", folderFilter, theFlow.getPath()); + continue; + } + + String flowDetailJson = gson.toJson(theFlow); + File flowDetailFile = new File(jobFolder.getAbsolutePath() + File.separator + flow.getId() + FLOW_DETAIL_EXT + JSON_FILE_EXT); FileUtils.write(flowFile, json, Charset.forName(FILE_ENCODE)); FileUtils.write(flowDetailFile, flowDetailJson, Charset.forName(FILE_ENCODE)); } - pageNumber ++; + pageNumber++; total += response.getFlow().size(); if (total >= response.getTotal()) { @@ -182,7 +228,28 @@ public void dumpProjectFlows(String projectId, String absolutePath) throws Clien } } - public void dumpProjectJobs(String projectId, String absolutePath) throws ClientException, IOException { + public void supplyFlowCategoryPath(List paths, ListFlowProjectResponse.Project project, String categoryId) throws ClientException { + DescribeFlowCategoryRequest request = new DescribeFlowCategoryRequest(); + request.setProjectId(project.getId()); + request.setId(categoryId); + DescribeFlowCategoryResponse response = Optional.ofNullable(flowCategoryResponseLocalCache.get(categoryId)) + .orElse(getDefaultAcsClient().getAcsResponse(request)); + if (response == null) { + LOGGER.warn("response got null by request: {}", GsonUtils.toJsonString(request)); + return; + } + flowCategoryResponseLocalCache.put(response.getId(), response); + paths.add(response.getName()); + if (StringUtils.isBlank(response.getParentId())) { + Collections.reverse(paths); + LOGGER.info("got full category path: {}", StringUtils.join(paths, File.separator)); + return; + } + + supplyFlowCategoryPath(paths, project, response.getParentId()); + } + + public void dumpProjectJobs(ListFlowProjectResponse.Project project, String absolutePath) throws ClientException, IOException { File projectFolder = new File(absolutePath); if (!projectFolder.exists()) { projectFolder.mkdirs(); @@ -194,7 +261,7 @@ public void dumpProjectJobs(String projectId, String absolutePath) throws Client ListFlowJobRequest request = new ListFlowJobRequest(); request.setPageNumber(pageNumber); request.setPageNumber(pageSize); - request.setProjectId(projectId); + request.setProjectId(project.getId()); request.setAdhoc(false); while (true) { @@ -202,16 +269,27 @@ public void dumpProjectJobs(String projectId, String absolutePath) throws Client ListFlowJobResponse response = getDefaultAcsClient().getAcsResponse(request); File jobFolder = new File(projectFolder.getAbsolutePath() + File.separator + JOB_DIR_NAME); if (!jobFolder.exists()) { - jobFolder.mkdirs(); + boolean res = jobFolder.mkdirs(); + LOGGER.info("mkdir {} {}", jobFolder, res); } for (ListFlowJobResponse.Job job : response.getJobList()) { - String json = gson.toJson(job); + job.setParamConf(replaceJobDateParam(job.getParamConf())); + + DescribeFlowJobRequest descFlowJobRequest = new DescribeFlowJobRequest(); + descFlowJobRequest.setProjectId(project.getId()); + descFlowJobRequest.setId(job.getId()); + DescribeFlowJobResponse jobResponse = getDefaultAcsClient().getAcsResponse(descFlowJobRequest); + if (jobResponse != null) { + jobResponse.setParamConf(replaceJobDateParam(jobResponse.getParamConf())); + } + + String json = gson.toJson(jobResponse != null ? jobResponse : job); File jobFile = new File(jobFolder.getAbsolutePath() + File.separator + job.getId() + JSON_FILE_EXT); FileUtils.write(jobFile, json, Charset.forName(FILE_ENCODE)); } - pageNumber ++; + pageNumber++; total += response.getJobList().size(); if (total >= response.getTotal()) { @@ -220,19 +298,31 @@ public void dumpProjectJobs(String projectId, String absolutePath) throws Client } } + private String replaceJobDateParam(String paramConf) { + if (StringUtils.isBlank(paramConf)) { + return paramConf; + } + + Map paramMap = GsonUtils.fromJsonString(paramConf, + new TypeToken>() {}.getType()); + MapUtils.emptyIfNull(paramMap).forEach((key, val) -> + MapUtils.emptyIfNull(paramMap).put(key, ParamUtil.convertParameterExpression(val))); + return GsonUtils.toJsonString(paramMap); + } + public static List load(String fromFolder) throws IOException { File folderPath = new File(fromFolder); List projects = new ArrayList<>(); - for (File projectPath: folderPath.listFiles(pathname -> pathname.isDirectory())) { + for (File projectPath : Objects.requireNonNull(folderPath.listFiles(File::isDirectory))) { File projectJsonFile = new File( projectPath.getAbsolutePath() + File.separator + projectPath.getName() + JSON_FILE_EXT); String prjJson = FileUtils.readFileToString(projectJsonFile, Charset.forName(FILE_ENCODE)); ListFlowProjectResponse.Project project = gson.fromJson( - prjJson, new TypeToken(){}.getType()); + prjJson, new TypeToken() {}.getType()); List jobs = loadProjectJobs(projectPath); - Map flows = loadProjectFlows(projectPath); + Map flows = loadProjectFlows(projectPath); AliyunEmrProject aliyunEmrProject = new AliyunEmrProject(); aliyunEmrProject.setProject(project); @@ -243,9 +333,9 @@ public static List load(String fromFolder) throws IOException return projects; } - private static Map loadProjectFlows(File projectPath) throws IOException { + private static Map loadProjectFlows(File projectPath) throws IOException { File flowFolder = new File(projectPath.getAbsolutePath() + File.separator + FLOW_DIR_NAME); - Map flows = new HashMap<>(100); + Map flows = new HashMap<>(100); File[] files = flowFolder.listFiles(f -> f.isFile() && f.getName().endsWith(JSON_FILE_EXT)); if (files == null) { return flows; @@ -258,15 +348,15 @@ private static Map loadProjectF String flowJson = FileUtils.readFileToString(flowJsonFile, Charset.forName(FILE_ENCODE)); ListFlowResponse.FlowItem flowItem = gson.fromJson( - flowJson, new TypeToken(){}.getType()); + flowJson, new TypeToken() {}.getType()); File jobDetailFile = new File( flowJsonFile.getAbsolutePath().replaceAll(JSON_FILE_EXT + "$", "") + FLOW_DETAIL_EXT + JSON_FILE_EXT); - DescribeFlowResponse flowDetail = null; + Flow flowDetail = null; if (jobDetailFile.exists()) { String jobDetailJson = FileUtils.readFileToString(jobDetailFile, Charset.forName(FILE_ENCODE)); - flowDetail = gson.fromJson(jobDetailJson, new TypeToken(){}.getType()); + flowDetail = gson.fromJson(jobDetailJson, new TypeToken() {}.getType()); } flows.put(flowItem, flowDetail); } diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtil.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtil.java new file mode 100644 index 0000000..62cd7bf --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtil.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +/** + * @author 聿剑 + * @date 2024/5/11 + */ +@Slf4j +public class CronUtil { + public static String cronToDwCron(String cron) { + if (StringUtils.isBlank(cron)) { + return cron; + } + + String[] parts = StringUtils.split(cron, " \t"); + if (parts == null || parts.length != 6) { + log.warn("invalid quartz cron part count: {}", cron); + return cron; + } + + String week = parts[5]; + if (StringUtils.isNumeric(week)) { + parts[5] = String.valueOf(rotateWeek(Integer.parseInt(week))); + } else if (StringUtils.contains(week, ",")) { + String[] weeks = StringUtils.split(week, ","); + parts[5] = Stream.of(weeks).map(Integer::valueOf).map(CronUtil::rotateWeek).map(String::valueOf).collect(Collectors.joining(",")); + } else if (StringUtils.contains(week, "-")) { + String[] weeks = StringUtils.split(week, "-"); + if (weeks.length == 2) { + parts[5] = rotateWeek(Integer.parseInt(weeks[0])) + "-" + rotateWeek(Integer.parseInt(weeks[1])); + } + } + return String.join(" ", parts); + } + + private static int rotateWeek(int week) { + switch (week) { + case 1: + return 7; + case 2: + return 1; + case 3: + return 2; + case 4: + return 3; + case 5: + return 4; + case 6: + return 5; + case 7: + return 6; + default: + return week; + } + } +} diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/Flow.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/Flow.java new file mode 100644 index 0000000..e745dc6 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/Flow.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import com.aliyuncs.emr.model.v20160408.DescribeFlowResponse; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author 聿剑 + * @date 2024/4/22 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class Flow extends DescribeFlowResponse { + private ListFlowProjectResponse.Project project; + private String path; +} diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtil.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtil.java new file mode 100644 index 0000000..f66066f --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtil.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +/** + * @author 聿剑 + * @date 2024/5/12 + */ +@Slf4j +public class ParamUtil { + private static final String DEFAULT_PARAMETER_CONVERT_PATTERN + = "\\$\\{(?[y{4}|M{2}|d{2}|H{2}|h{2}|m{2}|s{2}|:|a-z|A-Z||_| ]+)\\s*((?[-|+])\\s*(?\\d+)(?[y|m|d|h]))?}"; + + public static String convertParameterExpression(String value) { + Pattern p = Pattern.compile(DEFAULT_PARAMETER_CONVERT_PATTERN); + Matcher m = p.matcher(value); + boolean matched = false; + String newValue = value; + while (m.find()) { + matched = true; + String dateExpr = m.group("expr"); + String opr = m.group("opr"); + String count = m.group("count"); + String unit = m.group("unit"); + String newExpr = dateExpr + .replace("mm", "mi") + .replace("MM", "mm") + .replace("HH", "hh24"); + String oprSuffix = null; + if ("h".equalsIgnoreCase(unit)) { + oprSuffix = "/24"; + } + String newOpr = Optional.ofNullable(opr).orElse("") + + Optional.ofNullable(count).orElse("") + + Optional.ofNullable(oprSuffix).orElse(""); + if ("y".equalsIgnoreCase(unit)) { + newExpr = "yyyy"; + } + // 小时粒度 + newValue = "$[" + newExpr.trim() + newOpr + "]"; + if (StringUtils.indexOfIgnoreCase(newExpr, "dd") < 0) { + // >天粒度,为月,年粒度 + newValue = "${" + newExpr.trim() + newOpr + "}"; + } + } + if (matched) { + // log.info("convert emr job parameter expr: {} to dataworks expr: {}, by pattern: {}", value, newValue, + // DEFAULT_PARAMETER_CONVERT_PATTERN); + return newValue; + } + + return value; + } +} diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrServiceTest.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrServiceTest.java new file mode 100644 index 0000000..c68a167 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/AliyunEmrServiceTest.java @@ -0,0 +1,54 @@ +package com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse; +import com.aliyuncs.emr.model.v20160408.ListFlowProjectResponse.Project; +import com.aliyuncs.exceptions.ClientException; +import com.aliyuncs.profile.DefaultProfile; +import com.aliyuncs.profile.IClientProfile; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author sam.liux + * @date 2019/06/27 + */ +public class AliyunEmrServiceTest { + private final String accessId = System.getenv("ALIYUN_EMR_ACCESS_ID"); + private final String accessKey = System.getenv("ALIYUN_EMR_ACCESS_KEY"); + private final String endpoint = "emr.aliyuncs.com"; + private final String regionId = "cn-shanghai"; + + private DefaultAcsClient getDefaultAcsClient() { + DefaultProfile.addEndpoint(regionId, "emr", endpoint); + IClientProfile profile = DefaultProfile.getProfile(regionId, accessId, accessKey); + return new DefaultAcsClient(profile); + } + + @Test + public void testSupplyPath() throws ClientException, IOException { + AliyunEmrService service = new AliyunEmrService(accessId, accessKey, endpoint, regionId); + String flowId = "FC-761D677371F646A2"; + String projectId = "FP-C8B0B85B7AB70D51"; + ListFlowProjectResponse.Project project = new Project(); + project.setId(projectId); + List paths = new ArrayList<>(); + service.supplyFlowCategoryPath(paths, project, flowId); + System.out.println(StringUtils.join(paths, File.separator)); + + AliyunEmrExportRequest request = new AliyunEmrExportRequest(); + File dir = new File(Objects.requireNonNull(AliyunEmrServiceTest.class.getClassLoader().getResource(".")).getFile()); + request.setFolder(new File(Objects.requireNonNull(AliyunEmrServiceTest.class.getClassLoader().getResource(".")).getFile())); + request.setProjects(Collections.singletonList("Default")); + service.dump(request); + Assert.assertTrue(dir.listFiles() != null && Objects.requireNonNull(dir.listFiles()).length > 0); + } +} diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtilTest.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtilTest.java new file mode 100644 index 0000000..cee7227 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/CronUtilTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author 聿剑 + * @date 2024/5/11 + */ +public class CronUtilTest { + @Test + public void test() { + Assert.assertEquals("0 0 9 ? * 1", CronUtil.cronToDwCron("0 0 9 ? * 2")); + Assert.assertEquals("0 0 9 ? * 7,1,2,3,4,5,6", CronUtil.cronToDwCron("0 0 9 ? * 1,2,3,4,5,6,7")); + Assert.assertEquals("0 0 9 ? * 6-1", CronUtil.cronToDwCron("0 0 9 ? * 7-2")); + Assert.assertEquals("0 0 9 ? * ?", CronUtil.cronToDwCron("0 0 9 ? * ?")); + + // failed case + Assert.assertEquals(" ", CronUtil.cronToDwCron(" ")); + Assert.assertEquals("0 0 9 ? * 2 * ", CronUtil.cronToDwCron("0 0 9 ? * 2 * ")); + } +} diff --git a/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtilTest.java b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtilTest.java new file mode 100644 index 0000000..e167157 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-aliyunemr/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/aliyunemr/ParamUtilTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr; + +import org.junit.Assert; +import org.junit.Test; + +/** + * TODO 概要描述 + *

TODO 详细描述 + * + * @author 聿剑 + * @date 2024/5/12 + */ +public class ParamUtilTest { + @Test + public void test() { + Assert.assertEquals("$[yyyymmddhh24-7/24]", ParamUtil.convertParameterExpression("${yyyyMMddHH-7h}")); + Assert.assertEquals("${yyyymm-7}", ParamUtil.convertParameterExpression("${yyyyMM-7m}")); + Assert.assertEquals("${yyyy-7}", ParamUtil.convertParameterExpression("${yyyy-7y}")); + Assert.assertEquals("$[yyyymmdd-26]", ParamUtil.convertParameterExpression("${yyyyMMdd-26d}")); + Assert.assertEquals("$[ddmmyyyy-26]", ParamUtil.convertParameterExpression("${ddMMyyyy-26d}")); + Assert.assertEquals("$[ddmmyyyy-26]", ParamUtil.convertParameterExpression("${ddMMyyyy - 26d}")); + Assert.assertEquals("$[yyyymmdd hh24:mi:ss]", + ParamUtil.convertParameterExpression("${yyyyMMdd HH:mm:ss}")); + Assert.assertEquals("$[yyyymmdd-4]", ParamUtil.convertParameterExpression("${yyyyMMdd-4d}")); + } +} diff --git a/client/migrationx-domain/migrationx-domain-azkaban/pom.xml b/client/migrationx-domain/migrationx-domain-azkaban/pom.xml index f0a7a61..8a14567 100644 --- a/client/migrationx-domain/migrationx-domain-azkaban/pom.xml +++ b/client/migrationx-domain/migrationx-domain-azkaban/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 diff --git a/client/migrationx-domain/migrationx-domain-caiyunjian/pom.xml b/client/migrationx-domain/migrationx-domain-caiyunjian/pom.xml index a6c228d..c760154 100644 --- a/client/migrationx-domain/migrationx-domain-caiyunjian/pom.xml +++ b/client/migrationx-domain/migrationx-domain-caiyunjian/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -46,7 +46,7 @@ com.aliyun.dataworks migrationx-domain-dataworks - 1.1.4 + 1.1.5 diff --git a/client/migrationx-domain/migrationx-domain-core/pom.xml b/client/migrationx-domain/migrationx-domain-core/pom.xml index bbb27e2..83e9beb 100644 --- a/client/migrationx-domain/migrationx-domain-core/pom.xml +++ b/client/migrationx-domain/migrationx-domain-core/pom.xml @@ -21,7 +21,7 @@ com.aliyun.dataworks migrationx-domain - 1.1.4 + 1.1.5 migrationx-domain-core diff --git a/client/migrationx-domain/migrationx-domain-datago/pom.xml b/client/migrationx-domain/migrationx-domain-datago/pom.xml index 21a51ae..df32979 100644 --- a/client/migrationx-domain/migrationx-domain-datago/pom.xml +++ b/client/migrationx-domain/migrationx-domain-datago/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -38,17 +38,17 @@ com.aliyun.dataworks migrationx-domain-caiyunjian - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-common - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-dataworks - 1.1.4 + 1.1.5 org.apache.velocity diff --git a/client/migrationx-domain/migrationx-domain-dataworks/pom.xml b/client/migrationx-domain/migrationx-domain-dataworks/pom.xml index a1e450f..444101c 100644 --- a/client/migrationx-domain/migrationx-domain-dataworks/pom.xml +++ b/client/migrationx-domain/migrationx-domain-dataworks/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -39,7 +39,7 @@ com.aliyun.dataworks migrationx-domain-core - 1.1.4 + 1.1.5 com.alibaba diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/constants/ChangeType.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/constants/ChangeType.java new file mode 100644 index 0000000..0a64717 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/constants/ChangeType.java @@ -0,0 +1,46 @@ +package com.aliyun.dataworks.migrationx.domain.dataworks.constants; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 文件的变更类型 + * + * @author 戒迷 + * @date 2024/04/17 + */ +@RequiredArgsConstructor +@Getter +public enum ChangeType { + + /** + * NEW + */ + NEW(0), + /** + * UPDATE + */ + UPDATE(1), + /** + * DELETE + */ + DELETE(2), + ; + + private final Integer code; + + /** + * 通过code来获取枚举对象 + * + * @param code + * @return + */ + public static ChangeType getByCode(Integer code) { + for (ChangeType type : ChangeType.values()) { + if (code.equals(type.getCode())) { + return type; + } + } + return null; + } +} diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/File.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/File.java index 36ac10a..10166d7 100644 --- a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/File.java +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/File.java @@ -52,6 +52,7 @@ public class File { private String fileName; private Integer filePublish; private Integer fileType; + private String fileTypeStr; private String galaxyResultTableSql; private String galaxySourceTableSql; private String galaxyTaskConfig; diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileNodeCfg.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileNodeCfg.java index 552a328..69e7067 100644 --- a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileNodeCfg.java +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileNodeCfg.java @@ -15,12 +15,17 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client; +import java.util.Date; +import java.util.List; + +import com.alibaba.fastjson.JSON; + +import com.aliyun.migrationx.common.utils.GsonUtils; import lombok.Data; import lombok.ToString; import lombok.experimental.Accessors; - -import java.util.Date; -import java.util.List; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; /** * @author sam.liux @@ -66,4 +71,16 @@ public class FileNodeCfg { Integer taskRerunInterval; Integer taskRerunTime; String extConfig; + + public void setOutputByOutputList() { + if (StringUtils.isBlank(output) && !CollectionUtils.isEmpty(outputList)) { + this.output = GsonUtils.toJsonString(outputList); + } + } + + public void setInputByInputList() { + if (StringUtils.isBlank(input) && !CollectionUtils.isEmpty(inputList)) { + this.input = GsonUtils.toJsonString(inputList); + } + } } diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileVersion.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileVersion.java new file mode 100644 index 0000000..c03f138 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/objects/entity/client/FileVersion.java @@ -0,0 +1,24 @@ +package com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client; + +import lombok.Data; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author 戒迷 + * @date 2024/4/16 + */ +@Data +@Accessors(chain = true) +@ToString(exclude = {"fileContent"}) +public class FileVersion { + private Long fileId; + private String fileContent; + private String commitUser; + private Integer fileType; + private String changeType; + private String fileName; + private Long cloudUuid; + private String comment; + private String useType; +} diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverter.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverter.java new file mode 100644 index 0000000..70e8fad --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverter.java @@ -0,0 +1,375 @@ +package com.aliyun.dataworks.migrationx.domain.dataworks.service.converter; + +import java.nio.file.Paths; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.aliyun.dataworks.common.spec.domain.DataWorksWorkflowSpec; +import com.aliyun.dataworks.common.spec.domain.SpecRefEntity; +import com.aliyun.dataworks.common.spec.domain.Specification; +import com.aliyun.dataworks.common.spec.domain.dw.nodemodel.DataWorksNodeAdapter; +import com.aliyun.dataworks.common.spec.domain.dw.nodemodel.DwNodeDependentTypeInfo; +import com.aliyun.dataworks.common.spec.domain.dw.nodemodel.OutputContext; +import com.aliyun.dataworks.common.spec.domain.enums.ArtifactType; +import com.aliyun.dataworks.common.spec.domain.enums.NodeInstanceModeType; +import com.aliyun.dataworks.common.spec.domain.enums.NodeRerunModeType; +import com.aliyun.dataworks.common.spec.domain.enums.SpecKind; +import com.aliyun.dataworks.common.spec.domain.ref.SpecArtifact; +import com.aliyun.dataworks.common.spec.domain.ref.SpecDatasource; +import com.aliyun.dataworks.common.spec.domain.ref.SpecFile; +import com.aliyun.dataworks.common.spec.domain.ref.SpecNode; +import com.aliyun.dataworks.common.spec.domain.ref.SpecNodeOutput; +import com.aliyun.dataworks.common.spec.domain.ref.SpecRuntimeResource; +import com.aliyun.dataworks.common.spec.domain.ref.SpecScript; +import com.aliyun.dataworks.common.spec.domain.ref.SpecTrigger; +import com.aliyun.dataworks.common.spec.domain.ref.file.SpecLocalFile; +import com.aliyun.dataworks.common.spec.domain.ref.runtime.SpecScriptRuntime; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.File; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.FileDetail; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.FileNodeCfg; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.FileNodeInputOutput; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.FileNodeInputOutputContext; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.types.IoParseType; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.types.NodeUseType; +import com.aliyun.dataworks.migrationx.domain.dataworks.utils.CronExpressUtil; +import com.aliyun.migrationx.common.utils.DateUtils; +import com.google.common.base.Joiner; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; + +/** + * DataWorks SpecNode 转化为 DataWorks对象 + * + * @author 戒迷 + * @date 2024/4/16 + */ +@Slf4j +public class DataWorksSpecNodeConverter { + private DataWorksSpecNodeConverter() { + throw new IllegalStateException("Utility class"); + } + + public static FileDetail functionSpecToFileDetail(Specification spec, String resourceId) { + FileDetail fileDetail = new FileDetail(); + File file = functionSpecToFile(spec, resourceId); + fileDetail.setFile(file); + fileDetail.setNodeCfg(initFileNodeCfgByFile(file)); + return fileDetail; + } + + public static FileDetail resourceSpecToFileDetail(Specification spec, String resourceId) { + FileDetail fileDetail = new FileDetail(); + File file = resourceSpecToFile(spec, resourceId); + fileDetail.setFile(file); + fileDetail.setNodeCfg(initFileNodeCfgByFile(file)); + return fileDetail; + } + + private static FileNodeCfg initFileNodeCfgByFile(File file) { + FileNodeCfg fileNodeCfg = new FileNodeCfg(); + fileNodeCfg.setNodeName(file.getFileName()); + fileNodeCfg.setNodeId(file.getFileId()); + return fileNodeCfg; + } + + public static FileDetail resourceSpecToFileDetail(Specification spec) { + return resourceSpecToFileDetail(spec, null); + } + + public static FileDetail functionSpecToFileDetail(Specification spec) { + return functionSpecToFileDetail(spec, null); + } + + private static File functionSpecToFile(Specification spec, String functionId) { + DataWorksWorkflowSpec dataWorksWorkflowSpec = spec.getSpec(); + if (spec.getSpec() == null) { + log.warn("dataworks resource spec is null"); + return null; + } + + return ListUtils.emptyIfNull(dataWorksWorkflowSpec.getFunctions()).stream() + .filter(x -> StringUtils.isBlank(functionId) || StringUtils.equals(x.getId(), functionId)) + .findFirst() + .map(specFunc -> { + File dwFunc = new File(); + dwFunc.setFileName(specFunc.getName()); + dwFunc.setOwner(Optional.ofNullable(specFunc.getMetadata()).map(m -> (String)m.get("owner")).orElse(null)); + dwFunc.setFileTypeStr(Optional.ofNullable(specFunc.getScript()).map(SpecScript::getRuntime).map(SpecScriptRuntime::getCommand) + .orElse(null)); + dwFunc.setConnName(Optional.ofNullable(specFunc.getDatasource()).map(SpecDatasource::getName).orElse(null)); + return dwFunc; + }).orElse(null); + } + + private static File resourceSpecToFile(Specification spec, String resourceId) { + DataWorksWorkflowSpec dataWorksWorkflowSpec = spec.getSpec(); + if (spec.getSpec() == null) { + log.warn("dataworks resource spec is null"); + return null; + } + + return ListUtils.emptyIfNull(dataWorksWorkflowSpec.getFileResources()).stream() + .filter(x -> StringUtils.isBlank(resourceId) || StringUtils.equals(x.getId(), resourceId)) + .findFirst() + .map(specRes -> { + File dwRes = new File(); + dwRes.setFileName(specRes.getName()); + dwRes.setOwner(Optional.ofNullable(specRes.getMetadata()).map(m -> (String)m.get("owner")).orElse(null)); + String fileName = Optional.ofNullable(specRes.getFile()).filter(SpecLocalFile.class::isInstance).map(f -> (SpecLocalFile)f) + .map(f -> Paths.get(f.getPath()).toFile().getName()).orElse(specRes.getName()); + dwRes.setFileTypeStr(Optional.ofNullable(specRes.getScript()).map(SpecScript::getRuntime).map(SpecScriptRuntime::getCommand) + .orElse(null)); + dwRes.setOriginResourceName(fileName); + dwRes.setConnName(Optional.ofNullable(specRes.getDatasource()).map(SpecDatasource::getName).orElse(null)); + return dwRes; + }).orElse(null); + } + + public static FileDetail nodeSpecToFileDetail(Specification spec, String nodeId) { + FileDetail fileDetail = new FileDetail(); + fileDetail.setFile(nodeSpecToFile(spec, nodeId)); + fileDetail.setNodeCfg(nodeSpecToNodeCfg(spec, nodeId)); + return fileDetail; + } + + public static FileDetail nodeSpecToFileDetail(Specification spec) { + FileDetail fileDetail = new FileDetail(); + fileDetail.setFile(nodeSpecToFile(spec, null)); + fileDetail.setNodeCfg(nodeSpecToNodeCfg(spec, null)); + return fileDetail; + } + + public static File nodeSpecToFile(Specification spec, String nodeId) { + DataWorksWorkflowSpec dataWorksWorkflowSpec = spec.getSpec(); + if (spec.getSpec() == null) { + log.warn("dataworks workflow spec is null"); + return null; + } + + return Optional.ofNullable(getMatchSpecNode(dataWorksWorkflowSpec, nodeId)).map(specNode -> { + File file = new File(); + file.setAppId(null); + file.setBizId(null); + file.setCloudUuid(null); + file.setCommitStatus(null); + file.setConnName(Optional.ofNullable(specNode.getDatasource()).map(SpecDatasource::getName).orElse(null)); + file.setContent(Optional.ofNullable(specNode.getScript()).map(SpecScript::getContent).orElse(null)); + file.setCreateTime(null); + file.setCreateUser(null); + file.setCurrentVersion(null); + file.setExtend(null); + file.setExtraContent(null); + file.setFileDagUrl(null); + file.setFileDelete(null); + file.setFileDesc(specNode.getDescription()); + file.setFileFolderId(null); + file.setFileFolderPath(Optional.ofNullable(specNode.getScript()).map(SpecFile::getPath).orElse(null)); + file.setFileId(Long.valueOf(specNode.getId())); + file.setFileLockStatus(null); + file.setFileLockUser(null); + file.setFileLockUserName(null); + file.setFileName(specNode.getName()); + file.setFilePublish(null); + file.setFileTypeStr(Optional.ofNullable(specNode.getScript()) + .map(SpecScript::getRuntime).map(SpecScriptRuntime::getCommand).orElse(null)); + file.setGalaxyResultTableSql(null); + file.setGalaxySourceTableSql(null); + file.setGalaxyTaskConfig(null); + file.setInstanceInfo(null); + file.setIsAutoParse(null); + file.setIsLarge(null); + file.setIsOdps(null); + file.setIsProtected(null); + file.setLabelId(null); + file.setLastEditTime(null); + file.setLastEditUser(null); + file.setLastEditUserName(null); + file.setLimit(null); + file.setLocked(null); + file.setLockedBy(null); + file.setLockedByName(null); + file.setNodeId(null); + file.setOriginResourceName(null); + file.setOwner(specNode.getOwner()); + file.setOwnerName(null); + file.setParentId(null); + file.setParentType(null); + file.setPosition(null); + file.setReference(null); + file.setRegion(null); + file.setSourceApp(null); + file.setStart(null); + file.setTenantId(null); + file.setTtContent(null); + if (SpecKind.CYCLE_WORKFLOW.getLabel().equals(spec.getKind())) { + file.setUseType(NodeUseType.SCHEDULED.getValue()); + } else if (SpecKind.MANUAL_WORKFLOW.getLabel().equals(spec.getKind())) { + file.setUseType(NodeUseType.MANUAL_WORKFLOW.getValue()); + } else if (SpecKind.TEMPORARY_WORKFLOW.getLabel().equals(spec.getKind())) { + file.setUseType(NodeUseType.MANUAL.getValue()); + } + file.setWorkspaceUrl(null); + file.setIgnoreLock(null); + + return file; + }).orElse(null); + } + + public static SpecNode getMatchSpecNode(DataWorksWorkflowSpec dataWorksWorkflowSpec, String nodeId) { + for (SpecNode node : dataWorksWorkflowSpec.getNodes()) { + if (StringUtils.isBlank(nodeId) || StringUtils.equalsIgnoreCase(node.getId(), nodeId)) { + return node; + } + for (SpecNode innerNode : node.getInnerNodes()) { + if (StringUtils.isBlank(nodeId) || StringUtils.equalsIgnoreCase(innerNode.getId(), nodeId)) { + return innerNode; + } + } + } + return null; + } + + /** + * 处理Node类型的Spec + * + * @param spec + * @param nodeId + * @return + */ + public static FileNodeCfg nodeSpecToNodeCfg(Specification spec, String nodeId) { + DataWorksWorkflowSpec dataWorksWorkflowSpec = spec.getSpec(); + if (spec.getSpec() == null) { + log.warn("dataworks workflow spec is null"); + return null; + } + + return Optional.ofNullable(getMatchSpecNode(dataWorksWorkflowSpec, nodeId)).map(specNode -> { + FileNodeCfg nodeCfg = new FileNodeCfg(); + nodeCfg.setAppId(null); + nodeCfg.setBaselineId(null); + nodeCfg.setCreateTime(null); + nodeCfg.setCreateUser(null); + nodeCfg.setCronExpress(Optional.ofNullable(specNode.getTrigger()).map(SpecTrigger::getCron).orElse(null)); + nodeCfg.setCycleType(CronExpressUtil.parseCronToCycleType(nodeCfg.getCronExpress())); + nodeCfg.setDataxFileId(null); + nodeCfg.setDataxFileVersion(null); + + nodeCfg.setDependentType(0); + nodeCfg.setDescription(specNode.getDescription()); + nodeCfg.setEndEffectDate(Optional.ofNullable(specNode.getTrigger()).map(SpecTrigger::getEndTime) + .map(DateUtils::convertStringToDate).orElse(null)); + nodeCfg.setFileId(Optional.ofNullable(specNode.getId()).map(Long::valueOf).orElse(null)); + + nodeCfg.setIsAutoParse(null); + nodeCfg.setIsStop(null); + nodeCfg.setLastModifyTime(null); + nodeCfg.setLastModifyUser(null); + nodeCfg.setMultiinstCheckType(null); + nodeCfg.setNodeId(Long.valueOf(specNode.getId())); + nodeCfg.setNodeName(specNode.getName()); + nodeCfg.setOwner(specNode.getOwner()); + nodeCfg.setPriority(specNode.getPriority()); + nodeCfg.setResgroupId(Optional.ofNullable(specNode.getRuntimeResource()).map(SpecRuntimeResource::getResourceGroupId) + .map(Long::valueOf).orElse(null)); + nodeCfg.setStartEffectDate(Optional.ofNullable(specNode.getTrigger()).map(SpecTrigger::getStartTime) + .map(DateUtils::convertStringToDate).orElse(null)); + nodeCfg.setStartRightNow(Optional.ofNullable(specNode.getInstanceMode()) + .map(instanceMode -> instanceMode == NodeInstanceModeType.IMMEDIATELY) + .orElse(false)); + nodeCfg.setTaskRerunInterval(specNode.getRerunInterval()); + nodeCfg.setTaskRerunTime(specNode.getRerunTimes()); + + setRerunMode(specNode, nodeCfg); + setInputOutputList(specNode, nodeCfg); + setByAdaptor(spec, specNode, nodeCfg); + return nodeCfg; + }).orElse(null); + } + + private static void setRerunMode(SpecNode specNode, FileNodeCfg nodeCfg) { + if (null == specNode.getRerunMode() || NodeRerunModeType.ALL_ALLOWED == specNode.getRerunMode()) { + nodeCfg.setReRunAble(1); + } else if (NodeRerunModeType.ALL_DENIED == specNode.getRerunMode()) { + nodeCfg.setReRunAble(2); + } else if (NodeRerunModeType.FAILURE_ALLOWED == specNode.getRerunMode()) { + nodeCfg.setReRunAble(0); + } + } + + private static void setInputOutputList(SpecNode specNode, FileNodeCfg nodeCfg) { + nodeCfg.setInputList(ListUtils.emptyIfNull(specNode.getInputs()).stream() + .filter(SpecArtifact.class::isInstance) + .map(io -> (SpecArtifact)io) + .filter(io -> Objects.equals(io.getArtifactType(), ArtifactType.NODE_OUTPUT)) + .map(io -> (SpecNodeOutput)io) + .map(io -> { + FileNodeInputOutput in = new FileNodeInputOutput(); + in.setStr(io.getData()); + in.setParseType(IoParseType.MANUAL.getCode()); + in.setRefTableName(io.getRefTableName()); + return in; + }).collect(Collectors.toList())); + nodeCfg.setInputByInputList(); + + nodeCfg.setOutputList(ListUtils.emptyIfNull(specNode.getOutputs()).stream() + .filter(SpecArtifact.class::isInstance) + .map(io -> (SpecArtifact)io) + .filter(io -> Objects.equals(io.getArtifactType(), ArtifactType.NODE_OUTPUT)) + .map(io -> (SpecNodeOutput)io) + .map(io -> { + FileNodeInputOutput out = new FileNodeInputOutput(); + out.setStr(io.getData()); + out.setParseType(IoParseType.MANUAL.getCode()); + out.setRefTableName(io.getRefTableName()); + return out; + }).collect(Collectors.toList())); + nodeCfg.setOutputByOutputList(); + } + + private static void setByAdaptor(Specification spec, SpecNode specNode, FileNodeCfg nodeCfg) { + DataWorksNodeAdapter adapter = new DataWorksNodeAdapter(spec, specNode); + + ListUtils.emptyIfNull(spec.getSpec().getFlow()).stream() + .filter(f -> StringUtils.equals(specNode.getId(), Optional.ofNullable(f.getNodeId()).map(SpecRefEntity::getId).orElse(null))) + .findFirst().ifPresent(flow -> { + DwNodeDependentTypeInfo depInfo = adapter.getDependentType(list -> null); + nodeCfg.setDependentType(depInfo.getDependentType()); + if (CollectionUtils.isNotEmpty(depInfo.getDependentNodeOutputList())) { + nodeCfg.setDependentDataNode(Joiner.on(",").join(depInfo.getDependentNodeOutputList())); + } + }); + + nodeCfg.setInputContextList(ListUtils.emptyIfNull(adapter.getInputContexts()).stream().map(ctx -> { + FileNodeInputOutputContext nc = new FileNodeInputOutputContext(); + nc.setType(0); // input ctx + nc.setParamName(ctx.getKey()); + nc.setParseType(IoParseType.MANUAL.getCode()); + nc.setParamValue(ctx.getRefKey()); + return nc; + }).collect(Collectors.toList())); + nodeCfg.setOutputContextList(ListUtils.emptyIfNull(adapter.getOutputContexts()).stream().map(ctx -> { + FileNodeInputOutputContext nc = new FileNodeInputOutputContext(); + nc.setType(1); // output ctx + nc.setParamName(ctx.getKey()); + nc.setParseType(IoParseType.MANUAL.getCode()); + nc.setParamValue(ctx.getValueExpr()); + + if (StringUtils.equalsIgnoreCase(OutputContext.CTX_TYPE_CONST, ctx.getCtxType())) { + nc.setParamType(1); + } else if (StringUtils.equalsIgnoreCase(OutputContext.CTX_TYPE_CONST_SYSTEM_VARIABLE, ctx.getCtxType()) + || StringUtils.equalsIgnoreCase(OutputContext.CTX_TYPE_SCRIPT_OUTPUTS, ctx.getCtxType())) { + nc.setParamType(2); + nc.setParseType(IoParseType.SYSTEM.getCode()); + } else if (StringUtils.equalsIgnoreCase(OutputContext.CTX_TYPE_PARAMETER_NODE_OUTPUTS, ctx.getCtxType())) { + nc.setParamType(3); + } + return nc; + }).collect(Collectors.toList())); + nodeCfg.setParaValue(adapter.getParaValue()); + nodeCfg.setExtConfig(adapter.getExtConfig()); + } + +} diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/utils/CronExpressUtil.java b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/utils/CronExpressUtil.java index 1a40979..4ec9e0b 100644 --- a/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/utils/CronExpressUtil.java +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/utils/CronExpressUtil.java @@ -15,6 +15,7 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.utils; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.types.CycleType; import com.aliyun.dataworks.migrationx.domain.dataworks.utils.quartz.CronExpression; import com.aliyun.dataworks.migrationx.domain.dataworks.utils.quartz.ExtendedQuartzCronExpression; import com.google.common.base.Joiner; @@ -25,6 +26,8 @@ import java.text.ParseException; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -344,4 +347,32 @@ private static String normalizePart(String part) { } return part; } + + + public static Integer parseCronToCycleType(String cronExpression) { + if (StringUtils.isBlank(cronExpression) || "day".equalsIgnoreCase(cronExpression)) { + return CycleType.DAY.getCode(); + } + + try { + String[] cronExp = cronExpression.split("\\s+"); + String pattern = "[-/,*]"; + Pattern regex = Pattern.compile(pattern); + + String minute = cronExp[1]; + String hour = cronExp[2]; + + Matcher matchM = regex.matcher(minute); + Matcher matchH = regex.matcher(hour); + + if (matchM.find() || matchH.find()) { + //计算得到的小时调度和分钟调度才重新算cycleType + return CycleType.NOT_DAY.getCode(); + } else { + return CycleType.DAY.getCode(); + } + } catch (Exception e) { + return CycleType.DAY.getCode(); + } + } } diff --git a/client/migrationx-domain/migrationx-domain-dataworks/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverterTest.java b/client/migrationx-domain/migrationx-domain-dataworks/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverterTest.java new file mode 100644 index 0000000..29c4f30 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dataworks/src/test/java/com/aliyun/dataworks/migrationx/domain/dataworks/service/converter/DataWorksSpecNodeConverterTest.java @@ -0,0 +1,198 @@ +package com.aliyun.dataworks.migrationx.domain.dataworks.service.converter; + +import com.aliyun.dataworks.common.spec.SpecUtil; +import com.aliyun.dataworks.common.spec.domain.DataWorksWorkflowSpec; +import com.aliyun.dataworks.common.spec.domain.Specification; +import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.client.FileDetail; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author 戒迷 + * @date 2024/4/16 + */ +public class DataWorksSpecNodeConverterTest { + + @Test + public void testHandleNodeSpec() throws Exception { + String specStr = "{\n" + + "\t\"version\":\"1.1.0\",\n" + + "\t\"kind\":\"CycleWorkflow\",\n" + + "\t\"spec\":{\n" + + "\t\t\"nodes\":[\n" + + "\t\t\t{\n" + + "\t\t\t\t\"recurrence\":\"Normal\",\n" + + "\t\t\t\t\"id\":\"26248077\",\n" + + "\t\t\t\t\"timeout\":0,\n" + + "\t\t\t\t\"instanceMode\":\"T+1\",\n" + + "\t\t\t\t\"rerunMode\":\"Allowed\",\n" + + "\t\t\t\t\"rerunTimes\":0,\n" + + "\t\t\t\t\"rerunInterval\":120000,\n" + + "\t\t\t\t\"datasource\":{\n" + + "\t\t\t\t\t\"name\":\"odps_first\",\n" + + "\t\t\t\t\t\"type\":\"odps\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"script\":{\n" + + "\t\t\t\t\t\"path\":\"业务流程/建模引擎/MaxCompute/数据开发/config_driver数据同步/model_table\",\n" + + "\t\t\t\t\t\"runtime\":{\n" + + "\t\t\t\t\t\t\"command\":\"ODPS_SQL\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t\"parameters\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"name\":\"bizdate\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"Variable\",\n" + + "\t\t\t\t\t\t\t\"scope\":\"NodeParameter\",\n" + + "\t\t\t\t\t\t\t\"type\":\"System\",\n" + + "\t\t\t\t\t\t\t\"value\":\"$[yyyymmdd-1]\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"trigger\":{\n" + + "\t\t\t\t\t\"type\":\"Scheduler\",\n" + + "\t\t\t\t\t\"cron\":\"00 29 00 * * ?\",\n" + + "\t\t\t\t\t\"startTime\":\"1970-01-01 00:00:00\",\n" + + "\t\t\t\t\t\"endTime\":\"9999-01-01 15:12:51\",\n" + + "\t\t\t\t\t\"timezone\":\"Asia/Shanghai\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"runtimeResource\":{\n" + + "\t\t\t\t\t\"resourceGroup\":\"group_20051853\",\n" + + "\t\t\t\t\t\"resourceGroupId\":\"20051853\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"name\":\"model_table\",\n" + + "\t\t\t\t\"owner\":\"370260\",\n" + + "\t\t\t\t\"inputs\":{\n" + + "\t\t\t\t\t\"nodeOutputs\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"data\":\"dataworks_meta.dwd_base_config_driver_data_jsondata_df\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"NodeOutput\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"outputs\":{\n" + + "\t\t\t\t\t\"nodeOutputs\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"data\":\"dataworks_analyze.26248077_out\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"NodeOutput\"\n" + + "\t\t\t\t\t\t},\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"data\":\"dataworks_analyze.model_table_config_driver\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"NodeOutput\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"flow\":[\n" + + "\t\t\t{\n" + + "\t\t\t\t\"nodeId\":\"26248077\",\n" + + "\t\t\t\t\"depends\":[\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"type\":\"Normal\",\n" + + "\t\t\t\t\t\t\"output\":\"dataworks_meta.dwd_base_config_driver_data_jsondata_df\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t]\n" + + "\t\t\t}\n" + + "\t\t]\n" + + "\t}\n" + + "}"; + Specification spec = SpecUtil.parseToDomain(specStr); + FileDetail result = DataWorksSpecNodeConverter.nodeSpecToFileDetail(spec); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getFile()); + Assert.assertNotNull(result.getNodeCfg()); + } + + @Test + public void testHandleResourceSpec() throws Exception { + String specStr = "{\n" + + " \"version\":\"1.1.0\",\n" + + " \"kind\":\"CycleWorkflow\",\n" + + " \"spec\":{\n" + + " \"fileResources\":[\n" + + " {\n" + + " \"name\":\"mc.py\",\n" + + " \"id\":\"6300484767235409791\",\n" + + " \"script\":{\n" + + " \"path\":\"戒迷/资源/mc.py\",\n" + + " \"runtime\":{\n" + + " \"command\":\"ODPS_PYTHON\"\n" + + " }\n" + + " },\n" + + " \"runtimeResource\":{\n" + + " \"id\":\"5623679673296125496\",\n" + + " \"resourceGroup\":\"group_2\",\n" + + " \"resourceGroupId\":\"2\"\n" + + " },\n" + + " \"type\":\"python\",\n" + + " \"file\":{\n" + + " \"storage\":{\n" + + " \"type\": \"oss\"\n" + + " }\n" + + " },\n" + + " \"datasource\":{\n" + + " \"name\":\"odps_first\",\n" + + " \"type\":\"odps\"\n" + + " },\n" + + " \"metadata\":{\n" + + " \"owner\":\"370260\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Specification spec = SpecUtil.parseToDomain(specStr); + FileDetail result = DataWorksSpecNodeConverter.resourceSpecToFileDetail(spec); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getFile()); + Assert.assertNotNull(result.getNodeCfg()); + } + + @Test + public void testHandleFunction() throws Exception { + String specStr = "{\n" + + " \"version\":\"1.1.0\",\n" + + " \"kind\":\"CycleWorkflow\",\n" + + " \"spec\":{\n" + + " \"functions\":[\n" + + " {\n" + + " \"name\":\"odps_function\",\n" + + " \"id\":\"6615080895197716196\",\n" + + " \"script\":{\n" + + " \"path\":\"戒迷/函数/odps_function\",\n" + + " \"runtime\":{\n" + + " \"command\":\"ODPS_FUNCTION\"\n" + + " }\n" + + " },\n" + + " \"type\":\"other\",\n" + + " \"className\":\"main\",\n" + + " \"datasource\":{\n" + + " \"name\":\"odps_first\",\n" + + " \"type\":\"odps\"\n" + + " },\n" + + " \"runtimeResource\":{\n" + + " \"resourceGroup\":\"group_2\",\n" + + " \"id\":\"5623679673296125496\",\n" + + " \"resourceGroupId\":\"2\"\n" + + " },\n" + + " \"resourceType\":\"file\",\n" + + " \"metadata\":{\n" + + " \"owner\":\"370260\"\n" + + " },\n" + + " \"fileResources\":[\n" + + " {\n" + + " \"name\":\"mc.py\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + Specification spec = SpecUtil.parseToDomain(specStr); + FileDetail result = DataWorksSpecNodeConverter.functionSpecToFileDetail(spec); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getFile()); + Assert.assertNotNull(result.getNodeCfg()); + } +} + +// Generated with love by TestMe :) Please report issues and submit feature requests at: http://weirddev.com/forum#!/testme \ No newline at end of file diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/pom.xml b/client/migrationx-domain/migrationx-domain-dolphinscheduler/pom.xml index e409dba..95e3180 100644 --- a/client/migrationx-domain/migrationx-domain-dolphinscheduler/pom.xml +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -35,7 +35,7 @@ com.aliyun.dataworks migrationx-domain-core - 1.1.4 + 1.1.5 com.fasterxml.jackson.core diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/Project.java b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/Project.java index fe55eb8..76d5d9e 100644 --- a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/Project.java +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/Project.java @@ -32,5 +32,6 @@ public class Project { private Integer id; private String name; private Integer userId; + private String code; private String description; } diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerApiService.java b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerApiService.java index e7c2999..6a4e7f6 100644 --- a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerApiService.java +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerApiService.java @@ -15,6 +15,18 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import com.aliyun.migrationx.common.http.HttpClientUtil; import com.aliyun.migrationx.common.utils.GsonUtils; import com.google.common.base.Joiner; @@ -31,18 +43,6 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - /** * @author 聿剑 * @date 2022/10/20 @@ -155,8 +155,7 @@ public Response> queryAllProjectList(DolphinSchedulerRequest re HttpClientUtil client = new HttpClientUtil(); HttpGet httpGet = new HttpGet(); httpGet.setHeader("token", token); - String url = MessageFormat.format("{0}/dolphinscheduler/projects/query-project-list", - endpoint, request.getPageNo(), request.getPageSize()); + String url = MessageFormat.format("{0}/dolphinscheduler/projects/query-project-list", endpoint); httpGet.setURI(new URI(url)); String responseStr = client.executeAndGet(httpGet); return GsonUtils.fromJsonString(responseStr, new TypeToken>>() {}.getType()); diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerProjectRequest.java b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerProjectRequest.java index b829685..ed5843f 100644 --- a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerProjectRequest.java +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DolphinSchedulerProjectRequest.java @@ -15,13 +15,13 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1; +import javax.validation.constraints.NotBlank; + import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.experimental.Accessors; -import javax.validation.constraints.NotBlank; - /** * @author 聿剑 * @date 2022/10/20 @@ -33,4 +33,5 @@ public class DolphinSchedulerProjectRequest extends DolphinSchedulerRequest { @NotBlank(message = "dolphinscheduler project name needed") private String projectName; + private String projectCode; } diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DownloadResourceRequest.java b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DownloadResourceRequest.java index 18b206e..7b8349e 100644 --- a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DownloadResourceRequest.java +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v1/DownloadResourceRequest.java @@ -15,13 +15,13 @@ package com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1; +import javax.validation.constraints.NotNull; + import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.experimental.Accessors; -import javax.validation.constraints.NotNull; - /** * @author 聿剑 * @date 2022/10/20 @@ -33,4 +33,5 @@ public class DownloadResourceRequest extends DolphinSchedulerRequest { @NotNull(message = "resource id needed") private Integer id; + private String fullName; } diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v3/DolphinschedulerApiV3Service.java b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v3/DolphinschedulerApiV3Service.java new file mode 100644 index 0000000..6896ba5 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/main/java/com/aliyun/dataworks/migrationx/domain/dataworks/dolphinscheduler/v3/DolphinschedulerApiV3Service.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v3; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.BatchExportProcessDefinitionByIdsRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DolphinSchedulerApi; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DolphinSchedulerRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DownloadResourceRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.PaginateResponse; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryDataSourceListByPaginateRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryProcessDefinitionByPaginateRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryResourceListRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryUdfFuncListByPaginateRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.Response; +import com.aliyun.migrationx.common.http.HttpClientUtil; +import com.aliyun.migrationx.common.utils.GsonUtils; +import com.google.common.base.Joiner; +import com.google.common.reflect.TypeToken; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.util.EntityUtils; + +/** + * V3 Dolphinscheduler Api Implementations + * + * @author 聿剑 + * @date 2024/4/20 + */ +@Slf4j +public class DolphinschedulerApiV3Service implements DolphinSchedulerApi { + private static final String HEADER_TOKEN = "token"; + private final String endpoint; + private final String token; + + public DolphinschedulerApiV3Service(String endpoint, String token) { + this.endpoint = endpoint; + this.token = token; + } + + private HttpGet newHttpGet(String url) throws URISyntaxException { + HttpGet httpGet = new HttpGet(); + httpGet.setHeader(HEADER_TOKEN, token); + String finalUrl = MessageFormat.format("{0}/dolphinscheduler/{1}", endpoint, url); + httpGet.setURI(new URI(finalUrl)); + return httpGet; + } + + private HttpPost newHttpPost(String url) throws URISyntaxException { + HttpPost httpPost = new HttpPost(); + httpPost.setHeader(HEADER_TOKEN, token); + String finalUrl = MessageFormat.format("{0}/dolphinscheduler/{1}", endpoint, url); + httpPost.setURI(new URI(finalUrl)); + return httpPost; + } + + @Override + public PaginateResponse queryProcessDefinitionByPaging(QueryProcessDefinitionByPaginateRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("projects/{0}/process-definition/list&pageNo={1}&pageSize={2}", + request.getProjectCode(), request.getPageNo(), request.getPageSize()); + String responseStr = client.executeAndGet(newHttpGet(url)); + return GsonUtils.fromJsonString(responseStr, new TypeToken>() {}.getType()); + } + + @Override + public String batchExportProcessDefinitionByIds(BatchExportProcessDefinitionByIdsRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("projects/{0}/process-definition/batch-export?codes={1}", + request.getProjectCode(), + Joiner.on(",").join(ListUtils.emptyIfNull(request.getIds()).stream().distinct().collect(Collectors.toList()))); + return client.executeAndGet(newHttpPost(url)); + } + + @Override + public Response> queryResourceList(QueryResourceListRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("resources/query-by-type?type={0}", request.getType()); + HttpGet httpGet = newHttpGet(url); + String responseStr = client.executeAndGet(httpGet); + return GsonUtils.fromJsonString(responseStr, new TypeToken>>() {}.getType()); + } + + private String getSuggestedFileName(Header contentDispositionHeader) { + String value = contentDispositionHeader.getValue(); + return Arrays.stream(StringUtils.split(value, ";")) + .map(StringUtils::trim) + .filter(token -> StringUtils.startsWithIgnoreCase(token, "filename=")) + .findFirst() + .map(fileNamePart -> StringUtils.replace(fileNamePart, "filename=", "")) + .map(fileName -> RegExUtils.replaceAll(fileName, "^\"", "")) + .map(fileName -> RegExUtils.replaceAll(fileName, "\"$", "")) + .orElse(null); + } + + @Override + public File downloadResource(DownloadResourceRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("resources/download?fullName={0}", request.getFullName()); + HttpGet httpGet = newHttpGet(url); + HttpResponse resp = client.executeAndGetHttpResponse(httpGet); + InputStream inputStream = resp.getEntity().getContent(); + String fileName = Stream.of(resp.getAllHeaders()) + .filter(header -> StringUtils.equalsIgnoreCase(header.getName(), "Content-Disposition")) + .findFirst() + .map(this::getSuggestedFileName) + .orElse(null); + + if (StringUtils.isBlank(fileName)) { + String content = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); + Response response = GsonUtils.fromJsonString(content, new TypeToken>() {}.getType()); + log.warn("download resource id: {} failed: {}", + request.getId(), Optional.ofNullable(response).map(Response::getMsg).orElse(content)); + return null; + } + + File tmpFile = new File(FileUtils.getTempDirectory(), fileName); + FileOutputStream fileOutputStream = new FileOutputStream(tmpFile); + IOUtils.copy(inputStream, fileOutputStream); + return tmpFile; + } + + @Override + public PaginateResponse queryUdfFuncListByPaging(QueryUdfFuncListByPaginateRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("resources/udf-func?pageNo={0}&pageSize={1}", request.getPageNo(), request.getPageSize()); + HttpGet httpGet = newHttpGet(url); + String responseStr = client.executeAndGet(httpGet); + return GsonUtils.fromJsonString(responseStr, new TypeToken>() {}.getType()); + } + + @Override + public PaginateResponse queryDataSourceListByPaging(QueryDataSourceListByPaginateRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = MessageFormat.format("datasources/list-paging?pageNo={0}&pageSize={1}", request.getPageNo(), request.getPageSize()); + HttpGet httpGet = newHttpGet(url); + String responseStr = client.executeAndGet(httpGet); + return GsonUtils.fromJsonString(responseStr, new TypeToken>() {}.getType()); + } + + @Override + public Response> queryAllProjectList(DolphinSchedulerRequest request) throws Exception { + HttpClientUtil client = new HttpClientUtil(); + String url = "projects/list"; + HttpGet httpGet = newHttpGet(url); + String responseStr = client.executeAndGet(httpGet); + return GsonUtils.fromJsonString(responseStr, new TypeToken>>() {}.getType()); + } +} diff --git a/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/test/http/v3/process-definitions.http b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/test/http/v3/process-definitions.http new file mode 100644 index 0000000..d3bab55 --- /dev/null +++ b/client/migrationx-domain/migrationx-domain-dolphinscheduler/src/test/http/v3/process-definitions.http @@ -0,0 +1,29 @@ +### listProcessDefinitions +GET /dolphinscheduler/projects/{{projectCode}}/process-definition/list HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} + +### batchExportProcessDefinitions +POST /dolphinscheduler/projects/{{projectCode}}/process-definition/batch-export?codes=13198469779680 HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} + +### queryResourceList +GET /dolphinscheduler/resources/query-by-type?type=FILE HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} + +### queryProjectListPaging +GET /dolphinscheduler/projects?pageNo=1&pageSize=10 HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} + +### queryProjectList +GET /dolphinscheduler/projects/list HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} + +### getProcessDefinition +GET /dolphinscheduler/projects/{{projectCode}}/process-definition/13198469779680 HTTP/1.1 +Host: {{api_endpoint}} +token: {{token}} diff --git a/client/migrationx-domain/migrationx-domain-oozie/pom.xml b/client/migrationx-domain/migrationx-domain-oozie/pom.xml index 0bcaad1..62ccc86 100644 --- a/client/migrationx-domain/migrationx-domain-oozie/pom.xml +++ b/client/migrationx-domain/migrationx-domain-oozie/pom.xml @@ -20,7 +20,7 @@ migrationx-domain com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 diff --git a/client/migrationx-domain/pom.xml b/client/migrationx-domain/pom.xml index 64b76eb..0d6e00a 100644 --- a/client/migrationx-domain/pom.xml +++ b/client/migrationx-domain/pom.xml @@ -23,7 +23,7 @@ migrationx com.aliyun.dataworks - 1.1.4 + 1.1.5 migrationx-domain @@ -63,7 +63,7 @@ com.aliyun.dataworks migrationx-common - 1.1.4 + 1.1.5 commons-io diff --git a/client/migrationx-reader/pom.xml b/client/migrationx-reader/pom.xml index c3a9959..b49ccfb 100644 --- a/client/migrationx-reader/pom.xml +++ b/client/migrationx-reader/pom.xml @@ -20,7 +20,7 @@ migrationx com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -37,22 +37,22 @@ com.aliyun.dataworks migrationx-domain-core - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-aliyunemr - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-airflow - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-dolphinscheduler - 1.1.4 + 1.1.5 com.google.guava @@ -61,7 +61,7 @@ com.aliyun.dataworks migrationx-common - 1.1.4 + 1.1.5 commons-io diff --git a/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/aliyunemr/AliyunEmrCommandApp.java b/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/aliyunemr/AliyunEmrCommandApp.java index e103980..27116ba 100644 --- a/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/aliyunemr/AliyunEmrCommandApp.java +++ b/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/aliyunemr/AliyunEmrCommandApp.java @@ -15,27 +15,33 @@ package com.aliyun.dataworks.migrationx.reader.aliyunemr; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Objects; +import java.util.stream.Collectors; + import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.AliyunEmrExportRequest; import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.AliyunEmrExporterConstants; import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.AliyunEmrService; import com.aliyun.migrationx.common.command.appbase.CommandApp; import com.aliyun.migrationx.common.utils.ZipUtils; import com.aliyuncs.exceptions.ClientException; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.Objects; -import java.util.stream.Collectors; - /** * 阿里云EMR工作流导出工具 + * * @author sam.liux * @date 2019/06/27 */ @@ -56,6 +62,8 @@ public class AliyunEmrCommandApp extends CommandApp { private static final String OPT_HELP_LONG = "help"; private static final String OPT_PROJECTS = "p"; private static final String OPT_PROJECTS_LONG = "projects"; + private static final String OPT_FOLDER_FILTER = "ff"; + private static final String OPT_FOLDER_FILTER_LONG = "folderFilter"; public static void main(String[] args) throws ParseException, IOException, ClientException { Options options = new Options(); @@ -65,6 +73,7 @@ public static void main(String[] args) throws ParseException, IOException, Clien options.addOption(OPT_REGION_ID, OPT_REGION_ID_LONG, true, "region id, cn-shanghai etc."); options.addOption(OPT_PROJECTS, OPT_PROJECTS_LONG, true, "emr project, multiply projects separated by comma, prj_01,prj_02 etc."); options.addOption(OPT_EXPORT_FILE, OPT_EXPORT_DIR_LONG, true, "/home/admin/emr_dumps/aaa.zip etc."); + options.addOption(OPT_FOLDER_FILTER, OPT_FOLDER_FILTER_LONG, true, "/FLOW/folder1/folder2"); options.addOption(OPT_HELP, OPT_HELP_LONG, false, "show help."); CommandLineParser parser = new DefaultParser(); @@ -102,6 +111,7 @@ public static void main(String[] args) throws ParseException, IOException, Clien String regionId = cli.getOptionValue(OPT_REGION_ID); String projects = cli.getOptionValue(OPT_PROJECTS); String exportFile = cli.getOptionValue(OPT_EXPORT_FILE, new File("./emr_dumps/").getAbsolutePath()); + String folderFilter = cli.getOptionValue(OPT_FOLDER_FILTER); AliyunEmrService client = new AliyunEmrService(accessId, accessKey, endpoint, regionId); SimpleDateFormat dateTimeFormatter = new SimpleDateFormat(AliyunEmrExporterConstants.EXPORTER_OUTPUT_DIR_DATE_FORMAT); @@ -110,6 +120,7 @@ public static void main(String[] args) throws ParseException, IOException, Clien LOGGER.info("workspace folder: {}", folder); AliyunEmrExportRequest exportRequest = new AliyunEmrExportRequest(); exportRequest.setFolder(folder); + exportRequest.setFolderFilter(folderFilter); if (StringUtils.isNotBlank(projects)) { String[] tokens = StringUtils.split(projects, ","); if (tokens != null) { diff --git a/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/dolphinscheduler/DolphinSchedulerReader.java b/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/dolphinscheduler/DolphinSchedulerReader.java index 34eaa0d..5b8e4b4 100644 --- a/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/dolphinscheduler/DolphinSchedulerReader.java +++ b/client/migrationx-reader/src/main/java/com/aliyun/dataworks/migrationx/reader/dolphinscheduler/DolphinSchedulerReader.java @@ -15,7 +15,19 @@ package com.aliyun.dataworks.migrationx.reader.dolphinscheduler; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.Project; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.BatchExportProcessDefinitionByIdsRequest; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DolphinSchedulerApi; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DolphinSchedulerApiService; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DolphinSchedulerRequest; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.DownloadResourceRequest; @@ -26,11 +38,13 @@ import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryResourceListRequest; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.QueryUdfFuncListByPaginateRequest; import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v1.Response; +import com.aliyun.dataworks.migrationx.domain.dataworks.dolphinscheduler.v3.DolphinschedulerApiV3Service; import com.aliyun.migrationx.common.utils.GsonUtils; import com.aliyun.migrationx.common.utils.PaginateUtils; import com.aliyun.migrationx.common.utils.ZipUtils; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.MapUtils; @@ -40,16 +54,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; - /** * @author 聿剑 * @date 2022/10/19 */ +@Slf4j public class DolphinSchedulerReader { private static final Logger LOGGER = LoggerFactory.getLogger(DolphinSchedulerReader.class); private static final String PACKAGE_INFO_JSON = "package_info.json"; @@ -62,17 +71,25 @@ public class DolphinSchedulerReader { private static final String PROJECTS_JSON = "projects.json"; private final String version; - private List projects; + private final List projects; + private List projectInfoList = new ArrayList<>(); private final File exportFile; private Boolean skipResources = false; - private final DolphinSchedulerApiService dolphinSchedulerApiService; + private final DolphinSchedulerApi dolphinSchedulerApiService; public DolphinSchedulerReader(String endpoint, String token, String version, List projects, File exportFile) { this.version = version; this.projects = projects; this.exportFile = exportFile; - this.dolphinSchedulerApiService = new DolphinSchedulerApiService(endpoint, token); + if (StringUtils.startsWith(version, "1.") || StringUtils.startsWith(version, "2.")) { + this.dolphinSchedulerApiService = new DolphinSchedulerApiService(endpoint, token); + } else if (StringUtils.startsWith(version, "3.")) { + this.dolphinSchedulerApiService = new DolphinschedulerApiV3Service(endpoint, token); + } else { + throw new RuntimeException("unsupported dolphinscheduler version: " + version); + } + } public File export() throws Exception { @@ -115,7 +132,10 @@ private List queryProcessDefinitionByPage(PaginateUtils.Paginator p, QueryProcessDefinitionByPaginateRequest request = new QueryProcessDefinitionByPaginateRequest(); request.setPageNo(p.getPageNum()); request.setPageSize(p.getPageSize()); - request.setProjectName(project); + Project projectInfo = ListUtils.emptyIfNull(projectInfoList).stream() + .filter(prj -> StringUtils.equalsIgnoreCase(project, prj.getName())).findAny() + .orElseThrow(() -> new RuntimeException("project code not found by name: " + project)); + request.setProjectCode(projectInfo.getCode()); PaginateResponse response = dolphinSchedulerApiService.queryProcessDefinitionByPaging(request); return Optional.ofNullable(response) .map(Response::getData) @@ -126,7 +146,10 @@ private List queryProcessDefinitionByPage(PaginateUtils.Paginator p, private String batchExportProcessDefinitionByIds(List ids, String project) throws Exception { BatchExportProcessDefinitionByIdsRequest request = new BatchExportProcessDefinitionByIdsRequest(); request.setIds(ids); - request.setProjectName(project); + Project projectInfo = ListUtils.emptyIfNull(projectInfoList).stream() + .filter(prj -> StringUtils.equalsIgnoreCase(project, prj.getName())).findAny() + .orElseThrow(() -> new RuntimeException("project code not found by name: " + project)); + request.setProjectCode(projectInfo.getCode()); return dolphinSchedulerApiService.batchExportProcessDefinitionByIds(request); } @@ -163,8 +186,9 @@ private void exportProjects(File tmpDir) throws Exception { StringUtils.equalsIgnoreCase(prjName, proj.get("name").getAsString()))) .collect(Collectors.toList()); } else { - this.projects = ListUtils.emptyIfNull(projectsList).stream() - .map(proj -> proj.get("name").getAsString()) + this.projectInfoList = ListUtils.emptyIfNull(projectsList).stream() + .map(proj -> GsonUtils.fromJsonString(GsonUtils.toJsonString(proj), new TypeToken() {}.getType())) + .map(proj -> (Project)proj) .collect(Collectors.toList()); } @@ -188,15 +212,16 @@ private void exportDatasources(File tmpDir) throws InterruptedException { request.setPageNo(p.getPageNum()); request.setPageSize(p.getPageSize()); PaginateResponse response = dolphinSchedulerApiService.queryDataSourceListByPaging(request); + log.info("response: {}", response); FileUtils.writeStringToFile( new File(datasourceDir, "datasource_page_" + p.getPageNum() + ".json"), - GsonUtils.toJsonString(response.getData().getTotalList()), + GsonUtils.toJsonString(Optional.ofNullable(response).map(Response::getData).map(PaginateData::getTotalList).orElse(null)), StandardCharsets.UTF_8); PaginateUtils.PaginateResult paginateResult = new PaginateUtils.PaginateResult<>(); paginateResult.setPageNum(p.getPageNum()); paginateResult.setPageSize(p.getPageSize()); - paginateResult.setData(response.getData().getTotalList()); - paginateResult.setTotalCount(response.getData().getTotal()); + paginateResult.setData(Optional.ofNullable(response).map(Response::getData).map(PaginateData::getTotalList).orElse(null)); + paginateResult.setTotalCount(Optional.ofNullable(response).map(Response::getData).map(PaginateData::getTotal).orElse(0)); return paginateResult; } catch (Exception e) { throw new RuntimeException(e); diff --git a/client/migrationx-transformer/pom.xml b/client/migrationx-transformer/pom.xml index ed9d7b9..caae3b7 100644 --- a/client/migrationx-transformer/pom.xml +++ b/client/migrationx-transformer/pom.xml @@ -20,7 +20,7 @@ migrationx com.aliyun.dataworks - 1.1.4 + 1.1.5 4.0.0 @@ -38,48 +38,48 @@ com.aliyun.dataworks migrationx-domain-datago - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-dataworks - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-caiyunjian - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-azkaban - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-aliyunemr - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-airflow - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-oozie - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain-dolphinscheduler - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-domain pom - 1.1.4 + 1.1.5 @@ -93,7 +93,7 @@ com.aliyun.dataworks migrationx-common - 1.1.4 + 1.1.5 commons-io diff --git a/client/migrationx-transformer/src/main/java/com/aliyun/dataworks/migrationx/transformer/dataworks/converter/AliyunEmrWorkflowConverter.java b/client/migrationx-transformer/src/main/java/com/aliyun/dataworks/migrationx/transformer/dataworks/converter/AliyunEmrWorkflowConverter.java index 9ba0024..b54e6a5 100644 --- a/client/migrationx-transformer/src/main/java/com/aliyun/dataworks/migrationx/transformer/dataworks/converter/AliyunEmrWorkflowConverter.java +++ b/client/migrationx-transformer/src/main/java/com/aliyun/dataworks/migrationx/transformer/dataworks/converter/AliyunEmrWorkflowConverter.java @@ -37,6 +37,7 @@ import com.aliyun.dataworks.common.spec.domain.dw.types.ModelTreeRoot; import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.AliyunEmrProject; import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.AliyunEmrService; +import com.aliyun.dataworks.migrationx.domain.dataworks.aliyunemr.Flow; import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.Asset; import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.DwDatasource; import com.aliyun.dataworks.migrationx.domain.dataworks.objects.entity.DwNode; @@ -631,7 +632,7 @@ public static List load(String fromFolder) throws IOException ListFlowProjectResponse.Project project = GsonUtils.gson.fromJson( prjJson, new TypeToken() {}.getType()); List jobs = loadProjectJobs(projectPath); - Map flows = loadProjectFlows(projectPath); + Map flows = loadProjectFlows(projectPath); AliyunEmrProject aliyunEmrProject = new AliyunEmrProject(); aliyunEmrProject.setProject(project); @@ -642,10 +643,10 @@ public static List load(String fromFolder) throws IOException return projects; } - private static Map loadProjectFlows(File projectPath) + private static Map loadProjectFlows(File projectPath) throws IOException { File flowFolder = new File(projectPath.getAbsolutePath() + File.separator + AliyunEmrService.FLOW_DIR_NAME); - Map flows = new HashMap<>(100); + Map flows = new HashMap<>(100); File[] files = flowFolder.listFiles(f -> f.isFile() && f.getName().endsWith(AliyunEmrService.JSON_FILE_EXT)); if (files == null) { return flows; @@ -663,11 +664,11 @@ private static Map loadProjectF File jobDetailFile = new File( flowJsonFile.getAbsolutePath().replaceAll(AliyunEmrService.JSON_FILE_EXT + "$", "") + AliyunEmrService.FLOW_DETAIL_EXT + AliyunEmrService.JSON_FILE_EXT); - DescribeFlowResponse flowDetail = null; + Flow flowDetail = null; if (jobDetailFile.exists()) { String jobDetailJson = FileUtils.readFileToString(jobDetailFile, Charset.forName( AliyunEmrService.FILE_ENCODE)); - flowDetail = GsonUtils.gson.fromJson(jobDetailJson, new TypeToken() {}.getType()); + flowDetail = GsonUtils.gson.fromJson(jobDetailJson, new TypeToken() {}.getType()); } flows.put(flowItem, flowDetail); } diff --git a/client/migrationx-transformer/src/test/java/com/aliyun/dataworks/domain/utils/CronExpressUtilTest.java b/client/migrationx-transformer/src/test/java/com/aliyun/dataworks/domain/utils/CronExpressUtilTest.java new file mode 100644 index 0000000..b4efa5f --- /dev/null +++ b/client/migrationx-transformer/src/test/java/com/aliyun/dataworks/domain/utils/CronExpressUtilTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.domain.utils; + +import java.text.ParseException; + +import com.aliyun.dataworks.migrationx.domain.dataworks.utils.CronExpressUtil; +import org.junit.Test; + +/** + * @author 聿剑 + * @date 2024/5/11 + */ +public class CronExpressUtilTest { + @Test + public void test() throws ParseException { + String cron = "0 0 9 ? * 2 *"; + String newCron = CronExpressUtil.quartzCronExpressionToDwCronExpress(cron); + System.out.println("old: " + cron + ", new: " + newCron); + } +} diff --git a/client/migrationx-writer/pom.xml b/client/migrationx-writer/pom.xml index 55e924f..64ed4c6 100644 --- a/client/migrationx-writer/pom.xml +++ b/client/migrationx-writer/pom.xml @@ -21,7 +21,7 @@ com.aliyun.dataworks migrationx - 1.1.4 + 1.1.5 migrationx-writer @@ -36,12 +36,12 @@ com.aliyun.dataworks migrationx-domain-dataworks - 1.1.4 + 1.1.5 com.aliyun.dataworks migrationx-common - 1.1.4 + 1.1.5 com.aliyun diff --git a/client/package.xml b/client/package.xml index d34ad52..9d0652b 100644 --- a/client/package.xml +++ b/client/package.xml @@ -18,10 +18,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> - dir + tar.gz - false - client + true diff --git a/client/pom.xml b/client/pom.xml index edc5fae..98916d7 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -19,12 +19,12 @@ com.aliyun.dataworks dataworks-tool - 1.1.4 + 1.1.5 4.0.0 migrationx - 1.1.4 + 1.1.5 pom migrationx @@ -43,7 +43,7 @@ 2.8.9 UTF-8 1.8 - 1.2.13 + 1.2.11 2.1.0 1.4.01 1.6.1 @@ -59,7 +59,7 @@ com.aliyun.dataworks dw-common-spec - 1.1.4 + 1.1.5 org.slf4j @@ -198,11 +198,6 @@ commons-cli 1.4 - - com.aliyun - aliyun-java-sdk-emr - 4.0.212-SNAPSHOT - com.aliyun aliyun-java-sdk-core @@ -476,6 +471,18 @@ + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + diff --git a/pom.xml b/pom.xml index 2ba5298..efbb30b 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 com.aliyun.dataworks dataworks-tool - 1.1.4 + 1.1.5 pom dataworks-tool @@ -51,7 +51,7 @@ com.aliyun.dataworks dw-common-spec - 1.1.4 + 1.1.5 org.slf4j @@ -195,16 +195,6 @@ commons-cli 1.4 - - com.aliyun - aliyun-java-sdk-emr - 4.0.212-SNAPSHOT - - - com.aliyun - aliyun-java-sdk-core - 4.5.6 - org.slf4j slf4j-api diff --git a/spec/pom.xml b/spec/pom.xml index fb4a285..948b316 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -19,14 +19,14 @@ dataworks-tool com.aliyun.dataworks - 1.1.4 + 1.1.5 dw-common-spec jar dw-common-spec - 1.1.4 + 1.1.5 http://maven.apache.org diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksTableSpec.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksTableSpec.java new file mode 100644 index 0000000..5fe76ef --- /dev/null +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksTableSpec.java @@ -0,0 +1,12 @@ +package com.aliyun.dataworks.common.spec.domain; + +import com.aliyun.dataworks.common.spec.domain.ref.SpecTable; +import lombok.Data; + +/** + * @author 子梁 + * @date 2024/4/23 + */ +@Data +public class DataWorksTableSpec extends SpecTable implements Spec { +} diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksWorkflowSpec.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksWorkflowSpec.java index f2e2de0..71195ee 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksWorkflowSpec.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/DataWorksWorkflowSpec.java @@ -38,7 +38,11 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class DataWorksWorkflowSpec extends SpecEntity implements Spec { +public class DataWorksWorkflowSpec extends SpecRefEntity implements Spec { + private String name; + private String type; + private String owner; + private String description; private List variables; private List triggers; private List scripts; diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/AbstractBaseCode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/AbstractBaseCode.java index d949ac5..0e9b04d 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/AbstractBaseCode.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/AbstractBaseCode.java @@ -37,6 +37,11 @@ public abstract class AbstractBaseCode implements Code { protected List resourceReferences; + @Override + public String getRawContent() { + return getContent(); + } + @Override public String getContent() { return GsonUtils.toJsonString(this); diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/Code.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/Code.java index bf0a076..b414f3b 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/Code.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/Code.java @@ -23,6 +23,15 @@ * @date 2022/12/28 */ public interface Code { + /** + * 获取原始内容 + * + * @return String + */ + default String getRawContent() { + return getContent(); + } + /** * get serialized code content string, * 给调度的节点代码整体完整内容 diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModel.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModel.java index ce311ec..0a9c037 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModel.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModel.java @@ -44,6 +44,11 @@ public String getContent() { return Optional.ofNullable(codeModel).map(Code::getContent).orElse(null); } + @Override + public String getRawContent() { + return Optional.ofNullable(codeModel).map(Code::getRawContent).orElse(null); + } + @Override public void setSourceCode(String sourceCode) { codeModel.setSourceCode(sourceCode); diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModelFactory.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModelFactory.java index faadff6..75087c4 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModelFactory.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/CodeModelFactory.java @@ -55,11 +55,11 @@ private static Code parseCodeModel(String programType, String code) { }) .filter(inst -> ListUtils.emptyIfNull(inst.getProgramTypes()).stream().anyMatch(t -> t.equalsIgnoreCase(programType))) .collect(Collectors.toList()); - log.info("code model list: {}", list); + log.debug("code model list: {}", list); Code theOne = list.stream().max(Comparator.comparing(AbstractBaseCode::getClassHierarchyLevel)) .map(inst -> inst.parse(code)) .orElseGet(() -> new PlainTextCode().parse(code)); - log.info("code model: {}", theOne); + log.debug("code model: {}", theOne); return theOne; } } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/DefaultJsonFormCode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/DefaultJsonFormCode.java new file mode 100644 index 0000000..be3c031 --- /dev/null +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/DefaultJsonFormCode.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.common.spec.domain.dw.codemodel; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.alibaba.fastjson2.JSON; + +import com.aliyun.dataworks.common.spec.domain.dw.types.CodeProgramType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +/** + * json表单默认代码类型 + * - getContent() 获取发给调度的代码内容 + * - getExtraContent() 获取其他额外配置内容 + * + * @author 聿剑 + * @date 2024/4/12 + */ +@Data +@ToString +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class DefaultJsonFormCode extends JsonObjectCode implements JsonFormCode { + public static final String FIELD_EXTRA_CONTENT = "extraContent"; + public static final String FIELD_CONTENT = "content"; + + @Override + public List getProgramTypes() { + return Stream.of(CodeProgramType.HOLOGRES_SYNC_DATA, CodeProgramType.HOLOGRES_SYNC_DDL) + .map(CodeProgramType::name).distinct().collect(Collectors.toList()); + } + + public DefaultJsonFormCode setExtraContent(String extraContent) { + Optional.ofNullable(JSON.parseObject(rawContent)).ifPresent(js -> { + js.put(FIELD_EXTRA_CONTENT, extraContent); + rawContent = JSON.toJSONString(js); + }); + return this; + } + + public DefaultJsonFormCode setContent(String content) { + Optional.ofNullable(JSON.parseObject(rawContent)).ifPresent(js -> { + js.put(FIELD_CONTENT, content); + rawContent = JSON.toJSONString(js); + }); + return this; + } + + public String getExtraContent() { + if (StringUtils.isBlank(rawContent)) { + return null; + } + + return Optional.ofNullable(JSON.parseObject(rawContent)) + .map(js -> js.getString(FIELD_EXTRA_CONTENT)) + .orElse(null); + } + + @Override + public String getContent() { + if (StringUtils.isBlank(rawContent)) { + return null; + } + + return Optional.ofNullable(JSON.parseObject(rawContent)) + .map(js -> js.getString(FIELD_CONTENT)) + .orElse(null); + } + + @Override + public String getRawContent() { + return rawContent; + } +} diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/JsonObjectCode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/JsonObjectCode.java index def34be..0a26cfa 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/JsonObjectCode.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/JsonObjectCode.java @@ -33,11 +33,11 @@ @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) public abstract class JsonObjectCode extends AbstractBaseCode { - private transient String content; + protected transient String rawContent; @Override public JsonObjectCode parse(String code) { - this.content = code; + this.rawContent = code; if (StringUtils.isBlank(code)) { return this; } @@ -46,21 +46,26 @@ public JsonObjectCode parse(String code) { if (joc == null) { return this; } - joc.setContent(code); + joc.setRawContent(code); return joc; } @Override public void setSourceCode(String sourceCode) { - this.content = sourceCode; + this.rawContent = sourceCode; } @Override public String getSourceCode() { - return content; + return rawContent; } public String getContent() { return super.getContent(); } + + public JsonObjectCode setRawContent(String rawContent) { + this.rawContent = rawContent; + return this; + } } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/PaiFlowCode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/PaiFlowCode.java index 97dccbf..7970929 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/PaiFlowCode.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/codemodel/PaiFlowCode.java @@ -16,7 +16,7 @@ package com.aliyun.dataworks.common.spec.domain.dw.codemodel; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -210,6 +210,6 @@ public PaiFlowCode parse(String code) { @Override public List getProgramTypes() { - return Collections.singletonList(CodeProgramType.PAI_STUDIO.name()); + return Arrays.asList(CodeProgramType.PAI_STUDIO.name(), CodeProgramType.RECOMMEND_PLUS.name()); } } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNode.java index 6902bf1..dc203a1 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNode.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNode.java @@ -89,4 +89,11 @@ public interface DataWorksNode { * @return the node ext config */ String getExtConfig(); + + /** + * get schedule node type + * + * @return node type code + */ + Integer getNodeType(); } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapter.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapter.java index b710821..2d21382 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapter.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapter.java @@ -31,6 +31,7 @@ import com.aliyun.dataworks.common.spec.domain.adapter.SpecNodeAdapter; import com.aliyun.dataworks.common.spec.domain.dw.types.CodeProgramType; import com.aliyun.dataworks.common.spec.domain.enums.DependencyType; +import com.aliyun.dataworks.common.spec.domain.enums.TriggerType; import com.aliyun.dataworks.common.spec.domain.enums.VariableType; import com.aliyun.dataworks.common.spec.domain.interfaces.Input; import com.aliyun.dataworks.common.spec.domain.interfaces.Output; @@ -39,6 +40,7 @@ import com.aliyun.dataworks.common.spec.domain.ref.SpecNode; import com.aliyun.dataworks.common.spec.domain.ref.SpecNodeOutput; import com.aliyun.dataworks.common.spec.domain.ref.SpecScript; +import com.aliyun.dataworks.common.spec.domain.ref.SpecTrigger; import com.aliyun.dataworks.common.spec.domain.ref.SpecVariable; import com.aliyun.dataworks.common.spec.domain.ref.runtime.SpecScriptRuntime; import org.apache.commons.collections4.CollectionUtils; @@ -55,6 +57,10 @@ public class DataWorksNodeAdapter implements SpecNodeAdapter, DataWorksNode { public static final String TIMEOUT = "alisaTaskKillTimeout"; public static final String IGNORE_BRANCH_CONDITION_SKIP = "ignoreBranchConditionSkip"; + public static final Integer NODE_TYPE_NORMAL = 0; + public static final Integer NODE_TYPE_MANUAL = 1; + public static final Integer NODE_TYPE_PAUSE = 2; + public static final Integer NODE_TYPE_SKIP = 3; private static final Logger logger = LoggerFactory.getLogger(DataWorksNodeAdapter.class); @@ -70,7 +76,7 @@ public DataWorksNodeAdapter(Specification specification, @Override public SpecNode getSpecNode() { - return null; + return specNode; } @Override @@ -212,4 +218,23 @@ public String getExtConfig() { return extConfig.isEmpty() ? null : JSONObject.toJSONString(extConfig); } + + @Override + public Integer getNodeType() { + if (Optional.ofNullable(specNode.getTrigger()).map(SpecTrigger::getType).map(TriggerType.MANUAL::equals).orElse(false)) { + return NODE_TYPE_MANUAL; + } + + return Optional.ofNullable(specNode.getRecurrence()).map(nodeRecurrenceType -> { + switch (nodeRecurrenceType) { + case PAUSE: + return NODE_TYPE_PAUSE; + case SKIP: + return NODE_TYPE_SKIP; + case NORMAL: + return NODE_TYPE_NORMAL; + } + return null; + }).orElseThrow(() -> new RuntimeException("not support node type: " + specNode)); + } } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapter.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapter.java index 413743e..c0ba75e 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapter.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapter.java @@ -16,11 +16,9 @@ package com.aliyun.dataworks.common.spec.domain.dw.nodemodel; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -39,7 +37,6 @@ import com.aliyun.dataworks.common.spec.domain.dw.codemodel.EmrJobType; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.EmrLauncher; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.MultiLanguageScriptingCode; -import com.aliyun.dataworks.common.spec.domain.dw.codemodel.OdpsSparkCode; import com.aliyun.dataworks.common.spec.domain.dw.types.CodeProgramType; import com.aliyun.dataworks.common.spec.domain.dw.types.ProductModule; import com.aliyun.dataworks.common.spec.domain.enums.SpecVersion; @@ -74,30 +71,6 @@ public class DataWorksNodeCodeAdapter { private static final List JOIN_BRANCH_LOGICS = Arrays.asList(LOGIC_OR, LOGIC_AND); private final SpecNode specNode; - private static final Map, List> CODE_TYPE_MAP; - - static { - CODE_TYPE_MAP = new HashMap<>(); - CODE_TYPE_MAP.put(MultiLanguageScriptingCode.class, - Arrays.asList(CodeProgramType.CONTROLLER_ASSIGNMENT, CodeProgramType.CONTROLLER_CYCLE_END)); - CODE_TYPE_MAP.put(OdpsSparkCode.class, Collections.singletonList(CodeProgramType.ODPS_SPARK)); - CODE_TYPE_MAP.put(ControllerJoinCode.class, Collections.singletonList(CodeProgramType.CONTROLLER_JOIN)); - CODE_TYPE_MAP.put(ControllerBranchCode.class, Collections.singletonList(CodeProgramType.CONTROLLER_BRANCH)); - CODE_TYPE_MAP.put(DataIntegrationCode.class, Collections.singletonList(CodeProgramType.DI)); - CODE_TYPE_MAP.put(EmrCode.class, Arrays.asList( - CodeProgramType.EMR_HIVE, - CodeProgramType.EMR_SPARK, - CodeProgramType.EMR_SPARK_SQL, - CodeProgramType.EMR_MR, - CodeProgramType.EMR_SHELL, - CodeProgramType.EMR_SPARK_SHELL, - CodeProgramType.EMR_PRESTO, - CodeProgramType.EMR_IMPALA, - CodeProgramType.EMR_SCOOP, - CodeProgramType.EMR_SPARK_STREAMING, - CodeProgramType.EMR_HIVE_CLI, - CodeProgramType.EMR_STREAMING_SQL)); - } public DataWorksNodeCodeAdapter(SpecNode specNode) { this.specNode = specNode; @@ -110,32 +83,38 @@ public String getCode() { SpecScriptRuntime runtime = Optional.ofNullable(script.getRuntime()).orElseThrow( () -> new SpecException(SpecErrorCode.PARSE_ERROR, "node.script.runtime is null")); - Class codeClass = CODE_TYPE_MAP.entrySet().stream() - .filter(entry -> entry.getValue().stream().anyMatch(type -> StringUtils.equalsIgnoreCase(type.name(), runtime.getCommand()))) - .findAny().map(Entry::getKey).orElse(null); + try { + String command = runtime.getCommand(); + CodeModel codeModel = CodeModelFactory.getCodeModel(command, null); + Code code = codeModel.getCodeModel(); + Class codeClass = code.getClass(); - if (codeClass == null) { - return script.getContent(); - } + // logics of get code content for special node code + if (MultiLanguageScriptingCode.class.equals(codeClass)) { + return getMultiLanguageScriptingCode(script); + } - if (MultiLanguageScriptingCode.class.equals(codeClass)) { - return getMultiLanguageScriptingCode(script); - } + if (ControllerBranchCode.class.equals(codeClass)) { + return getControllerBranchCode(specNode, script); + } - if (ControllerBranchCode.class.equals(codeClass)) { - return getControllerBranchCode(specNode, script); - } + if (ControllerJoinCode.class.equals(codeClass)) { + return getControllerJoinCode(specNode); + } - if (ControllerJoinCode.class.equals(codeClass)) { - return getControllerJoinCode(specNode); - } + if (DataIntegrationCode.class.equals(codeClass)) { + return getDiCode(script); + } - if (DataIntegrationCode.class.equals(codeClass)) { - return getDiCode(script); - } + if (codeClass.equals(EmrCode.class)) { + return getEmrCode(script); + } - if (codeClass.equals(EmrCode.class)) { - return getEmrCode(script); + // common default logic to get content + code.setSourceCode(script.getContent()); + return code.getContent(); + } catch (Exception ex) { + log.warn("get code model error: ", ex); } return script.getContent(); } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeInputOutputAdapter.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeInputOutputAdapter.java index 1bbd939..73bcc96 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeInputOutputAdapter.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeInputOutputAdapter.java @@ -64,18 +64,48 @@ public DataWorksNodeInputOutputAdapter(Specification spec } public List getInputs() { - List inputs = ListUtils.emptyIfNull(specNode.getInputs()).stream() + SpecNode outerNode = ListUtils.emptyIfNull(specification.getNodes()).stream() + // current SpecNode is inner node of other node + .filter(node -> ListUtils.emptyIfNull(node.getInnerNodes()).stream() + .anyMatch(innerNode -> StringUtils.equals(innerNode.getId(), specNode.getId()))) + .findAny().orElse(null); + if (outerNode != null) { + return getInputList(outerNode.getInnerFlow(), outerNode.getInnerNodes(), specNode); + } + return getInputList(specification.getFlow(), specification.getNodes(), specNode); + } + + private List getInputList(List flow, List allNodes, SpecNode node) { + List inputs = ListUtils.emptyIfNull(node.getInputs()).stream() .filter(o -> o instanceof SpecNodeOutput) .map(o -> (SpecArtifact)o) .filter(o -> ArtifactType.NODE_OUTPUT.equals(o.getArtifactType())) .collect(Collectors.toList()); - Optional specNodeFlowDepend = ListUtils.emptyIfNull(specification.getFlow()).stream() - .filter(fd -> StringUtils.equalsIgnoreCase(specNode.getId(), fd.getNodeId().getId())) + Optional specNodeFlowDepend = ListUtils.emptyIfNull(flow).stream() + .filter(fd -> StringUtils.equalsIgnoreCase(node.getId(), fd.getNodeId().getId())) .peek(fd -> log.info("node flow depends source nodeId: {}, depends: {}", JSON.toJSONString(fd.getNodeId()), JSON.toJSONString(fd.getDepends()))) .findFirst(); + specNodeFlowDepend + .map(SpecFlowDepend::getDepends) + .orElse(ListUtils.emptyIfNull(null)) + .stream() + .filter(dep -> DependencyType.NORMAL.equals(dep.getType())) + .filter(dep -> dep.getOutput() == null || StringUtils.isBlank(dep.getOutput().getData())) + .filter(dep -> dep.getNodeId() != null) + .map(out -> ListUtils.emptyIfNull(allNodes).stream().filter(n -> StringUtils.equals(out.getNodeId().getId(), n.getId())) + .findAny().flatMap(depNode -> depNode.getOutputs().stream() + .filter(o -> o instanceof SpecNodeOutput).map(o -> (SpecNodeOutput)o).findAny()) + .map(output -> { + SpecNodeOutput io = new SpecNodeOutput(); + io.setData(output.getData()); + io.setRefTableName(output.getRefTableName()); + return io; + }).orElse(null)) + .filter(Objects::nonNull).forEach(inputs::add); + specNodeFlowDepend .map(SpecFlowDepend::getDepends) .orElse(ListUtils.emptyIfNull(null)) diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CalcEngineType.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CalcEngineType.java index 6d67067..e149e55 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CalcEngineType.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CalcEngineType.java @@ -39,7 +39,9 @@ public enum CalcEngineType implements IntEnum, LocaleAware, Labe FLINK(15, "Flink", "Flink"), DATABASE(100, "Database", "数据库"), DI(101, "Data Integration", "数据集成"), - ALGORITHM(102, "Algorithm", "算法"); + ALGORITHM(102, "Algorithm", "算法"), + STAR_ROCKS(10001, "StarRocks", "StarRocks"), + HIVE(10002, "Hive", "Hive"); private final int value; private final String name; diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CodeProgramType.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CodeProgramType.java index 2ca626c..7335816 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CodeProgramType.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/dw/types/CodeProgramType.java @@ -49,19 +49,25 @@ public enum CodeProgramType { SCHEDULER_TRIGGER(1114, "SCHEDULER_TRIGGER", CalcEngineType.GENERAL, null, ".json"), PARAM_HUB(1115, "PARAM_HUB", CalcEngineType.GENERAL, null, ".param-hub.json"), FTP_CHECK(1320, "FTP_CHECK", CalcEngineType.GENERAL, null, ".json"), + SSH(1321, "SSH", CalcEngineType.GENERAL, null, ".ssh.sh"), CHECK(19, "CHECK", CalcEngineType.GENERAL, null, ".json"), OSS_INSPECT(239, "OSS_INSPECT", CalcEngineType.GENERAL, null, ".json"), CROSS_TENANTS(1089, "CROSS_TENANTS", CalcEngineType.GENERAL, null, ".json"), HIVE(3, "HIVE", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".sql"), - ODPS_PERL(9, "odps_pl", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".pl"), + // Data Masking + YSF_DESEN(82, "YSF_DESEN", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.data.masking.sql"), + ODPS_XLIB(8, "ODPS_XLIB", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.xlib.py"), + ODPS_PERL(9, "odps_pl", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.pl"), ODPS_SQL(10, "ODPS_SQL", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".sql"), + EXTREME_STORAGE(30, "EXTREME_STORAGE", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.extreme.store.sh"), ODPS_SPARK_SQL(226, "ODPS_SPARK_SQL", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".sql"), - ODPS_MR(11, "ODPS_MR", CalcEngineType.ODPS, LabelType.DATA_PROCESS, null), + ODPS_MR(11, "ODPS_MR", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mr.sql"), PYODPS3(1221, "PYODPS3", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".py"), ODPS_SCRIPT(24, "ODPS_SQL_SCRIPT", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".ms"), PYODPS(221, "PY_ODPS", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".py"), - ODPS_SPARK(225, "ODPS_SPARK", CalcEngineType.ODPS, LabelType.DATA_PROCESS, null), + ODPS_SHARK(223, "ODPS_SHARK", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.shark.json"), + ODPS_SPARK(225, "ODPS_SPARK", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".mc.spark.json"), COMPONENT_SQL(1010, "COMPONENT_SQL", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".sql"), SQL_COMPONENT(3010, "SQL_COMPONENT", CalcEngineType.ODPS, LabelType.DATA_PROCESS, ".sql"), ODPS_PYTHON(12, "ODPS_PYTHON", CalcEngineType.ODPS, LabelType.RESOURCE, ".py"), @@ -112,14 +118,18 @@ public enum CodeProgramType { CDH_TABLE(280, "CDH_TABLE", CalcEngineType.HADOOP_CDH, LabelType.TABLE, null), CDH_FUNCTION(281, "CDH_FUNCTION", CalcEngineType.HADOOP_CDH, LabelType.FUNCTION, null), - PAI(1002, "pai", CalcEngineType.ALGORITHM, null, null), - PAI_STUDIO(1117, "pai_studio", CalcEngineType.ALGORITHM, null, null), + PAI(1002, "pai", CalcEngineType.ALGORITHM, null, ".json"), + PAI_STUDIO(1117, "pai_studio", CalcEngineType.ALGORITHM, null, ".json"), + RECOMMEND_PLUS(1116, "RECOMMEND_PLUS", CalcEngineType.ALGORITHM, null, ".json"), + PAI_DLC(1119, "pai_dlc", CalcEngineType.ALGORITHM, null, ".pai.dlc.sh"), + ALINK(240, "alink", CalcEngineType.ALGORITHM, null, ".alink.py"), + XLAB(87, "xlab", CalcEngineType.ALGORITHM, null, ".xlab.json"), HOLOGRES_DEVELOP(1091, "HOLOGRES_DEVELOP", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".sql"), HOLOGRES_SYNC(1092, "HOLOGRES_DEVELOP", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".json"), HOLOGRES_SQL(1093, "HOLOGRES_SQL", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".sql"), - HOLOGRES_SYNC_DDL(1094, "HOLOGRES_SYNC_DDL", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".json"), - HOLOGRES_SYNC_DATA(1095, "HOLOGRES_SYNC_DATA", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".json"), + HOLOGRES_SYNC_DDL(1094, "HOLOGRES_SYNC_DDL", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".hologres.ddl.sync.json"), + HOLOGRES_SYNC_DATA(1095, "HOLOGRES_SYNC_DATA", CalcEngineType.HOLO, LabelType.DATA_PROCESS, ".hologres.data.sync.json"), BLINK_BATCH_SQL(2020, "BLINK_BATCH_SQL", CalcEngineType.FLINK, LabelType.DATA_PROCESS, null), BLINK_DATASTREAM(2019, "BLINK_DATASTREAM", CalcEngineType.FLINK, LabelType.DATA_PROCESS, null), diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/enums/SpecKind.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/enums/SpecKind.java index e433fa3..0c10acb 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/enums/SpecKind.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/enums/SpecKind.java @@ -32,6 +32,10 @@ public enum SpecKind implements LabelEnum { * Manual workflow */ MANUAL_WORKFLOW("ManualWorkflow"), + /** + * Single Manual Node + */ + MANUAL_NODE("ManualNode"), /** * TemporaryWorkflow @@ -40,7 +44,31 @@ public enum SpecKind implements LabelEnum { /** * PaiFlow */ - PAIFLOW("PaiFlow"); + PAIFLOW("PaiFlow"), + /** + * BatchDeployment + */ + BATCH_DEPLOYMENT("BatchDeployment"), + /** + * DataSource + */ + DATASOURCE("DataSource"), + /** + * DataQuality + */ + DATA_QUALITY("DataQuality"), + /** + * DataService + */ + DATA_SERVICE("DataService"), + /** + * DataCatalog + */ + DATA_CATALOG("DataCatalog"), + /** + * Table + */ + TABLE("Table"); private final String label; diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/ContainerNode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/ContainerNode.java new file mode 100644 index 0000000..076a2b6 --- /dev/null +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/ContainerNode.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Alibaba Cloud; + * 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 com.aliyun.dataworks.common.spec.domain.ref; + +import java.util.List; + +import com.aliyun.dataworks.common.spec.domain.noref.SpecFlowDepend; + +/** + * container node interface + * + * @author 聿剑 + * @date 2024/5/2 + */ +public interface ContainerNode { + + /** + * get inner nodes + * + * @return list of inner nodes + */ + List getInnerNodes(); + + /** + * get inner flow depends of inner nodes + * + * @return List of spec flow depend + */ + List getInnerFlow(); +} \ No newline at end of file diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/SpecNode.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/SpecNode.java index 7434253..3ef8602 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/SpecNode.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/SpecNode.java @@ -15,7 +15,10 @@ package com.aliyun.dataworks.common.spec.domain.ref; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; import com.aliyun.dataworks.common.spec.domain.SpecRefEntity; import com.aliyun.dataworks.common.spec.domain.enums.NodeInstanceModeType; @@ -26,12 +29,14 @@ import com.aliyun.dataworks.common.spec.domain.noref.SpecBranch; import com.aliyun.dataworks.common.spec.domain.noref.SpecCombined; import com.aliyun.dataworks.common.spec.domain.noref.SpecDoWhile; +import com.aliyun.dataworks.common.spec.domain.noref.SpecFlowDepend; import com.aliyun.dataworks.common.spec.domain.noref.SpecForEach; import com.aliyun.dataworks.common.spec.domain.noref.SpecJoin; import com.aliyun.dataworks.common.spec.domain.noref.SpecNodeRef; import com.aliyun.dataworks.common.spec.domain.noref.SpecParamHub; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.collections4.ListUtils; /** * @author yiwei.qyw @@ -39,7 +44,7 @@ */ @EqualsAndHashCode(callSuper = true) @Data -public class SpecNode extends SpecRefEntity { +public class SpecNode extends SpecRefEntity implements ContainerNode { private NodeRecurrenceType recurrence; private Integer priority; @@ -94,4 +99,34 @@ public class SpecNode extends SpecRefEntity { private String owner; private String description; + + @Override + public List getInnerNodes() { + List nodes = new ArrayList<>(); + Optional.ofNullable(doWhile).ifPresent(dw -> { + Optional.ofNullable(dw.getSpecWhile()).ifPresent(nodes::add); + nodes.addAll(ListUtils.emptyIfNull(dw.getNodes())); + }); + + Optional.ofNullable(foreach).ifPresent(fe -> nodes.addAll(ListUtils.emptyIfNull(foreach.getNodes()))); + Optional.ofNullable(combined).ifPresent(cb -> nodes.addAll(ListUtils.emptyIfNull(cb.getNodes()))); + return Collections.unmodifiableList(nodes); + } + + @Override + public List getInnerFlow() { + if (doWhile != null) { + return doWhile.getFlow(); + } + + if (foreach != null) { + return foreach.getFlow(); + } + + if (combined != null) { + return combined.getFlow(); + } + + return null; + } } \ No newline at end of file diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngine.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngine.java index 9b39162..a4b312c 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngine.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngine.java @@ -35,6 +35,10 @@ public class SpecCalcEngine extends SpecRefEntity { private CalcEngineType type; private SpecDatasource datasource; private String endpoint; - private String project; + /** + * mc场景下database代指mcProject + */ + private String database; private String schema; + private SpecCalcEngineVersion version; } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngineVersion.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngineVersion.java new file mode 100644 index 0000000..00fb375 --- /dev/null +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/domain/ref/calcengine/SpecCalcEngineVersion.java @@ -0,0 +1,15 @@ +package com.aliyun.dataworks.common.spec.domain.ref.calcengine; + +import lombok.Data; + +/** + * CalcEngineVersion + * + * @author 子梁 + * @date 2024/4/23 + */ +@Data +public class SpecCalcEngineVersion { + private String versionNumber; + private String versionInfo; +} diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/DataWorksTableSpecParser.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/DataWorksTableSpecParser.java new file mode 100644 index 0000000..72cb3b0 --- /dev/null +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/DataWorksTableSpecParser.java @@ -0,0 +1,29 @@ +package com.aliyun.dataworks.common.spec.parser; + +import java.util.Arrays; +import java.util.Map; + +import com.aliyun.dataworks.common.spec.domain.DataWorksTableSpec; +import com.aliyun.dataworks.common.spec.domain.enums.SpecKind; +import com.aliyun.dataworks.common.spec.parser.impl.SpecParser; + +/** + * @author 子梁 + * @date 2024/4/23 + */ +public class DataWorksTableSpecParser extends SpecParser { + + @Override + public boolean support(String kind) { + return Arrays.stream(SpecKind.values()).map(SpecKind::getLabel).anyMatch(k -> k.equalsIgnoreCase(kind)); + } + + @Override + public DataWorksTableSpec parse(Map rawContext, SpecParserContext specParserContext) { + DataWorksTableSpec specObj = instantiateSpecObject(); + specParserContext.setIgnoreMissingFields(true); + parseSpecObjectFields(specObj, rawContext, specParserContext); + return specObj; + } + +} diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/ToDomainRootParser.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/ToDomainRootParser.java index 6b97af0..431906b 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/ToDomainRootParser.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/ToDomainRootParser.java @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import com.aliyun.dataworks.common.spec.domain.Spec; @@ -26,6 +27,7 @@ import com.aliyun.dataworks.common.spec.domain.SpecRefEntity; import com.aliyun.dataworks.common.spec.domain.Specification; import com.aliyun.dataworks.common.spec.domain.enums.ArtifactType; +import com.aliyun.dataworks.common.spec.domain.noref.SpecDepend; import com.aliyun.dataworks.common.spec.domain.ref.SpecNode; import com.aliyun.dataworks.common.spec.domain.ref.SpecNodeOutput; import com.aliyun.dataworks.common.spec.exception.SpecErrorCode; @@ -172,6 +174,11 @@ private void replaceObj(SpecEntityContext refElm, SpecRefEntity targetElm, Objec SpecNodeOutput ar = new SpecNodeOutput(); ar.setData((String)refElm.getEntityValue()); ar.setArtifactType(ArtifactType.NODE_OUTPUT); + Optional.ofNullable(refElm.getOwnerObject()) + .filter(oo -> oo instanceof SpecDepend) + .map(oo -> ((SpecDepend)oo).getOutput()) + .map(SpecNodeOutput::getRefTableName) + .ifPresent(ar::setRefTableName); SpecDevUtil.setValue(ownerObject, refElm.getEntityField(), ar); return; } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/DataWorksWorkflowSpecParser.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/DataWorksWorkflowSpecParser.java index 73bac9e..e6cb563 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/DataWorksWorkflowSpecParser.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/DataWorksWorkflowSpecParser.java @@ -24,6 +24,7 @@ import com.aliyun.dataworks.common.spec.parser.Parser; import com.aliyun.dataworks.common.spec.parser.SpecParserContext; import com.aliyun.dataworks.common.spec.parser.SpecParserFactory; +import com.aliyun.dataworks.common.spec.utils.SpecDevUtil; /** * @author 聿剑 @@ -39,6 +40,7 @@ public boolean support(String kind) { public DataWorksWorkflowSpec parse(Map rawContext, SpecParserContext specParserContext) { DataWorksWorkflowSpec specObj = instantiateSpecObject(); specParserContext.setIgnoreMissingFields(true); + SpecDevUtil.setSimpleField(rawContext, specObj); parseSpecObjectFields(specObj, rawContext, specParserContext); return specObj; } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/SpecDependParser.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/SpecDependParser.java index ff0793b..8317dd6 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/SpecDependParser.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/parser/impl/SpecDependParser.java @@ -41,6 +41,7 @@ public SpecDepend parse(Map rawContext, SpecParserContext specPa Optional.ofNullable(rawContext.get("output")).map(o -> (String)o).ifPresent(output -> { SpecNodeOutput out = new SpecNodeOutput(); out.setData(output); + Optional.ofNullable(rawContext.get("refTableName")).map(ref -> (String)ref).ifPresent(out::setRefTableName); specDepend.setOutput(out); }); return specDepend; diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/utils/SpecDevUtil.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/utils/SpecDevUtil.java index 17bd112..ee166b6 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/utils/SpecDevUtil.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/utils/SpecDevUtil.java @@ -298,11 +298,7 @@ public static void setEntityToCtx(Object object, SpecParserContext parserContext } String key = object.getClass().getSimpleName() + "#" + id; - - if (entityMap.containsKey(key)) { - throw new SpecException(SpecErrorCode.DUPLICATE_ID, - "Duplicate id:" + id + "\n" + "Type:" + object.getClass().getSimpleName()); - } + entityMap.put(key, (SpecRefEntity)object); } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecDependWriter.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecDependWriter.java index ff9e4ba..3dd7ebe 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecDependWriter.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecDependWriter.java @@ -39,6 +39,7 @@ public JSONObject write(SpecDepend specObj, SpecWriterContext context) { JSONObject json = writeJsonObject(specObj, true); Optional.ofNullable(specObj.getNodeId()).ifPresent(nodeId -> json.put("nodeId", nodeId.getId())); Optional.ofNullable(specObj.getOutput()).ifPresent(output -> json.put("output", output.getData())); + Optional.ofNullable(specObj.getOutput()).ifPresent(output -> json.put("refTableName", output.getRefTableName())); return json; } } diff --git a/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecNodeWriter.java b/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecNodeWriter.java index 5af6975..3cdd9c2 100644 --- a/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecNodeWriter.java +++ b/spec/src/main/java/com/aliyun/dataworks/common/spec/writer/impl/SpecNodeWriter.java @@ -90,6 +90,10 @@ public JSONObject write(SpecNode specObj, SpecWriterContext context) { } private JSONObject writeIo(List ioList) { + if (ioList == null) { + return null; + } + JSONObject ioJson = new JSONObject(); Map, List> ioGroup = ListUtils.emptyIfNull(ioList).stream().collect(Collectors.groupingBy(Object::getClass)); diff --git a/spec/src/main/spec/examples/json/emr.json b/spec/src/main/spec/examples/json/emr.json index adf45be..3345ad3 100644 --- a/spec/src/main/spec/examples/json/emr.json +++ b/spec/src/main/spec/examples/json/emr.json @@ -15,7 +15,7 @@ "script": { "path": "test/EMR_test_biz/EMR/hive1", "language": "hive-sql", - "content": "select 1", + "content": {}, "runtime": { "engine": "Hive", "command": "EMR_HIVE", diff --git a/spec/src/main/spec/examples/json/manual_flow.json b/spec/src/main/spec/examples/json/manual_flow.json index 2ae5bc7..9788f31 100644 --- a/spec/src/main/spec/examples/json/manual_flow.json +++ b/spec/src/main/spec/examples/json/manual_flow.json @@ -2,6 +2,7 @@ "version": "1.1.0", "kind": "ManualWorkflow", "spec": { + "id": 12345, "scripts": [ { "id": "file1", diff --git a/spec/src/main/spec/examples/yaml/emr.yaml b/spec/src/main/spec/examples/yaml/emr.yaml index 442cedd..72765e5 100644 --- a/spec/src/main/spec/examples/yaml/emr.yaml +++ b/spec/src/main/spec/examples/yaml/emr.yaml @@ -13,7 +13,7 @@ spec: script: path: test/EMR_test_biz/EMR/hive1 language: hive-sql # hive sql language - content: select 1 # sql code + content: { } # sql code runtime: engine: Hive command: EMR_HIVE # Node type diff --git a/spec/src/main/spec/examples/yaml/manual_flow.yaml b/spec/src/main/spec/examples/yaml/manual_flow.yaml index 074aff9..0b534f4 100644 --- a/spec/src/main/spec/examples/yaml/manual_flow.yaml +++ b/spec/src/main/spec/examples/yaml/manual_flow.yaml @@ -2,6 +2,7 @@ version: 1.1.0 kind: ManualWorkflow # Manual workflow spec: # define entities of each type + id: 12345 scripts: - id: file1 path: /path/to/datax_1.json diff --git a/spec/src/test/java/com/aliyun/dataworks/common/spec/SpecUtilTest.java b/spec/src/test/java/com/aliyun/dataworks/common/spec/SpecUtilTest.java index cf9df9d..37dcf37 100644 --- a/spec/src/test/java/com/aliyun/dataworks/common/spec/SpecUtilTest.java +++ b/spec/src/test/java/com/aliyun/dataworks/common/spec/SpecUtilTest.java @@ -361,6 +361,7 @@ public void testManual() { String spec = readJson("manual_flow.json"); Specification specObj = SpecUtil.parseToDomain(spec); Assert.assertNotNull(specObj); + Assert.assertNotNull(specObj.getSpec().getId()); Assert.assertEquals(specObj.getKind(), SpecKind.MANUAL_WORKFLOW.getLabel()); } @@ -1269,4 +1270,181 @@ public void testParseForeach() { log.info("before: {}", json); log.info("foreach: {}", JSON.toJSONString(SpecUtil.write(foreach, new SpecWriterContext()), Feature.PrettyFormat)); } + + @Test + public void testx() { + String spec = "{\n" + + "\t\"version\":\"1.1.0\",\n" + + "\t\"kind\":\"TemporaryWorkflow\",\n" + + "\t\"spec\":{\n" + + "\t\t\"nodes\":[\n" + + "\t\t\t{\n" + + "\t\t\t\t\"recurrence\":\"Normal\",\n" + + "\t\t\t\t\"id\":\"5143110377713406119\",\n" + + "\t\t\t\t\"timeout\":0,\n" + + "\t\t\t\t\"instanceMode\":\"T+1\",\n" + + "\t\t\t\t\"rerunMode\":\"Allowed\",\n" + + "\t\t\t\t\"rerunTimes\":3,\n" + + "\t\t\t\t\"rerunInterval\":180000,\n" + + "\t\t\t\t\"script\":{\n" + + "\t\t\t\t\t\"path\":\"聿剑/flow/flow6/f_ge_shell1\",\n" + + "\t\t\t\t\t\"runtime\":{\n" + + "\t\t\t\t\t\t\"command\":\"DIDE_SHELL\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t\"content\":\"#!/bin/bash\\n#********************************************************************#\\n##author:聿剑\\n" + + "##create time:2024-04-09 16:05:37\\n#********************************************************************#\\necho $1\",\n" + + "\t\t\t\t\t\"id\":\"6138281211054878711\",\n" + + "\t\t\t\t\t\"parameters\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"name\":\"flow_bizdate\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"Variable\",\n" + + "\t\t\t\t\t\t\t\"scope\":\"NodeParameter\",\n" + + "\t\t\t\t\t\t\t\"type\":\"System\",\n" + + "\t\t\t\t\t\t\t\"value\":\"$[yyyymmdd-1]\",\n" + + "\t\t\t\t\t\t\t\"id\":\"6584333807719816392\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"trigger\":{\n" + + "\t\t\t\t\t\"type\":\"Scheduler\",\n" + + "\t\t\t\t\t\"id\":\"4752762997864777554\",\n" + + "\t\t\t\t\t\"cron\":\"00 00 00 * * ?\",\n" + + "\t\t\t\t\t\"startTime\":\"1970-01-01 00:00:00\",\n" + + "\t\t\t\t\t\"endTime\":\"9999-01-01 00:00:00\",\n" + + "\t\t\t\t\t\"timezone\":\"Asia/Shanghai\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"runtimeResource\":{\n" + + "\t\t\t\t\t\"resourceGroup\":\"group_2\",\n" + + "\t\t\t\t\t\"id\":\"5623679673296125496\",\n" + + "\t\t\t\t\t\"resourceGroupId\":\"2\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"name\":\"f_ge_shell1\",\n" + + "\t\t\t\t\"owner\":\"064152\",\n" + + "\t\t\t\t\"metadata\":{\n" + + "\t\t\t\t\t\"owner\":{\n" + + "\t\t\t\t\t\t\"userId\":\"064152\",\n" + + "\t\t\t\t\t\t\"userName\":\"聿剑\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t\"containerUuid\":\"8522335580915008505\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"inputs\":{\n" + + "\t\t\t\t\t\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"outputs\":{\n" + + "\t\t\t\t\t\"nodeOutputs\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"data\":\"5143110377713406119\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"NodeOutput\",\n" + + "\t\t\t\t\t\t\t\"refTableName\":\"f_ge_shell1\",\n" + + "\t\t\t\t\t\t\t\"isDefault\":true\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t},\n" + + "\t\t\t{\n" + + "\t\t\t\t\"recurrence\":\"Normal\",\n" + + "\t\t\t\t\"id\":\"7495526614688319692\",\n" + + "\t\t\t\t\"timeout\":0,\n" + + "\t\t\t\t\"instanceMode\":\"T+1\",\n" + + "\t\t\t\t\"rerunMode\":\"Allowed\",\n" + + "\t\t\t\t\"rerunTimes\":3,\n" + + "\t\t\t\t\"rerunInterval\":180000,\n" + + "\t\t\t\t\"datasource\":{\n" + + "\t\t\t\t\t\"name\":\"odps_first\",\n" + + "\t\t\t\t\t\"type\":\"odps\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"script\":{\n" + + "\t\t\t\t\t\"path\":\"聿剑/flow/flow6/f_mc_sql1\",\n" + + "\t\t\t\t\t\"runtime\":{\n" + + "\t\t\t\t\t\t\"command\":\"ODPS_SQL\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t\"content\":\"--MaxCompute SQL\\n--********************************************************************--\\n--author: " + + "聿剑\\n--create time: 2024-04-09 10:53:58\\n--********************************************************************--\\nSELECT " + + "'${flow_bizdate}';\\n\",\n" + + "\t\t\t\t\t\"id\":\"5724702094894912201\",\n" + + "\t\t\t\t\t\"parameters\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"name\":\"flow_bizdate\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"Variable\",\n" + + "\t\t\t\t\t\t\t\"scope\":\"NodeParameter\",\n" + + "\t\t\t\t\t\t\t\"type\":\"System\",\n" + + "\t\t\t\t\t\t\t\"value\":\"$[yyyymmdd-1]\",\n" + + "\t\t\t\t\t\t\t\"id\":\"6584333807719816392\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"trigger\":{\n" + + "\t\t\t\t\t\"type\":\"Scheduler\",\n" + + "\t\t\t\t\t\"id\":\"8888865284073976707\",\n" + + "\t\t\t\t\t\"cron\":\"00 00 00 * * ?\",\n" + + "\t\t\t\t\t\"startTime\":\"1970-01-01 00:00:00\",\n" + + "\t\t\t\t\t\"endTime\":\"9999-01-01 00:00:00\",\n" + + "\t\t\t\t\t\"timezone\":\"Asia/Shanghai\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"runtimeResource\":{\n" + + "\t\t\t\t\t\"resourceGroup\":\"group_2\",\n" + + "\t\t\t\t\t\"id\":\"5623679673296125496\",\n" + + "\t\t\t\t\t\"resourceGroupId\":\"2\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"name\":\"f_mc_sql1\",\n" + + "\t\t\t\t\"owner\":\"064152\",\n" + + "\t\t\t\t\"metadata\":{\n" + + "\t\t\t\t\t\"owner\":{\n" + + "\t\t\t\t\t\t\"userId\":\"064152\",\n" + + "\t\t\t\t\t\t\"userName\":\"聿剑\"\n" + + "\t\t\t\t\t},\n" + + "\t\t\t\t\t\"containerUuid\":\"8522335580915008505\"\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"inputs\":{\n" + + "\t\t\t\t\t\n" + + "\t\t\t\t},\n" + + "\t\t\t\t\"outputs\":{\n" + + "\t\t\t\t\t\"nodeOutputs\":[\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"data\":\"7495526614688319692\",\n" + + "\t\t\t\t\t\t\t\"artifactType\":\"NodeOutput\",\n" + + "\t\t\t\t\t\t\t\"refTableName\":\"f_mc_sql1\",\n" + + "\t\t\t\t\t\t\t\"isDefault\":true\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"flow\":[\n" + + "\t\t\t{\n" + + "\t\t\t\t\"nodeId\":\"5143110377713406119\",\n" + + "\t\t\t\t\"depends\":[\n" + + "\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\"type\":\"Normal\",\n" + + "\t\t\t\t\t\t\"output\":\"7495526614688319692\"\n" + + "\t\t\t\t\t}\n" + + "\t\t\t\t]\n" + + "\t\t\t}\n" + + "\t\t],\n" + + "\t\t\"variables\":[\n" + + "\t\t\t{\n" + + "\t\t\t\t\"name\":\"flow_bizdate\",\n" + + "\t\t\t\t\"artifactType\":\"Variable\",\n" + + "\t\t\t\t\"scope\":\"NodeParameter\",\n" + + "\t\t\t\t\"type\":\"System\",\n" + + "\t\t\t\t\"value\":\"$[yyyymmdd-1]\"\n" + + "\t\t\t}\n" + + "\t\t]\n" + + "\t},\n" + + "\t\"metadata\":{\n" + + "\t\t\"owner\":{\n" + + "\t\t\t\"userId\":\"064152\",\n" + + "\t\t\t\"userName\":\"聿剑\"\n" + + "\t\t},\n" + + "\t\t\"name\":\"fullflow2\",\n" + + "\t\t\"tenantId\":\"1\",\n" + + "\t\t\"type\":\"CycleWorkflow\",\n" + + "\t\t\"uuid\":\"8522335580915008505\",\n" + + "\t\t\"projectId\":\"23620\"\n" + + "\t}\n" + + "}"; + Specification resp = SpecUtil.parseToDomain(spec); + log.info("resp: {}", resp); + Assert.assertNotNull(resp); + } } \ No newline at end of file diff --git a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/codemode/CodeModelFactoryTest.java b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/codemode/CodeModelFactoryTest.java index 592e467..fc2da11 100644 --- a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/codemode/CodeModelFactoryTest.java +++ b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/codemode/CodeModelFactoryTest.java @@ -23,12 +23,14 @@ import com.aliyun.dataworks.common.spec.domain.dw.codemodel.CodeModel; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.CodeModelFactory; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.ControllerJoinCode; +import com.aliyun.dataworks.common.spec.domain.dw.codemodel.DefaultJsonFormCode; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.EmrCode; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.EmrJobType; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.EmrProperty; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.MultiLanguageScriptingCode; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.OdpsSparkCode; import com.aliyun.dataworks.common.spec.domain.dw.codemodel.PlainTextCode; +import com.aliyun.dataworks.common.spec.domain.dw.types.CodeProgramType; import com.aliyun.dataworks.common.spec.utils.GsonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -107,4 +109,56 @@ public void testMultiLanguageCode() { c.setSourceCode("select 1"); System.out.println("emr hive template: " + GsonUtils.toJsonString(c.getTemplate())); } + + @Test + public void testDefaultJsonFormCode() { + String code = "{\n" + + " \"content\": \"IMPORT FOREIGN SCHEMA shanghai_onlineTest_simple LIMIT TO (wq_test_dataworks_pt_001) from SERVER " + + "odps_server INTO public OPTIONS(prefix 'tmp_foreign_', suffix 'xozi4mmb', if_table_exist 'error',if_unsupported_type 'error');" + + "\\nDROP TABLE IF EXISTS \\\"public\\\".tmp_holo_8gwvxopb_wqtest;\\nBEGIN;\\nCREATE TABLE IF NOT EXISTS \\\"public\\\"" + + ".tmp_holo_8gwvxopb_wqtest (\\n \\\"f1\\\" text NOT NULL,\\n \\\"f2\\\" text NOT NULL,\\n \\\"f4\\\" text NOT NULL,\\n \\\"f5\\\" " + + "text NOT NULL,\\n \\\"f3\\\" text NOT NULL,\\n \\\"f6\\\" text NOT NULL,\\n \\\"f7\\\" text NOT NULL,\\n \\\"f10\\\" text NOT NULL," + + "\\n \\\"ds\\\" bigint NOT NULL,\\n \\\"pt\\\" text NOT NULL\\n);\\nCALL SET_TABLE_PROPERTY('\\\"public\\\"" + + ".tmp_holo_8gwvxopb_wqtest', 'orientation', 'column');\\ncomment on column \\\"public\\\".tmp_holo_8gwvxopb_wqtest.pt is '分区字段';" + + "\\nCOMMIT;\\nINSERT INTO \\\"public\\\".tmp_holo_8gwvxopb_wqtest\\nSELECT \\n CAST(\\\"f1\\\" as text),\\n CAST(\\\"f2\\\" as " + + "text),\\n CAST(\\\"f4\\\" as text),\\n CAST(\\\"f5\\\" as text),\\n CAST(\\\"f3\\\" as text),\\n CAST(\\\"f6\\\" as " + + "text),\\n CAST(\\\"f7\\\" as text),\\n CAST(\\\"f10\\\" as text),\\n CAST(\\\"ds\\\" as bigint),\\n CAST(\\\"pt\\\" as " + + "text)\\nFROM \\\"public\\\".tmp_foreign_wq_test_dataworks_pt_001xozi4mmb\\nWHERE pt='${bizdate}';\\nDROP FOREIGN TABLE IF EXISTS " + + "\\\"public\\\".tmp_foreign_wq_test_dataworks_pt_001xozi4mmb;BEGIN;\\nDROP TABLE IF EXISTS \\\"public\\\".wqtest;\\nALTER TABLE " + + "\\\"public\\\".tmp_holo_8gwvxopb_wqtest RENAME TO wqtest;\\nCOMMIT;\\n\",\n" + + " \"extraContent\": \"{\\\"connId\\\":\\\"yongxunqa_holo_shanghai\\\",\\\"dbName\\\":\\\"yongxunqa_hologres_db\\\"," + + "\\\"syncType\\\":1,\\\"extendProjectName\\\":\\\"shanghai_onlineTest_simple\\\",\\\"schemaName\\\":\\\"public\\\"," + + "\\\"tableName\\\":\\\"wqtest\\\",\\\"partitionColumn\\\":\\\"\\\",\\\"orientation\\\":\\\"column\\\"," + + "\\\"columns\\\":[{\\\"name\\\":\\\"f1\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false," + + "\\\"holoName\\\":\\\"f1\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f2\\\",\\\"comment\\\":\\\"\\\"," + + "\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f2\\\",\\\"holoType\\\":\\\"text\\\"}," + + "{\\\"name\\\":\\\"f4\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f4\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f5\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f5\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f3\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f3\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f6\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f6\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f7\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f7\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f10\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f10\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"ds\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"BIGINT\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"ds\\\"," + + "\\\"holoType\\\":\\\"bigint\\\"},{\\\"name\\\":\\\"pt\\\",\\\"comment\\\":\\\"分区字段\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"pt\\\",\\\"holoType\\\":\\\"text\\\"}],\\\"serverName\\\":\\\"odps_server\\\"," + + "\\\"extendTableName\\\":\\\"wq_test_dataworks_pt_001\\\",\\\"foreignSchemaName\\\":\\\"public\\\",\\\"foreignTableName\\\":\\\"\\\"," + + "\\\"instanceId\\\":\\\"yongxunqa_holo_shanghai\\\",\\\"engineType\\\":\\\"Hologres\\\",\\\"clusteringKey\\\":[]," + + "\\\"bitmapIndexKey\\\":[],\\\"segmentKey\\\":[],\\\"dictionaryEncoding\\\":[]}\"\n" + + " }"; + CodeModel codeModel = CodeModelFactory.getCodeModel(CodeProgramType.HOLOGRES_SYNC_DATA.name(), code); + log.info("content: {}", codeModel.getCodeModel().getContent()); + log.info("extraContent: {}", codeModel.getCodeModel().getExtraContent()); + Assert.assertTrue(StringUtils.startsWith(codeModel.getCodeModel().getContent(), "IMPORT FOREIGN SCHEMA shanghai_on")); + + CodeModel newCode = CodeModelFactory.getCodeModel(CodeProgramType.HOLOGRES_SYNC_DATA.name(), "{}"); + newCode.getCodeModel().setExtraContent("xx").setContent("select 1"); + log.info("content: {}", newCode.getContent()); + log.info("rawContent: {}", newCode.getRawContent()); + Assert.assertEquals("xx", newCode.getCodeModel().getExtraContent()); + Assert.assertEquals("select 1", newCode.getContent()); + Assert.assertEquals("{\"extraContent\":\"xx\",\"content\":\"select 1\"}", newCode.getRawContent()); + } } diff --git a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapterTest.java b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapterTest.java index 64f3662..40d0cd5 100644 --- a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapterTest.java +++ b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeAdapterTest.java @@ -25,6 +25,7 @@ import com.aliyun.dataworks.common.spec.domain.DataWorksWorkflowSpec; import com.aliyun.dataworks.common.spec.domain.Specification; import com.aliyun.dataworks.common.spec.domain.enums.DependencyType; +import com.aliyun.dataworks.common.spec.domain.enums.NodeRecurrenceType; import com.aliyun.dataworks.common.spec.domain.noref.SpecDepend; import com.aliyun.dataworks.common.spec.domain.noref.SpecFlowDepend; import com.aliyun.dataworks.common.spec.domain.ref.SpecNode; @@ -33,6 +34,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; @@ -96,6 +98,11 @@ public void testGetDependentType() throws IOException { Assert.assertNotNull(depInfo); Assert.assertEquals(DwNodeDependentTypeInfo.USER_DEFINE_AND_SELF, depInfo.getDependentType()); Assert.assertEquals(3, CollectionUtils.size(depInfo.getDependentNodeIdList())); + + DataWorksNodeAdapter adapter = new DataWorksNodeAdapter(specObj, specObj.getSpec().getNodes().get(0)); + Assert.assertNotNull(adapter.getInputs()); + Assert.assertTrue(adapter.getInputs().stream().filter(in -> in instanceof SpecNodeOutput) + .anyMatch(in -> StringUtils.equals("test_node_1", ((SpecNodeOutput)in).getRefTableName()))); } private DwNodeDependentTypeInfo getDwNodeDependentTypeInfo(Specification specification) { @@ -130,6 +137,10 @@ public void testDowhile() throws IOException { Assert.assertNotNull(dowhile.getDoWhile()); Assert.assertNotNull(dowhile.getDoWhile().getSpecWhile()); Assert.assertNotNull(dowhile.getDoWhile().getNodes()); + + DataWorksNodeAdapter dataWorksNodeAdapter = new DataWorksNodeAdapter(specObj, dowhile.getDoWhile().getSpecWhile()); + System.out.println(dataWorksNodeAdapter.getCode()); + System.out.println(dataWorksNodeAdapter.getInputs()); } @Test @@ -141,6 +152,7 @@ public void testForeach() throws IOException { System.out.println(spec); Specification specObj = SpecUtil.parseToDomain(spec); Assert.assertNotNull(specObj); + System.out.println(SpecUtil.writeToSpec(specObj)); DataWorksWorkflowSpec specification = specObj.getSpec(); Assert.assertNotNull(specification); @@ -151,6 +163,15 @@ public void testForeach() throws IOException { Assert.assertNotNull(foreach.getForeach()); Assert.assertNotNull(foreach.getForeach().getNodes()); + Assert.assertEquals(3, CollectionUtils.size(foreach.getInnerNodes())); + Assert.assertNotNull(foreach.getInnerFlow()); + + ListUtils.emptyIfNull(specObj.getSpec().getNodes().get(0).getInnerNodes()).forEach(inner -> { + DataWorksNodeAdapter adapter = new DataWorksNodeAdapter(specObj, inner); + log.info("name: {}, inputs: {}, outputs: {}", inner.getName(), adapter.getInputs(), adapter.getOutputs()); + Assert.assertTrue(CollectionUtils.isNotEmpty(adapter.getOutputs())); + Assert.assertEquals(inner.getId(), ((SpecNodeOutput)adapter.getOutputs().get(0)).getData()); + }); } @Test @@ -174,6 +195,7 @@ public void testShell() throws IOException { log.info("para value: {}", adapter.getParaValue()); Assert.assertNotNull(adapter.getParaValue()); Assert.assertEquals("111111 222222", adapter.getParaValue()); + Assert.assertEquals(3, (int)adapter.getNodeType()); } @Test @@ -205,6 +227,8 @@ public void testPyOdps2() throws IOException { Assert.assertNotNull(adapter.getExtConfig()); Assert.assertTrue(adapter.getExtConfig().contains(DataWorksNodeAdapter.IGNORE_BRANCH_CONDITION_SKIP)); Assert.assertFalse(adapter.getExtConfig().contains(DataWorksNodeAdapter.TIMEOUT)); + + Assert.assertEquals(0, (int)adapter.getNodeType()); } @Test @@ -225,12 +249,15 @@ public void testGetDependentTypeWithOutputs() { spec.setSpec(dwSpec); SpecNode node = new SpecNode(); node.setId("1"); + node.setRecurrence(NodeRecurrenceType.PAUSE); DataWorksNodeAdapter dataWorksNodeAdapter = new DataWorksNodeAdapter(spec, node); DwNodeDependentTypeInfo info = dataWorksNodeAdapter.getDependentType(null); Assert.assertNotNull(info); Assert.assertEquals(info.getDependentType(), DwNodeDependentTypeInfo.USER_DEFINE); Assert.assertNotNull(info.getDependentNodeOutputList()); Assert.assertTrue(info.getDependentNodeOutputList().contains("output1")); + + Assert.assertEquals(2, (int)dataWorksNodeAdapter.getNodeType()); } @Test @@ -665,4 +692,31 @@ public void testParamNode() { Assert.assertTrue(adapter.getOutputContexts().stream().filter(oc -> oc.getKey().equals("p1")) .anyMatch(oc -> oc.getValueExpr().equals("ppppp111"))); } + + @Test + public void testManual() throws IOException { + String spec = IOUtils.toString( + Objects.requireNonNull(DataWorksNodeAdapterTest.class.getClassLoader().getResource("nodemodel/manual.json")), + StandardCharsets.UTF_8); + + System.out.println(spec); + Specification specObj = SpecUtil.parseToDomain(spec); + Assert.assertNotNull(specObj); + + DataWorksWorkflowSpec specification = specObj.getSpec(); + Assert.assertNotNull(specification); + Assert.assertEquals(1, CollectionUtils.size(specification.getNodes())); + + SpecNode shellNode = specification.getNodes().get(0); + Assert.assertNotNull(shellNode); + + shellNode.setIgnoreBranchConditionSkip(true); + shellNode.setTimeout(0); + + DataWorksNodeAdapter adapter = new DataWorksNodeAdapter(specObj, shellNode); + log.info("para value: {}", adapter.getParaValue()); + Assert.assertNotNull(adapter.getParaValue()); + Assert.assertEquals("2=222222 1=111111", adapter.getParaValue()); + Assert.assertEquals(1, (int)adapter.getNodeType()); + } } diff --git a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapterTest.java b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapterTest.java index 90b2bed..8a5ef23 100644 --- a/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapterTest.java +++ b/spec/src/test/java/com/aliyun/dataworks/common/spec/domain/dw/nodemodel/DataWorksNodeCodeAdapterTest.java @@ -39,7 +39,9 @@ import com.aliyun.dataworks.common.spec.domain.ref.runtime.emr.EmrJobConfig; import com.aliyun.dataworks.common.spec.domain.ref.runtime.emr.EmrJobExecuteMode; import com.aliyun.dataworks.common.spec.domain.ref.runtime.emr.EmrJobSubmitMode; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; @@ -47,6 +49,7 @@ * @author 聿剑 * @date 2023/11/9 */ +@Slf4j public class DataWorksNodeCodeAdapterTest { @Test public void testAssignCode() { @@ -268,4 +271,55 @@ public void testEmrSpecNodeAdapter() { Assert.assertTrue(CollectionUtils.isNotEmpty(cm.getCodeModel().getLauncher().getAllocationSpec().entrySet())); Assert.assertNotNull(cm.getCodeModel().getProperties()); } + + @Test + public void testHologresDataSyncNode() { + String code = "{\n" + + " \"content\": \"IMPORT FOREIGN SCHEMA shanghai_onlineTest_simple LIMIT TO (wq_test_dataworks_pt_001) from SERVER " + + "odps_server INTO public OPTIONS(prefix 'tmp_foreign_', suffix 'xozi4mmb', if_table_exist 'error',if_unsupported_type 'error');" + + "\\nDROP TABLE IF EXISTS \\\"public\\\".tmp_holo_8gwvxopb_wqtest;\\nBEGIN;\\nCREATE TABLE IF NOT EXISTS \\\"public\\\"" + + ".tmp_holo_8gwvxopb_wqtest (\\n \\\"f1\\\" text NOT NULL,\\n \\\"f2\\\" text NOT NULL,\\n \\\"f4\\\" text NOT NULL,\\n \\\"f5\\\" " + + "text NOT NULL,\\n \\\"f3\\\" text NOT NULL,\\n \\\"f6\\\" text NOT NULL,\\n \\\"f7\\\" text NOT NULL,\\n \\\"f10\\\" text NOT NULL," + + "\\n \\\"ds\\\" bigint NOT NULL,\\n \\\"pt\\\" text NOT NULL\\n);\\nCALL SET_TABLE_PROPERTY('\\\"public\\\"" + + ".tmp_holo_8gwvxopb_wqtest', 'orientation', 'column');\\ncomment on column \\\"public\\\".tmp_holo_8gwvxopb_wqtest.pt is '分区字段';" + + "\\nCOMMIT;\\nINSERT INTO \\\"public\\\".tmp_holo_8gwvxopb_wqtest\\nSELECT \\n CAST(\\\"f1\\\" as text),\\n CAST(\\\"f2\\\" as " + + "text),\\n CAST(\\\"f4\\\" as text),\\n CAST(\\\"f5\\\" as text),\\n CAST(\\\"f3\\\" as text),\\n CAST(\\\"f6\\\" as " + + "text),\\n CAST(\\\"f7\\\" as text),\\n CAST(\\\"f10\\\" as text),\\n CAST(\\\"ds\\\" as bigint),\\n CAST(\\\"pt\\\" as " + + "text)\\nFROM \\\"public\\\".tmp_foreign_wq_test_dataworks_pt_001xozi4mmb\\nWHERE pt='${bizdate}';\\nDROP FOREIGN TABLE IF EXISTS " + + "\\\"public\\\".tmp_foreign_wq_test_dataworks_pt_001xozi4mmb;BEGIN;\\nDROP TABLE IF EXISTS \\\"public\\\".wqtest;\\nALTER TABLE " + + "\\\"public\\\".tmp_holo_8gwvxopb_wqtest RENAME TO wqtest;\\nCOMMIT;\\n\",\n" + + " \"extraContent\": \"{\\\"connId\\\":\\\"yongxunqa_holo_shanghai\\\",\\\"dbName\\\":\\\"yongxunqa_hologres_db\\\"," + + "\\\"syncType\\\":1,\\\"extendProjectName\\\":\\\"shanghai_onlineTest_simple\\\",\\\"schemaName\\\":\\\"public\\\"," + + "\\\"tableName\\\":\\\"wqtest\\\",\\\"partitionColumn\\\":\\\"\\\",\\\"orientation\\\":\\\"column\\\"," + + "\\\"columns\\\":[{\\\"name\\\":\\\"f1\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false," + + "\\\"holoName\\\":\\\"f1\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f2\\\",\\\"comment\\\":\\\"\\\"," + + "\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f2\\\",\\\"holoType\\\":\\\"text\\\"}," + + "{\\\"name\\\":\\\"f4\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f4\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f5\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f5\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f3\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f3\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f6\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f6\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f7\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f7\\\"," + + "\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"f10\\\",\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"f10\\\",\\\"holoType\\\":\\\"text\\\"},{\\\"name\\\":\\\"ds\\\"," + + "\\\"comment\\\":\\\"\\\",\\\"type\\\":\\\"BIGINT\\\",\\\"allowNull\\\":false,\\\"holoName\\\":\\\"ds\\\"," + + "\\\"holoType\\\":\\\"bigint\\\"},{\\\"name\\\":\\\"pt\\\",\\\"comment\\\":\\\"分区字段\\\",\\\"type\\\":\\\"STRING\\\"," + + "\\\"allowNull\\\":false,\\\"holoName\\\":\\\"pt\\\",\\\"holoType\\\":\\\"text\\\"}],\\\"serverName\\\":\\\"odps_server\\\"," + + "\\\"extendTableName\\\":\\\"wq_test_dataworks_pt_001\\\",\\\"foreignSchemaName\\\":\\\"public\\\",\\\"foreignTableName\\\":\\\"\\\"," + + "\\\"instanceId\\\":\\\"yongxunqa_holo_shanghai\\\",\\\"engineType\\\":\\\"Hologres\\\",\\\"clusteringKey\\\":[]," + + "\\\"bitmapIndexKey\\\":[],\\\"segmentKey\\\":[],\\\"dictionaryEncoding\\\":[]}\"\n" + + " }"; + SpecNode node = new SpecNode(); + SpecScript scr = new SpecScript(); + SpecScriptRuntime rt = new SpecScriptRuntime(); + rt.setCommand(CodeProgramType.HOLOGRES_SYNC_DATA.name()); + scr.setRuntime(rt); + scr.setContent(code); + node.setScript(scr); + DataWorksNodeCodeAdapter codeAdapter = new DataWorksNodeCodeAdapter(node); + String codeContent = codeAdapter.getCode(); + log.info("code content: {}", codeContent); + Assert.assertTrue(StringUtils.startsWith(codeContent, "IMPORT FOREIGN SCHEMA shanghai_on")); + } } diff --git a/spec/src/test/resources/manual_flow.json b/spec/src/test/resources/manual_flow.json index 497a83d..73cbf71 100644 --- a/spec/src/test/resources/manual_flow.json +++ b/spec/src/test/resources/manual_flow.json @@ -1,6 +1,7 @@ { "version": "1.0.0", "kind": "ManualWorkflow", + "id": 1234, "scripts": [ { "id": "file1", diff --git a/spec/src/test/resources/nodemodel/all_depend_types.json b/spec/src/test/resources/nodemodel/all_depend_types.json index 83d848e..7a67764 100644 --- a/spec/src/test/resources/nodemodel/all_depend_types.json +++ b/spec/src/test/resources/nodemodel/all_depend_types.json @@ -5,7 +5,7 @@ "nodes": [ { "id": "53046d47cb5842ec960921e4cdc28a73", - "recurrence": "Normal", + "recurrence": "Pause", "timeout": 3, "instanceMode": "T+1", "rerunMode": "Allowed", @@ -145,7 +145,8 @@ "depends": [ { "type": "Normal", - "output": "5a550dd3c0fb40e5b1455ff3dd998da7" + "output": "5a550dd3c0fb40e5b1455ff3dd998da7", + "refTableName": "test_node_1" }, { "type": "CrossCycleDependsOnOtherNode", diff --git a/spec/src/test/resources/nodemodel/assignment.json b/spec/src/test/resources/nodemodel/assignment.json index bd7dcd9..89c1e54 100644 --- a/spec/src/test/resources/nodemodel/assignment.json +++ b/spec/src/test/resources/nodemodel/assignment.json @@ -5,7 +5,7 @@ "nodes": [ { "id": "c6a3bbe198fd4031bbe71ad3e66ceb91", - "recurrence": "Normal", + "recurrence": "Skip", "timeout": 3, "instanceMode": "T+1", "rerunMode": "Allowed", diff --git a/spec/src/test/resources/nodemodel/dide_shell.json b/spec/src/test/resources/nodemodel/dide_shell.json index 9735421..e43f434 100644 --- a/spec/src/test/resources/nodemodel/dide_shell.json +++ b/spec/src/test/resources/nodemodel/dide_shell.json @@ -5,7 +5,7 @@ "nodes": [ { "id": "c6a3bbe198fd4031bbe71ad3e66ceb91", - "recurrence": "Normal", + "recurrence": "Skip", "timeout": 3, "instanceMode": "T+1", "rerunMode": "Allowed", diff --git a/spec/src/test/resources/nodemodel/dowhile.json b/spec/src/test/resources/nodemodel/dowhile.json index 0da1505..fb1da0b 100644 --- a/spec/src/test/resources/nodemodel/dowhile.json +++ b/spec/src/test/resources/nodemodel/dowhile.json @@ -166,6 +166,8 @@ "rerunInterval": 18000, "script": { "path": "/循环节点0/cycle_end", + "content": "select True", + "language": "odps-sql", "runtime": { "engine": "GENERAL", "command": "CONTROLLER_CYCLE_END" diff --git a/spec/src/test/resources/nodemodel/foreach.json b/spec/src/test/resources/nodemodel/foreach.json index ca96089..2979ed4 100644 --- a/spec/src/test/resources/nodemodel/foreach.json +++ b/spec/src/test/resources/nodemodel/foreach.json @@ -182,7 +182,7 @@ "nodeId": "8401efef76224eacbf28cc284b11a788", "depends": [ { - "nodeId": "d0e36d269ed0414d9acd08149f360129", + "output": "d0e36d269ed0414d9acd08149f360129", "type": "Normal" } ] @@ -191,7 +191,7 @@ "nodeId": "227b06c3ab0549e3b77731b0c828dcec", "depends": [ { - "nodeId": "8401efef76224eacbf28cc284b11a788", + "output": "8401efef76224eacbf28cc284b11a788", "type": "Normal" } ] diff --git a/spec/src/test/resources/nodemodel/manual.json b/spec/src/test/resources/nodemodel/manual.json new file mode 100644 index 0000000..819d0ac --- /dev/null +++ b/spec/src/test/resources/nodemodel/manual.json @@ -0,0 +1,109 @@ +{ + "version": "1.1.0", + "kind": "ManualWorkflow", + "spec": { + "nodes": [ + { + "id": "c6a3bbe198fd4031bbe71ad3e66ceb91", + "recurrence": "Normal", + "timeout": 3, + "instanceMode": "T+1", + "rerunMode": "Allowed", + "rerunTimes": 3, + "rerunInterval": 180000, + "script": { + "path": "聿剑测试/通用节点测试/dide_shell", + "language": "odps", + "content": "select 1", + "runtime": { + "engine": "MaxCompute", + "command": "PYODPS2" + }, + "parameters": [ + { + "artifactType": "Variable", + "name": "2", + "scope": "NodeParameter", + "type": "System", + "value": "222222", + "node": { + "output": "cd0a7f7e21ef49e6bebeb98438c9ebe9" + } + }, + { + "artifactType": "Variable", + "name": "1", + "scope": "NodeParameter", + "type": "System", + "value": "111111", + "node": { + "output": "cd0a7f7e21ef49e6bebeb98438c9ebe9" + } + } + ] + }, + "trigger": { + "type": "Manual" + }, + "runtimeResource": { + "resourceGroup": "group_2", + "resourceGroupId": "2" + }, + "name": "dide_shell", + "owner": "064152", + "inputs": { + "variables": [ + { + "artifactType": "Variable", + "name": "sql_assign_0", + "scope": "NodeContext", + "type": "NodeOutput", + "value": "${outputs}", + "node": { + "output": "cd0a7f7e21ef49e6bebeb98438c9ebe9" + } + } + ] + }, + "outputs": { + "variables": [ + { + "artifactType": "Variable", + "name": "sql_assign_1", + "scope": "NodeContext", + "type": "NodeOutput", + "value": "${outputs}", + "node": { + "output": "c6a3bbe198fd4031bbe71ad3e66ceb91" + } + } + ], + "outputs": [ + { + "artifactType": "Output", + "data": "c6a3bbe198fd4031bbe71ad3e66ceb91", + "refTableName": "sql_assign_1" + } + ] + }, + "functions": [], + "fileResources": [] + } + ], + "flow": [ + { + "nodeId": "c6a3bbe198fd4031bbe71ad3e66ceb91", + "depends": [ + { + "type": "Normal", + "output": "f6cbb79aa27540dc9d3f055a639436b1" + }, + { + "type": "Normal", + "output": "cd0a7f7e21ef49e6bebeb98438c9ebe9" + } + ] + } + ] + } +} \ No newline at end of file