All Products
Search
Document Center

DataWorks:Best practices: Basic practices for table management using OpenAPI

Last Updated:May 31, 2025

DataWorks provides various API operations. You can use the OpenAPI module and other open capabilities of DataWorks to implement various business scenarios based on your requirements. This topic uses metadata table management as an example to describe how to connect metadata API operations to perform operations such as querying lists, querying table details.

Prerequisites

Before you perform operations that are described in this topic, we recommend that you review the following topics to learn more about the basic capabilities and concepts of the OpenAPI module in DataWorks:

I. Query table lists

This practice describes how to use OpenAPI to query the table list under a MaxCompute project or schema, with support for paged queries. The main procedure is as follows:

Backend: Use MetaServiceProxy to query table details

  1. Write a ListTables method in MetaServiceProxy to process frontend parameters and send requests to the ListTables API operation to obtain the list information of metadata tables.

    • Supported query parameters: Parent-level metadata entity ID, name (fuzzy matching), comment (fuzzy matching), type, sorting parameters, and paging parameters.

      Note

      Refer to Metadata entity-related concepts. The parent-level metadata entity ID may exist in two formats.

      • Parent-level metadata entity ID as MaxCompute project ID, format: maxcompute-project:${Alibaba Cloud account ID}::{projectName}

      • Parent-level metadata entity ID as MaxCompute schema ID, format: maxcompute-schema:${Alibaba Cloud account ID}::{projectName}:{schemaName}

    • Query results include: Total number of data tables, paging information, and for each data table: ID, parent-level metadata entity ID, table name, comment, database name, schema name, type, partition field list, creation time, and modification time.

    • Obtain the Alibaba Cloud account ID: Whether you are using a RAM user or an Alibaba Cloud account, you can view the Alibaba Cloud account ID in the upper-right corner of the page.

      1. Log on to the DataWorks console using your Alibaba Cloud account or RAM user.

      2. Hover your mouse over the profile picture in the upper-right corner to view the Alibaba Cloud account ID.

        • Alibaba Cloud account: The Account ID is the Alibaba Cloud account ID that you need to obtain.

        • RAM user: You can directly view the Alibaba Cloud Account ID.

    • Sample code:

      /**
       * @author dataworks demo
       */
      @Service
      public class MetaServiceProxy {
      
          @Autowired
          private DataWorksOpenApiClient dataWorksOpenApiClient;
      
          /**
           * DataWorks OpenAPI : ListTables
           *
           * @param listTablesDto
           */
          public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) {
              try {
                  Client client = dataWorksOpenApiClient.createClient();
                  ListTablesRequest listTablesRequest = new ListTablesRequest();
                  // Parent entity ID, metadata entity concept description.
                  listTablesRequest.setParentMetaEntityId(listTablesDto.getParentMetaEntityId());
                  // Table name, supports fuzzy matching
                  listTablesRequest.setName(listTablesDto.getName());
                  // Table comment, supports fuzzy matching
                  listTablesRequest.setComment(listTablesDto.getComment());
                  // Table type, returns all types by default
                  listTablesRequest.setTableTypes(listTablesDto.getTableTypes());
                  // Sort by, CreateTime (default) / ModifyTime / Name / TableType
                  listTablesRequest.setSortBy(listTablesDto.getSortBy());
                  // Sort direction, supports Asc (default) / Desc
                  listTablesRequest.setOrder(listTablesDto.getOrder());
                  // Page number, default is 1
                  listTablesRequest.setPageNumber(listTablesDto.getPageNumber());
                  // Page size, default is 10, maximum 100
                  listTablesRequest.setPageSize(listTablesDto.getPageSize());
                  ListTablesResponse response = client.listTables(listTablesRequest);
                  // Get the total number of tables that meet the requirements
                  System.out.println(response.getBody().getPagingInfo().getTotalCount());
                  for (Table table : response.getBody().getPagingInfo().getTables()) {
                      // Table ID
                      System.out.println(table.getId());
                      // Parent entity ID of the table
                      System.out.println(table.getParentMetaEntityId());
                      // Table name
                      System.out.println(table.getName());
                      // Table comment
                      System.out.println(table.getComment());
                      // Database/MaxCompute project name that the table belongs to
                      System.out.println(table.getId().split(":")[3]);
                      // Schema name that the table belongs to
                      System.out.println(table.getId().split(":")[4]);
                      // Table type
                      System.out.println(table.getTableType());
                      // Table creation time
                      System.out.println(table.getCreateTime());
                      // Table modification time
                      System.out.println(table.getModifyTime());
                      // Table partition field list
                      System.out.println(table.getPartitionKeys());
                  }
                  return response.getBody().getPagingInfo();
              } catch (TeaException error) {
                  // Handle exceptions with caution based on your actual business scenario and do not ignore exceptions in your project. The error messages displayed in this example are for reference only.
                  // The error message
                  System.out.println(error.getMessage());
                  // Display the URL for troubleshooting.
                  System.out.println(error.getData().get("Recommend"));
                  com.aliyun.teautil.Common.assertAsString(error.message);
              } catch (Exception _error) {
                  TeaException error = new TeaException(_error.getMessage(), _error);
                  // Handle exceptions with caution based on your actual business scenario and do not ignore exceptions in your project. The error messages displayed in this example are for reference only.
                  // The error message
                  System.out.println(error.getMessage());
                  // Display the URL for troubleshooting.
                  System.out.println(error.getData().get("Recommend"));
                  com.aliyun.teautil.Common.assertAsString(error.message);
              }
              return null;
          }
      }
  2. Add a listTables method in MetaRestController as an entry point for frontend access.

    /**
     * Demonstrate how to build a custom metadata platform by using the DataWorks OpenAPI module
     *
     * @author dataworks demo
     */
    @RestController
    @RequestMapping("/meta")
    public class MetaRestController {
    
        @Autowired
        private MetaServiceProxy metaService;
    
        /**
         * Query data in the metatables by page.
         *
         * @param listTablesDto
         * @return {@link ListTablesResponseBodyPagingInfo}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listTables")
        public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) {
            System.out.println("listTablesDto = " + listTablesDto);
            return metaService.listTables(listTablesDto);
        }
    }

Frontend: Display table metadata information

import React from "react";
import cn from "classnames";
import {
  Table,
  Form,
  Field,
  Input,
  Button,
  Pagination,
  Dialog,
  Card,
  Grid,
  Select,
} from "@alifd/next";
import type { TableListInput, TableEntity } from "../services/meta";
import * as services from "../services";
import DetailContent from "./detailContent";
import LineageContent from "./lineageContent";
import classes from "../styles/app.module.css";
import moment from "moment";

export interface Props {}

const { Column } = Table;
const { Item } = Form;
const { Header, Content } = Card;
const { Row, Col } = Grid;
const { Option } = Select;

const App: React.FunctionComponent = () => {
  const field = Field.useField();
  const [datasource, setDatasource] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [total, setTotal] = React.useState(0);
  const [current, setCurrent] = React.useState(1);
  const onSubmit = React.useCallback(
    (pageNumber: number = 1) => {
      field.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        setLoading(true);
        try {
          const response = await services.meta.getTableList({
            pageNumber,
            ...values,
          } as TableListInput);
          setDatasource(response.tables);
          setCurrent(pageNumber);
          setTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
          setLoading(false);
        }
      });
    },
    [field]
  );
  const onViewDetail = React.useCallback((record: TableEntity) => {
    Dialog.show({
      title: "Data Table Details",
      content: ,
      shouldUpdatePosition: true,
      footerActions: ["ok"],
    });
  }, []);
  const onViewLineage = React.useCallback((record: TableEntity) => {
    Dialog.show({
      title: "Table Lineage",
      content: ,
      shouldUpdatePosition: true,
      footerActions: ["ok"],
    });
  }, []);
  React.useEffect(() => {
    field.setValue("dataSourceType", "odps");
  }, []);
  return (
    






















Table
View
Materialized View





Creation Time
Modification Time
Name
Type





Ascending
Descending




 onSubmit()}>
                Query
              



No Data
              }
            >
               {
                  const parts = value.split(":");
                  return parts.length > 1 ? parts[parts.length - 3] : "";
                }}
              />
               {
                  const parts = value.split(":");
                  return parts.length > 1 ? parts[parts.length - 2] : "";
                }}
              />
              


 {
                  return value != null && value.length > 0 ? "Yes" : "No";
                }}
              />
               {
                  return moment(value).format("YYYY-MM-DD HH:mm:ss");
                }}
              />
               {
                  return moment(value).format("YYYY-MM-DD HH:mm:ss");
                }}
              />
               (
                  
 onViewDetail(record)}
                      text
                    >
                      View Details
                    
 onViewLineage(record)}
                      text
                    >
                      View Table Lineage
                    

                )}
              />
            




After completing the code development above, you can deploy and run the project code locally. You can enter the MaxCompute project ID and other parameters to query and obtain the list of all tables in the project, with support for paged query.

2. Lookup table details

The following practice will combine three OpenAPI methods: GetTable, ListColumns, and ListPartitions to query table details, and use the UpdateColumnBusinessMetadata API to update the business description of fields. The practice workflow is as follows.

Backend: Use MetaServiceProxy to query table details

  1. Create getTable, listColumns, and listPartitions methods in MetaServiceProxy to call the OpenAPI service to obtain the basic information, field information, and partition information of a table. Create the updateColumnBusinessMetadata method to update the business description of fields

    /**
     * @author dataworks demo
     */
    @Service
    public class MetaServiceProxy {
    
        @Autowired
        private DataWorksOpenApiClient dataWorksOpenApiClient;
    
        /**
         * DataWorks OpenAPI : getTableDto
         *
         * @param getTableDto
         */
        public Table getTable(GetTableDto getTableDto) {
            try {
                Client client = dataWorksOpenApiClient.createClient();
                GetTableRequest getTableRequest = new GetTableRequest();
                // Table ID
                getTableRequest.setId(getTableDto.getId());
                // Whether to return business metadata
                getTableRequest.setIncludeBusinessMetadata(getTableDto.getIncludeBusinessMetadata());
    
                GetTableResponse response = client.getTable(getTableRequest);
                // Corresponding data table
                Table table = response.getBody().getTable();
                // Table ID
                System.out.println(table.getId());
                // Parent entity ID of the table
                System.out.println(table.getParentMetaEntityId());
                // Table name
                System.out.println(table.getName());
                // Table comment
                System.out.println(table.getComment());
                // Database / MaxCompute project name that the table belongs to
                System.out.println(table.getId().split(":")[3]);
                // Schema name that the table belongs to
                System.out.println(table.getId().split(":")[4]);
                // Table type
                System.out.println(table.getTableType());
                // Table creation time
                System.out.println(table.getCreateTime());
                // Table modification time
                System.out.println(table.getModifyTime());
                // Whether it is a partitioned table
                System.out.println(table.getPartitionKeys() != null && !table.getPartitionKeys().isEmpty());
                // Technical metadata of the table
                TableTechnicalMetadata technicalMetadata = table.getTechnicalMetadata();
                // Table owner (name)
                System.out.println(technicalMetadata.getOwner());
                // Whether it is a compressed table
                System.out.println(technicalMetadata.getCompressed());
                // Input format (supported by HMS, DLF, etc.)
                System.out.println(technicalMetadata.getInputFormat());
                // Output format (supported by HMS, DLF, etc.)
                System.out.println(technicalMetadata.getOutputFormat());
                // Class used for table serialization (supported by HMS, DLF, etc.)
                System.out.println(technicalMetadata.getSerializationLibrary());
                // Table storage location (supported by HMS, DLF, etc.)
                System.out.println(technicalMetadata.getLocation());
                // Other parameters
                Map parameters = technicalMetadata.getParameters();
                // Last DDL update time (millisecond timestamp), only supported by MaxCompute type
                System.out.println(parameters.get("lastDDLTime"));
                // Lifecycle (in days), only supported by MaxCompute type
                System.out.println(parameters.get("lifecycle"));
                // Storage volume (in bytes), not real-time, only supported by MaxCompute type
                System.out.println(parameters.get("dataSize"));
                // Business metadata of the table
                TableBusinessMetadata businessMetadata = table.getBusinessMetadata();
                if (businessMetadata != null) {
                    // Usage instructions
                    System.out.println(businessMetadata.getReadme());
                    // Custom tag information
                    List tags = businessMetadata.getTags();
                    if (tags != null && !tags.isEmpty()) {
                        for (TableBusinessMetadataTags tag : tags) {
                            System.out.println(tag.getKey() + ":" + tag.getValue());
                        }
                    }
                    // Upstream production tasks
                    List upstreamTasks = businessMetadata.getUpstreamTasks();
                    if (upstreamTasks != null && !upstreamTasks.isEmpty()) {
                        for (TableBusinessMetadataUpstreamTasks upstreamTask : upstreamTasks) {
                            // Task ID and task name, you can get task details through GetTask API
                            System.out.println(upstreamTask.getId() + ":" + upstreamTask.getName());
                        }
                    }
                    // Category information
                    List> categories = businessMetadata.getCategories();
                    if (categories != null && !categories.isEmpty()) {
                        // Traverse multi-level categories
                        for (List
  2. In MetaRestController, provide four methods: getTable, listColumns, listPartitions, and updateColumnBusinessMetadata for frontend calls.

    /**
     * Demonstrate how to build a custom metadata platform by using the OpenAPI module.
     *
     * @author dataworks demo
     */
    @RestController
    @RequestMapping("/meta")
    public class MetaRestController {
    
        @Autowired
        private MetaServiceProxy metaService;
    
        /**
         * Get table details
         *
         * @param getTableDto
         * @return {@link Table}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/getTable")
        public Table getTable(GetTableDto getTableDto) {
            System.out.println("getTableDto = " + getTableDto);
            return metaService.getTable(getTableDto);
        }
    
        /**
         * Paged query for field data
         *
         * @param listColumnsDto
         * @return {@link ListColumnsResponseBodyPagingInfo}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listColumns")
        public ListColumnsResponseBodyPagingInfo listColumns(ListColumnsDto listColumnsDto) {
            System.out.println("listColumnsDto = " + listColumnsDto);
            return metaService.listColumns(listColumnsDto);
        }
    
        /**
         * Update field business description
         *
         * @param updateColumnBusinessMetadataDto
         * @return {@link Boolean}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @PostMapping("/updateColumnBusinessMetadata")
        public Boolean updateColumnBusinessMetadata(@RequestBody UpdateColumnBusinessMetadataDto updateColumnBusinessMetadataDto) {
            System.out.println("updateColumnBusinessMetadataDto = " + updateColumnBusinessMetadataDto);
            return metaService.updateColumnBusinessMetadata(updateColumnBusinessMetadataDto);
        }
    
        /**
         * Paged query for partition data
         *
         * @param listPartitionsDto
         * @return {@link ListPartitionsResponseBodyPagingInfo}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listPartitions")
        public ListPartitionsResponseBodyPagingInfo listPartitions(ListPartitionsDto listPartitionsDto) {
            System.out.println("listPartitionsDto = " + listPartitionsDto);
            return metaService.listPartitions(listPartitionsDto);
        }
    }

Frontend: Display basic table information, field information, and partition information.

import React from "react";
import moment from "moment";
import {
  Form,
  Grid,
  Table,
  Pagination,
  Tag,
  Field,
  Input,
  Button,
  Select,
  Dialog,
} from "@alifd/next";
import cn from "classnames";
import * as services from "../services";
import {
  type ListColumnsInput,
  type TableColumn,
  type TableEntity,
  type TablePartition,
} from "../services/meta";
import classes from "../styles/detailContent.module.css";

export interface Props {
  item: TableEntity;
}

const formItemLayout = {
  labelCol: {
    fixedSpan: 6,
  },
  wrapperCol: {
    span: 18,
  },
  labelTextAlign: "left" as const,
  colon: true,
  className: cn(classes.formItemWrapper),
};
const { Row, Col } = Grid;
const { Item } = Form;
const { Column } = Table;
const { Option } = Select;
const DetailContent: React.FunctionComponent = (props) => {
  let columnsPageSize = 10;
  let partitionsPageSize = 5;
  const listColumnsField = Field.useField();
  const listPartitionsField = Field.useField();
  const labelAlign = "top";
  const [detail, setDetail] = React.useState>({});
  const [columns, setColumns] = React.useState>([]);
  const [partitions, setPartitions] = React.useState>(
    []
  );
  const [columnsTotal, setColumnsTotal] = React.useState(0);
  const [partitionTotal, setPartitionTotal] = React.useState(0);
  const [editingKey, setEditingKey] = React.useState("");
  const [editingDescription, setEditingDescription] =
    React.useState("");

  const [updateColumnDialogVisible, setUpdateColumnDialogVisible] =
    React.useState(false);
  const onUpdateColumnDialogOpen = () => {
    setUpdateColumnDialogVisible(true);
  };
  const onUpdateColumnDialogOk = () => {
    handleColumnDescriptionSave();
    onUpdateColumnDialogClose();
  };
  const onUpdateColumnDialogClose = () => {
    setUpdateColumnDialogVisible(false);
  };
  const handleEditColumnDescription = async (record: TableColumn) => {
    setEditingKey(record.id);
    setEditingDescription(record.businessMetadata?.description);
    onUpdateColumnDialogOpen();
  };
  const handleColumnDesrciptionChange = async (e: string) => {
    setEditingDescription(e);
  };
  const handleColumnDescriptionSave = async () => {
    try {
      const res = await services.meta.updateColumnDescription({
        id: editingKey,
        description: editingDescription,
      });
      if (!res) {
        console.log("Request update failed");
      }
    } catch (e) {
      console.error("Update failed", e);
    } finally {
      listColumns();
      setEditingKey("");
    }
  };
  const getTableDetail = React.useCallback(async () => {
    const response = await services.meta.getTableDetail({
      id: props.item.id,
      includeBusinessMetadata: true,
    });
    setDetail(response);
  }, [props.item.id]);
  const listColumns = React.useCallback(
    async (pageNumber: number = 1) => {
      listColumnsField.setValue("tableId", props.item.id);
      listColumnsField.setValue("pageSize", columnsPageSize);
      listColumnsField.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        try {
          const response = await services.meta.getMetaTableColumns({
            pageNumber,
            ...values,
          } as ListColumnsInput);
          setColumns(response.columns);
          setColumnsTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
        }
      });
    },
    [listColumnsField]
  );
  const listPartitions = React.useCallback(
    async (pageNumber: number = 1) => {
      listPartitionsField.setValue("tableId", props.item.id);
      listPartitionsField.setValue("pageSize", partitionsPageSize);
      listPartitionsField.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        try {
          const response = await services.meta.getTablePartition({
            pageNumber,
            ...values,
          } as ListColumnsInput);
          setPartitions(response.partitionList);
          setPartitionTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
        }
      });
    },
    [listPartitionsField]
  );
  React.useEffect(() => {
    if (props.item.id) {
      getTableDetail();
      listColumns();
      listPartitions();
    }
  }, [props.item.id]);
  return (
    




 handleColumnDesrciptionChange(e.toString())}
              >
            







                {detail.id}
              






                {detail?.id?.split(":").slice(-3, -2)[0]}
              




                {detail?.id?.split(":").slice(-2, -1)[0]}
              






                {detail.name}
              




                {detail.comment}
              






                {detail.tableType}
              




                {detail.technicalMetadata?.compressed ? "Yes" : "No"}
              






                {detail.businessMetadata?.extension?.projectId}
              




                {detail.businessMetadata?.extension?.envType === "Dev"
                  ? "Development"
                  : "Production"}
              






                {detail.technicalMetadata?.parameters["lifecycle"]
                  ? `${detail.technicalMetadata?.parameters["lifecycle"]} days`
                  : "Permanent"}
              



{`${detail.technicalMetadata?.parameters["dataSize"]} Bytes`}






                {moment(
                  Number(detail.technicalMetadata?.parameters["lastDDLTime"])
                ).format("YYYY-MM-DD HH:mm:ss")}
              




                {detail.partitionKeys != null && detail.partitionKeys.length > 0
                  ? "Yes"
                  : "No"}
              






                {detail.businessMetadata?.tags?.map((tag, index) => (
                  

                      {tag.key + (tag.value ? ":" + tag.value : "")}
                    

                ))}
              






                {detail.businessMetadata?.categories?.map((category, index) => (
                  

                      {category.map((obj) => obj.name).join("->")}
                    

                ))}
              






                {detail.businessMetadata?.upstreamTasks?.map((task, index) => (
                  

                      {task.name + " (" + task.id + ")"}
                    

                ))}
              







                  {detail.businessMetadata?.extension?.viewCount}
                






                  {detail.businessMetadata?.extension?.readCount}
                








                  {detail.businessMetadata?.extension?.favorCount}
                







                {moment(detail.createTime).format("YYYY-MM-DD HH:mm:ss")}
              




                {moment(detail.modifyTime).format("YYYY-MM-DD HH:mm:ss")}
              

















Position
Name





Ascending
Descending




 listColumns()}>
                    Query
                  







 (
                
{value?.description}
 handleEditColumnDescription(record)}
                    size="small"
                    type="primary"
                    text
                    style={{ marginLeft: 8 }}
                  >
                    Edit
                  

              )}
            />
             (value ? "Yes" : "")}
            />
             (value ? "Yes" : "")}
            />
             (value ? "Yes" : "")}
            />
            














Creation Time
Modification Time
Partition Name
Partition Record Count
Partition Size





Ascending
Descending




 listPartitions()}>
                    Query
                  



Not a partitioned table}>
            
 moment(value).format("YYYY-MM-DD HH:mm:ss")}
            />
             moment(value).format("YYYY-MM-DD HH:mm:ss")}
            />
             `${value} bytes`}
            />
            





  );
};

export default DetailContent;

After you write the preceding code, you can deploy and run the project code offline. For deployment and running operations, see Common operation: Local deployment and running.

3. Query table lineage

When the definition of a table changes, you can use DataWorks OpenAPI to analyze the lineage of upstream and downstream tasks to find the nodes associated with the table.

Note

Use ListLineages to query the upstream and downstream lineage of a metatable.

Backend: Use MetaServiceProxy to query table lineage

  1. Create the listTableLineages method in MetaServiceProxy to call the OpenAPI service to obtain the upstream and downstream lineage entities and relationship information of a table.

    /**
     * @author dataworks demo
     */
    @Service
    public class MetaServiceProxy {
    
        @Autowired
        private DataWorksOpenApiClient dataWorksOpenApiClient;
    
        /**
         * DataWorks OpenAPI : ListLineages
         *
         * @param listTableLineagesDto
         */
        public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) {
            try {
                Client client = dataWorksOpenApiClient.createClient();
                ListLineagesRequest listLineagesRequest = new ListLineagesRequest();
                // Lineage query direction, whether it is upstream
                boolean needUpstream = "up".equalsIgnoreCase(listTableLineagesDto.getDirection());
                // Table ID, which can be obtained through the ListTables interface
                // Partition name, supports fuzzy matching
                if (needUpstream) {
                    listLineagesRequest.setDstEntityId(listTableLineagesDto.getTableId());
                    listLineagesRequest.setSrcEntityName(listTableLineagesDto.getName());
                } else {
                    listLineagesRequest.setSrcEntityId(listTableLineagesDto.getTableId());
                    listLineagesRequest.setDstEntityName(listTableLineagesDto.getName());
                }
                // Sort method, supports Name
                listLineagesRequest.setSortBy(listTableLineagesDto.getSortBy());
                // Sort direction, supports Asc (default) / Desc
                listLineagesRequest.setOrder(listTableLineagesDto.getOrder());
                // Page number, default is 1
                listLineagesRequest.setPageNumber(listTableLineagesDto.getPageNumber());
                // Page size, default is 10, maximum is 100
                listLineagesRequest.setPageSize(listTableLineagesDto.getPageSize());
                // Whether to return specific lineage relationship information
                listLineagesRequest.setNeedAttachRelationship(true);
                ListLineagesResponse response = client.listLineages(listLineagesRequest);
                // Get the total number of lineage entities that meet the requirements
                System.out.println(response.getBody().getPagingInfo().getTotalCount());
                if (response.getBody().getPagingInfo().getLineages() == null) {
                    response.getBody().getPagingInfo().setLineages(Collections.emptyList());
                }
                if (response.getBody().getPagingInfo().getLineages() != null) {
                    for (ListLineagesResponseBodyPagingInfoLineages lineage : response.getBody().getPagingInfo().getLineages()) {
                        LineageEntity entity;
                        // Get the corresponding lineage entity
                        if (needUpstream) {
                            entity = lineage.getSrcEntity();
                        } else {
                            entity = lineage.getDstEntity();
                        }
                        // Entity ID
                        System.out.println(entity.getId());
                        // Entity name
                        System.out.println(entity.getName());
                        // Entity property information
                        System.out.println(entity.getAttributes());
                        // List of lineage relationships between entities
                        List relationships = lineage.getRelationships();
                        if (relationships != null) {
                            relationships.forEach(relationship -> {
                                // Lineage relationship ID
                                System.out.println(relationship.getId());
                                // Creation time
                                System.out.println(relationship.getCreateTime());
                                // Task corresponding to the lineage relationship
                                LineageTask task = relationship.getTask();
                                if (task != null) {
                                    // Task ID, for DataWorks tasks, you can get task details through GetTask
                                    System.out.println(task.getId());
                                    // Task type
                                    System.out.println(task.getType());
                                    // Task property information
                                    Map attributes = task.getAttributes();
                                    if (attributes != null) {
                                        // For DataWorks tasks, you can get task and instance property information
                                        // Project ID
                                        System.out.println(attributes.get("projectId"));
                                        // Task ID
                                        String taskId = attributes.containsKey("taskId") ? attributes.get("taskId") : attributes.get("nodeId");
                                        System.out.println(taskId);
                                        // Instance ID
                                        String taskInstanceId = attributes.containsKey("taskInstanceId") ? attributes.get("taskInstanceId") : attributes.get("jobId");
                                        System.out.println(taskInstanceId);
                                    }
                                }
                            });
                        }
                    }
                    return response.getBody().getPagingInfo();
                }
            } catch (TeaException error) {
                // Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided only for reference.
                // The error message
                System.out.println(error.getMessage());
                // Display the troubleshooting information
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided only for reference.
                // The error message
                System.out.println(error.getMessage());
                // Display the troubleshooting information
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
            }
            return null;
        }
    }
  2. In MetaRestController, provide the listTableLineages method for frontend calls.

    /**
     * Demonstrate how to build a custom metadata platform by using the OpenAPI module
     *
     * @author dataworks demo
     */
    @RestController
    @RequestMapping("/meta")
    public class MetaRestController {
    
        @Autowired
        private MetaServiceProxy metaService;
    
        /**
         * Query the data lineage of the metatable
         *
         * @param listTableLineagesDto
         * @return
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listTableLineages")
        public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) {
            System.out.println("listTableLineagesDto = " + listTableLineagesDto);
            return metaService.listTableLineages(listTableLineagesDto);
        }
    
    }

Frontend: Display table lineage

import React from 'react';
import Graphin, { Behaviors } from '@antv/graphin';
import type { IUserNode, IUserEdge, GraphinData, GraphEvent } from '@antv/graphin';
import cn from 'classnames';
import * as services from '../services';
import type { TableEntity, ListTableLineageOutput, LineageEntity } from '../services/meta';
import classes from '../styles/lineageContent.module.css';
import '@antv/graphin/dist/index.css';

export interface Props {
  item: TableEntity;
}

function transNode(entity: LineageEntity | TableEntity, direction?: string): IUserNode {
  return {
    id: entity.id,
    label: entity.name,
    data: {
      ...entity,
      direction,
    },
    style: {
      label: {
        value: entity.name,
      }
    },
  }
}
function transEdge(source: LineageEntity | TableEntity, target: LineageEntity | TableEntity): IUserEdge {
  return {
    source: source.id,
    target: target.id,
    style: {
      keyshape: {
        lineDash: [8, 4],
        lineWidth: 2,
      },
      animate: {
        type: 'line-dash',
        repeat: true,
      },
    }
  };
}
function parse(
  source: LineageEntity | TableEntity,
  data: ListTableLineageOutput,
  direction: string,
): [IUserNode[], IUserEdge[]] {
  const nodes: IUserNode[] = [];
  const edges: IUserEdge[] = [];
  data.lineages.forEach((lineageRelationship) => {
    if (direction === 'down') {
      nodes.push(transNode(lineageRelationship.dstEntity, direction));
      edges.push(transEdge(source, lineageRelationship.dstEntity));
    }  else {
      nodes.push(transNode(lineageRelationship.srcEntity, direction));
      edges.push(transEdge(lineageRelationship.srcEntity, source));
    }
  });
  return [nodes, edges];
}
function mergeNodes(prev: IUserNode[], next: IUserNode[]) {
  const result: IUserNode[] = prev.slice();
  next.forEach((item) => {
    const hasValue = prev.findIndex(i => i.id === item.id) >= 0;
    !hasValue && result.push(item);
  });
  return result;
}
function mergeEdges(source: IUserEdge[], target: IUserEdge[]) {
  const result: IUserEdge[] = source.slice();
  target.forEach((item) => {
    const hasValue = source.findIndex(i => i.target === item.target && i.source === item.source) >= 0;
    !hasValue && result.push(item);
  });
  return result;
}

const { ActivateRelations, DragNode, ZoomCanvas } = Behaviors;
const LineageContent: React.FunctionComponent = (props) => {
  const ref = React.useRef();
  const [data, setData] = React.useState({ nodes: [], edges: [] });
  const getTableLineage = async (
    collection: GraphinData,
    target: TableEntity | LineageEntity,
    direction: string,
  ) => {
    if (!direction) {
      return collection;
    }
    const response = await services.meta.getTableLineage({
      direction,
      tableId: target.id,
    });
    const [nodes, edges] = parse(target, response, direction);
    collection.nodes = mergeNodes(collection.nodes!, nodes);
    collection.edges = mergeEdges(collection.edges!, edges);
    return collection;
  };
  const init = async () => {
    let nextData = Object.assign({}, data, { nodes: [transNode(props.item)] });
    nextData = await getTableLineage(nextData, props.item, 'up');
    nextData = await getTableLineage(nextData, props.item, 'down');
    setData(nextData);
    ref.current!.graph.fitCenter();
  };
  React.useEffect(() => {
    ref.current?.graph && init();
  }, [
    ref.current?.graph,
  ]);
  React.useEffect(() => {
    const graph = ref.current?.graph;
    const event = async (event: GraphEvent) => {
      const source = event.item?.get('model').data;
      let nextData = Object.assign({}, data);
      nextData = await getTableLineage(nextData, source, source.direction);
      setData(nextData);
    };
    graph?.on('node:click', event);
    return () => {
      graph?.off('node:click', event);
    };
  }, [
    data,
    ref.current?.graph,
  ]);
  return (
    
}
        layout={{
          type: 'dagre',
          rankdir: 'LR',
          align: 'DL',
          nodesep: 10,
          ranksep: 40,
        }}
      >
        




  );
};

export default LineageContent;

After you complete the code development, you can deploy and run the project code locally. For information about how to deploy and run the code, see Common operation: Deploy and run locally.

4. Find tasks corresponding to a lookup table

When the specification of a table changes, you can perform kinship analysis of downstream tasks through DataWorks OpenAPI, OpenData, or message subscription to find the nodes corresponding to the table. The specific operations are as follows.

Note

Messages currently support table changes, task changes, and more. Enterprise Edition users can connect to table change messages. When you receive a table change message, you can view the kinship relationships of the table.

Kinship tasks

  1. Use ListLineages to view the kinship relationships of the table and obtain the DataWorks task ID (and task instance ID) from the Task in the kinship relationships.

  2. Based on the obtained task ID, use GetTask to obtain the task details.

Output tasks

  1. Based on the entity ID, use the GetTable API to obtain the business metadata of the table, which includes the DataWorks task ID in the upstream output tasks.

  2. Based on the specified task ID, use GetTask to obtain the task details.

Common operations: Local deployment and running

  1. Prepare the dependencies.

    You need to prepare the following dependencies: Java 8 or later, Maven build tool, Node environment, and pnpm tool. You can run the following commands to check whether you have these environments:

    npm -v // If the installation is successful, the version is displayed in the command output. If the installation fails, an error that indicates no command is available is reported.
    java -version // If the installation is successful, the version is displayed in the command output. If the installation fails, an error that indicates no command is available is reported.
    pnpm -v // If the installation is successful, the version is displayed in the command output. If the installation fails, an error that indicates no command is available is reported.
  2. Download the project code and run the following command.

    Project code download link: meta-api-demo.zip.

    pnpm i
  3. Find the application.properties file in the backend/src/main/resources path of the example project, and modify the core parameters in the file.

    • Set the api.access-key-id and api.access-key-secret parameters to the AccessKey ID and AccessKey Secret of your Alibaba Cloud account.

      Note

      You can refer to AccessKey management (AccessKey) to obtain AccessKey and other related information of your Alibaba Cloud account.

    • Configure the api.region-id and api.endpoint parameters based on the information about the region of the API operations that you want to call.

    Other parameters can be modified according to your actual situation. The following is an example of the filled parameters.本地部署示例

  4. Run the following command in the root directory of the project to execute the example code.

    npm run dev