mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 02:29:03 +00:00 
			
		
		
		
	Fix#10584: Update Lineage API to support lineage from table to dashboard data model (#11489)
* Fix: Update Lineage API to support lineage from table to dashboard data model * Minor change
This commit is contained in:
		
							parent
							
								
									8607d30b1e
								
							
						
					
					
						commit
						b2f2320145
					
				| @ -265,7 +265,7 @@ public interface CollectionDAO { | |||||||
|   WorkflowDAO workflowDAO(); |   WorkflowDAO workflowDAO(); | ||||||
| 
 | 
 | ||||||
|   @CreateSqlObject |   @CreateSqlObject | ||||||
|   DataModelDAO dataModelDAO(); |   DataModelDAO dashboardDataModelDAO(); | ||||||
| 
 | 
 | ||||||
|   interface DashboardDAO extends EntityDAO<Dashboard> { |   interface DashboardDAO extends EntityDAO<Dashboard> { | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ public class DashboardDataModelRepository extends EntityRepository<DashboardData | |||||||
|         DashboardDataModelResource.COLLECTION_PATH, |         DashboardDataModelResource.COLLECTION_PATH, | ||||||
|         Entity.DASHBOARD_DATA_MODEL, |         Entity.DASHBOARD_DATA_MODEL, | ||||||
|         DashboardDataModel.class, |         DashboardDataModel.class, | ||||||
|         dao.dataModelDAO(), |         dao.dashboardDataModelDAO(), | ||||||
|         dao, |         dao, | ||||||
|         DATA_MODEL_PATCH_FIELDS, |         DATA_MODEL_PATCH_FIELDS, | ||||||
|         DATA_MODEL_UPDATE_FIELDS, |         DATA_MODEL_UPDATE_FIELDS, | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ import java.util.List; | |||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| import org.jdbi.v3.sqlobject.transaction.Transaction; | import org.jdbi.v3.sqlobject.transaction.Transaction; | ||||||
|  | import org.openmetadata.schema.ColumnsEntityInterface; | ||||||
| import org.openmetadata.schema.api.lineage.AddLineage; | import org.openmetadata.schema.api.lineage.AddLineage; | ||||||
| import org.openmetadata.schema.entity.data.Table; | import org.openmetadata.schema.entity.data.Table; | ||||||
| import org.openmetadata.schema.type.ColumnLineage; | import org.openmetadata.schema.type.ColumnLineage; | ||||||
| @ -89,12 +90,13 @@ public class LineageRepository { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     List<ColumnLineage> columnsLineage = details.getColumnsLineage(); |     List<ColumnLineage> columnsLineage = details.getColumnsLineage(); | ||||||
|     if (!from.getType().equals(Entity.TABLE) || !to.getType().equals(Entity.TABLE)) { |     if (areValidEntities(from, to)) { | ||||||
|       throw new IllegalArgumentException("Column level lineage is only allowed between two tables."); |       throw new IllegalArgumentException( | ||||||
|  |           "Column level lineage is only allowed between two tables or from table to dashboard."); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Table fromTable = dao.tableDAO().findEntityById(from.getId()); |     Table fromTable = dao.tableDAO().findEntityById(from.getId()); | ||||||
|     Table toTable = dao.tableDAO().findEntityById(to.getId()); |     ColumnsEntityInterface toTable = getToEntity(to); | ||||||
|     if (columnsLineage != null) { |     if (columnsLineage != null) { | ||||||
|       for (ColumnLineage columnLineage : columnsLineage) { |       for (ColumnLineage columnLineage : columnsLineage) { | ||||||
|         for (String fromColumn : columnLineage.getFromColumns()) { |         for (String fromColumn : columnLineage.getFromColumns()) { | ||||||
| @ -112,6 +114,17 @@ public class LineageRepository { | |||||||
|     return JsonUtils.pojoToJson(details); |     return JsonUtils.pojoToJson(details); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private ColumnsEntityInterface getToEntity(EntityReference from) throws IOException { | ||||||
|  |     return from.getType().equals(Entity.TABLE) | ||||||
|  |         ? dao.tableDAO().findEntityById(from.getId()) | ||||||
|  |         : dao.dashboardDataModelDAO().findEntityById(from.getId()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private boolean areValidEntities(EntityReference from, EntityReference to) { | ||||||
|  |     return !from.getType().equals(Entity.TABLE) | ||||||
|  |         || !(to.getType().equals(Entity.TABLE) || to.getType().equals(Entity.DASHBOARD_DATA_MODEL)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @Transaction |   @Transaction | ||||||
|   public boolean deleteLineage(String fromEntity, String fromId, String toEntity, String toId) throws IOException { |   public boolean deleteLineage(String fromEntity, String fromId, String toEntity, String toId) throws IOException { | ||||||
|     // Validate from entity |     // Validate from entity | ||||||
|  | |||||||
| @ -41,8 +41,11 @@ import org.junit.jupiter.api.Order; | |||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.jupiter.api.TestInfo; | import org.junit.jupiter.api.TestInfo; | ||||||
| import org.junit.jupiter.api.TestMethodOrder; | import org.junit.jupiter.api.TestMethodOrder; | ||||||
|  | import org.openmetadata.schema.EntityInterface; | ||||||
|  | import org.openmetadata.schema.api.data.CreateDashboardDataModel; | ||||||
| import org.openmetadata.schema.api.data.CreateTable; | import org.openmetadata.schema.api.data.CreateTable; | ||||||
| import org.openmetadata.schema.api.lineage.AddLineage; | import org.openmetadata.schema.api.lineage.AddLineage; | ||||||
|  | import org.openmetadata.schema.entity.data.DashboardDataModel; | ||||||
| import org.openmetadata.schema.entity.data.Table; | import org.openmetadata.schema.entity.data.Table; | ||||||
| import org.openmetadata.schema.entity.teams.Role; | import org.openmetadata.schema.entity.teams.Role; | ||||||
| import org.openmetadata.schema.entity.teams.User; | import org.openmetadata.schema.entity.teams.User; | ||||||
| @ -56,6 +59,7 @@ import org.openmetadata.schema.type.MetadataOperation; | |||||||
| import org.openmetadata.service.Entity; | import org.openmetadata.service.Entity; | ||||||
| import org.openmetadata.service.OpenMetadataApplicationTest; | import org.openmetadata.service.OpenMetadataApplicationTest; | ||||||
| import org.openmetadata.service.resources.databases.TableResourceTest; | import org.openmetadata.service.resources.databases.TableResourceTest; | ||||||
|  | import org.openmetadata.service.resources.datamodels.DashboardDataModelResourceTest; | ||||||
| import org.openmetadata.service.resources.teams.RoleResource; | import org.openmetadata.service.resources.teams.RoleResource; | ||||||
| import org.openmetadata.service.resources.teams.RoleResourceTest; | import org.openmetadata.service.resources.teams.RoleResourceTest; | ||||||
| import org.openmetadata.service.resources.teams.UserResourceTest; | import org.openmetadata.service.resources.teams.UserResourceTest; | ||||||
| @ -66,9 +70,12 @@ import org.openmetadata.service.util.TestUtils; | |||||||
| public class LineageResourceTest extends OpenMetadataApplicationTest { | public class LineageResourceTest extends OpenMetadataApplicationTest { | ||||||
|   public static final List<Table> TABLES = new ArrayList<>(); |   public static final List<Table> TABLES = new ArrayList<>(); | ||||||
|   public static final int TABLE_COUNT = 10; |   public static final int TABLE_COUNT = 10; | ||||||
| 
 |  | ||||||
|   private static final String DATA_STEWARD_ROLE_NAME = "DataSteward"; |   private static final String DATA_STEWARD_ROLE_NAME = "DataSteward"; | ||||||
| 
 | 
 | ||||||
|  |   private static DashboardDataModel DATA_MODEL; | ||||||
|  | 
 | ||||||
|  |   private static Table TABLE_DATA_MODEL_LINEAGE; | ||||||
|  | 
 | ||||||
|   @BeforeAll |   @BeforeAll | ||||||
|   public static void setup(TestInfo test) throws IOException, URISyntaxException { |   public static void setup(TestInfo test) throws IOException, URISyntaxException { | ||||||
|     // Create TABLE_COUNT number of tables |     // Create TABLE_COUNT number of tables | ||||||
| @ -78,6 +85,14 @@ public class LineageResourceTest extends OpenMetadataApplicationTest { | |||||||
|       CreateTable createTable = tableResourceTest.createRequest(test, i); |       CreateTable createTable = tableResourceTest.createRequest(test, i); | ||||||
|       TABLES.add(tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS)); |       TABLES.add(tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS)); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // Entities to test lineage DashboardDataModel <-> Table | ||||||
|  |     DashboardDataModelResourceTest dashboardResourceTest = new DashboardDataModelResourceTest(); | ||||||
|  |     CreateDashboardDataModel createDashboardDataModel = dashboardResourceTest.createRequest(test); | ||||||
|  |     DATA_MODEL = dashboardResourceTest.createEntity(createDashboardDataModel, ADMIN_AUTH_HEADERS); | ||||||
|  |     CreateTable createTable = tableResourceTest.createRequest(test, TABLE_COUNT); | ||||||
|  |     createTable.setColumns(createDashboardDataModel.getColumns()); | ||||||
|  |     TABLE_DATA_MODEL_LINEAGE = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Order(1) |   @Order(1) | ||||||
| @ -286,6 +301,32 @@ public class LineageResourceTest extends OpenMetadataApplicationTest { | |||||||
|     addEdge(TABLES.get(0), TABLES.get(1), details, ADMIN_AUTH_HEADERS); |     addEdge(TABLES.get(0), TABLES.get(1), details, ADMIN_AUTH_HEADERS); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @Order(4) | ||||||
|  |   @Test | ||||||
|  |   void putLineageFromDashboardDataModelToTable() throws HttpResponseException { | ||||||
|  | 
 | ||||||
|  |     // Add column lineage dashboard.d1 -> table.c1 | ||||||
|  |     LineageDetails details = new LineageDetails(); | ||||||
|  |     String d1c1FQN = DATA_MODEL.getColumns().get(0).getFullyQualifiedName(); | ||||||
|  |     String d1c2FQN = DATA_MODEL.getColumns().get(1).getFullyQualifiedName(); | ||||||
|  |     String d1c3FQN = DATA_MODEL.getColumns().get(2).getFullyQualifiedName(); | ||||||
|  |     String c1c1FQN = TABLE_DATA_MODEL_LINEAGE.getColumns().get(0).getFullyQualifiedName(); | ||||||
|  |     String c1c2FQN = TABLE_DATA_MODEL_LINEAGE.getColumns().get(1).getFullyQualifiedName(); | ||||||
|  |     String c1c3FQN = TABLE_DATA_MODEL_LINEAGE.getColumns().get(2).getFullyQualifiedName(); | ||||||
|  | 
 | ||||||
|  |     List<ColumnLineage> lineage = details.getColumnsLineage(); | ||||||
|  |     lineage.add(new ColumnLineage().withFromColumns(List.of(c1c1FQN)).withToColumn(d1c1FQN)); | ||||||
|  |     lineage.add(new ColumnLineage().withFromColumns(List.of(c1c2FQN)).withToColumn(d1c2FQN)); | ||||||
|  |     lineage.add(new ColumnLineage().withFromColumns(List.of(c1c3FQN)).withToColumn(d1c3FQN)); | ||||||
|  | 
 | ||||||
|  |     addEdge(TABLE_DATA_MODEL_LINEAGE, DATA_MODEL, details, ADMIN_AUTH_HEADERS); | ||||||
|  | 
 | ||||||
|  |     assertResponse( | ||||||
|  |         () -> addEdge(DATA_MODEL, TABLE_DATA_MODEL_LINEAGE, details, ADMIN_AUTH_HEADERS), | ||||||
|  |         BAD_REQUEST, | ||||||
|  |         "Column level lineage is only allowed between two tables or from table to dashboard."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public Edge getEdge(Table from, Table to) { |   public Edge getEdge(Table from, Table to) { | ||||||
|     return getEdge(from.getId(), to.getId(), null); |     return getEdge(from.getId(), to.getId(), null); | ||||||
|   } |   } | ||||||
| @ -298,7 +339,8 @@ public class LineageResourceTest extends OpenMetadataApplicationTest { | |||||||
|     addEdge(from, to, null, ADMIN_AUTH_HEADERS); |     addEdge(from, to, null, ADMIN_AUTH_HEADERS); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private void addEdge(Table from, Table to, LineageDetails details, Map<String, String> authHeaders) |   private void addEdge( | ||||||
|  |       EntityInterface from, EntityInterface to, LineageDetails details, Map<String, String> authHeaders) | ||||||
|       throws HttpResponseException { |       throws HttpResponseException { | ||||||
|     if (details != null) { |     if (details != null) { | ||||||
|       details.setSqlQuery("select *;"); |       details.setSqlQuery("select *;"); | ||||||
|  | |||||||
| @ -0,0 +1,28 @@ | |||||||
|  | /* | ||||||
|  |  *  Copyright 2022 Collate | ||||||
|  |  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  *  you may not use this file except in compliance with the License. | ||||||
|  |  *  You may obtain a copy of the License at | ||||||
|  |  *  http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  *  Unless required by applicable law or agreed to in writing, software | ||||||
|  |  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  *  See the License for the specific language governing permissions and | ||||||
|  |  *  limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.openmetadata.schema; | ||||||
|  | 
 | ||||||
|  | import java.util.List; | ||||||
|  | import org.openmetadata.schema.type.Column; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Interface to be implemented by entities with a list of Column and FullyQualifiedName. It is used when adding lineage | ||||||
|  |  * between different entities. | ||||||
|  |  */ | ||||||
|  | public interface ColumnsEntityInterface { | ||||||
|  | 
 | ||||||
|  |   String getFullyQualifiedName(); | ||||||
|  | 
 | ||||||
|  |   List<Column> getColumns(); | ||||||
|  | } | ||||||
| @ -6,7 +6,7 @@ | |||||||
|   "description": "Dashboard Data Model entity definition. Data models are the schemas used to build dashboards, charts, or other data assets.", |   "description": "Dashboard Data Model entity definition. Data models are the schemas used to build dashboards, charts, or other data assets.", | ||||||
|   "type": "object", |   "type": "object", | ||||||
|   "javaType": "org.openmetadata.schema.entity.data.DashboardDataModel", |   "javaType": "org.openmetadata.schema.entity.data.DashboardDataModel", | ||||||
|   "javaInterfaces": ["org.openmetadata.schema.EntityInterface"], |   "javaInterfaces": ["org.openmetadata.schema.EntityInterface", "org.openmetadata.schema.ColumnsEntityInterface"], | ||||||
|   "definitions": { |   "definitions": { | ||||||
|     "dataModelType": { |     "dataModelType": { | ||||||
|       "javaType": "org.openmetadata.schema.type.DataModelType", |       "javaType": "org.openmetadata.schema.type.DataModelType", | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
|   "type": "object", |   "type": "object", | ||||||
|   "javaType": "org.openmetadata.schema.entity.data.Table", |   "javaType": "org.openmetadata.schema.entity.data.Table", | ||||||
|   "javaInterfaces": [ |   "javaInterfaces": [ | ||||||
|     "org.openmetadata.schema.EntityInterface" |     "org.openmetadata.schema.EntityInterface", "org.openmetadata.schema.ColumnsEntityInterface" | ||||||
|   ], |   ], | ||||||
|   "definitions": { |   "definitions": { | ||||||
|     "entityName": { |     "entityName": { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nahuel
						Nahuel