mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-23 00:18:06 +00:00
Add Data Product Lineage (#20400)
* Add Data Product Lineage * Migrate Data Products * add null check for entity * add data product layer * Add index specific conditions * add tests * update nlp icon * Add missing conditions --------- Co-authored-by: karanh37 <karanh37@gmail.com>
This commit is contained in:
parent
3245b2d98e
commit
7e731648ab
@ -50,6 +50,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.json.JsonPatch;
|
import javax.json.JsonPatch;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -196,6 +197,7 @@ public class LineageRepository {
|
|||||||
|
|
||||||
addServiceLineage(fromEntity, toEntity, lineageDetails, childRelationExists);
|
addServiceLineage(fromEntity, toEntity, lineageDetails, childRelationExists);
|
||||||
addDomainLineage(fromEntity, toEntity, lineageDetails, childRelationExists);
|
addDomainLineage(fromEntity, toEntity, lineageDetails, childRelationExists);
|
||||||
|
addDataProductsLineage(fromEntity, toEntity, lineageDetails, childRelationExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addServiceLineage(
|
private void addServiceLineage(
|
||||||
@ -203,44 +205,17 @@ public class LineageRepository {
|
|||||||
EntityInterface toEntity,
|
EntityInterface toEntity,
|
||||||
LineageDetails entityLineageDetails,
|
LineageDetails entityLineageDetails,
|
||||||
boolean childRelationExists) {
|
boolean childRelationExists) {
|
||||||
boolean addService =
|
if (!shouldAddServiceLineage(fromEntity, toEntity)) {
|
||||||
Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_SERVICE)
|
return;
|
||||||
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_SERVICE);
|
}
|
||||||
// Add Service Level Lineage
|
// Add Service Level Lineage
|
||||||
if (addService && fromEntity.getService() != null && toEntity.getService() != null) {
|
EntityReference fromService = fromEntity.getService();
|
||||||
EntityReference fromService = fromEntity.getService();
|
EntityReference toService = toEntity.getService();
|
||||||
EntityReference toService = toEntity.getService();
|
if (Boolean.FALSE.equals(fromService.getId().equals(toService.getId()))) {
|
||||||
if (Boolean.FALSE.equals(fromService.getId().equals(toService.getId()))) {
|
LineageDetails serviceLineageDetails =
|
||||||
CollectionDAO.EntityRelationshipObject serviceRelation =
|
getOrCreateLineageDetails(
|
||||||
dao.relationshipDAO()
|
fromService.getId(), toService.getId(), entityLineageDetails, childRelationExists);
|
||||||
.getRecord(fromService.getId(), toService.getId(), Relationship.UPSTREAM.ordinal());
|
insertLineage(fromService, toService, serviceLineageDetails);
|
||||||
LineageDetails serviceLineageDetails;
|
|
||||||
if (serviceRelation != null) {
|
|
||||||
serviceLineageDetails =
|
|
||||||
JsonUtils.readValue(serviceRelation.getJson(), LineageDetails.class);
|
|
||||||
if (!childRelationExists) {
|
|
||||||
serviceLineageDetails.withAssetEdges(serviceLineageDetails.getAssetEdges() + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
serviceLineageDetails =
|
|
||||||
new LineageDetails()
|
|
||||||
.withCreatedAt(entityLineageDetails.getCreatedAt())
|
|
||||||
.withCreatedBy(entityLineageDetails.getCreatedBy())
|
|
||||||
.withUpdatedAt(entityLineageDetails.getUpdatedAt())
|
|
||||||
.withUpdatedBy(entityLineageDetails.getUpdatedBy())
|
|
||||||
.withSource(LineageDetails.Source.CHILD_ASSETS)
|
|
||||||
.withAssetEdges(1);
|
|
||||||
}
|
|
||||||
dao.relationshipDAO()
|
|
||||||
.insert(
|
|
||||||
fromService.getId(),
|
|
||||||
toService.getId(),
|
|
||||||
fromService.getType(),
|
|
||||||
toService.getType(),
|
|
||||||
Relationship.UPSTREAM.ordinal(),
|
|
||||||
JsonUtils.pojoToJson(serviceLineageDetails));
|
|
||||||
addLineageToSearch(fromService, toService, serviceLineageDetails);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,47 +224,106 @@ public class LineageRepository {
|
|||||||
EntityInterface toEntity,
|
EntityInterface toEntity,
|
||||||
LineageDetails entityLineageDetails,
|
LineageDetails entityLineageDetails,
|
||||||
boolean childRelationExists) {
|
boolean childRelationExists) {
|
||||||
boolean addDomain =
|
|
||||||
Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_DOMAIN)
|
if (!shouldAddDomainsLineage(fromEntity, toEntity)) {
|
||||||
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_DOMAIN);
|
return;
|
||||||
// Add Service Level Lineage
|
}
|
||||||
if (addDomain && fromEntity.getDomain() != null && toEntity.getDomain() != null) {
|
|
||||||
EntityReference fromDomain = fromEntity.getDomain();
|
EntityReference fromDomain = fromEntity.getDomain();
|
||||||
EntityReference toDomain = toEntity.getDomain();
|
EntityReference toDomain = toEntity.getDomain();
|
||||||
if (Boolean.FALSE.equals(fromDomain.getId().equals(toDomain.getId()))) {
|
if (Boolean.FALSE.equals(fromDomain.getId().equals(toDomain.getId()))) {
|
||||||
CollectionDAO.EntityRelationshipObject serviceRelation =
|
LineageDetails domainLineageDetails =
|
||||||
dao.relationshipDAO()
|
getOrCreateLineageDetails(
|
||||||
.getRecord(fromDomain.getId(), toDomain.getId(), Relationship.UPSTREAM.ordinal());
|
fromDomain.getId(), toDomain.getId(), entityLineageDetails, childRelationExists);
|
||||||
LineageDetails domainLineageDetails;
|
insertLineage(fromDomain, toDomain, domainLineageDetails);
|
||||||
if (serviceRelation != null) {
|
}
|
||||||
domainLineageDetails =
|
}
|
||||||
JsonUtils.readValue(serviceRelation.getJson(), LineageDetails.class);
|
|
||||||
if (!childRelationExists) {
|
private void addDataProductsLineage(
|
||||||
domainLineageDetails.withAssetEdges(domainLineageDetails.getAssetEdges() + 1);
|
EntityInterface fromEntity,
|
||||||
}
|
EntityInterface toEntity,
|
||||||
} else {
|
LineageDetails entityLineageDetails,
|
||||||
domainLineageDetails =
|
boolean childRelationExists) {
|
||||||
new LineageDetails()
|
|
||||||
.withCreatedAt(entityLineageDetails.getCreatedAt())
|
if (!shouldAddDataProductLineage(fromEntity, toEntity)) {
|
||||||
.withCreatedBy(entityLineageDetails.getCreatedBy())
|
return;
|
||||||
.withUpdatedAt(entityLineageDetails.getUpdatedAt())
|
}
|
||||||
.withUpdatedBy(entityLineageDetails.getUpdatedBy())
|
|
||||||
.withSource(LineageDetails.Source.CHILD_ASSETS)
|
for (EntityReference fromEntityRef : fromEntity.getDataProducts()) {
|
||||||
.withAssetEdges(1);
|
for (EntityReference toEntityRef : toEntity.getDataProducts()) {
|
||||||
|
if (!fromEntityRef.getId().equals(toEntityRef.getId())) {
|
||||||
|
LineageDetails dataProductsLineageDetails =
|
||||||
|
getOrCreateLineageDetails(
|
||||||
|
fromEntityRef.getId(),
|
||||||
|
toEntityRef.getId(),
|
||||||
|
entityLineageDetails,
|
||||||
|
childRelationExists);
|
||||||
|
|
||||||
|
insertLineage(fromEntityRef, toEntityRef, dataProductsLineageDetails);
|
||||||
}
|
}
|
||||||
dao.relationshipDAO()
|
|
||||||
.insert(
|
|
||||||
fromDomain.getId(),
|
|
||||||
toDomain.getId(),
|
|
||||||
fromDomain.getType(),
|
|
||||||
toDomain.getType(),
|
|
||||||
Relationship.UPSTREAM.ordinal(),
|
|
||||||
JsonUtils.pojoToJson(domainLineageDetails));
|
|
||||||
addLineageToSearch(fromDomain, toDomain, domainLineageDetails);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldAddDataProductLineage(
|
||||||
|
EntityInterface fromEntity, EntityInterface toEntity) {
|
||||||
|
return Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_DATA_PRODUCTS)
|
||||||
|
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_DATA_PRODUCTS)
|
||||||
|
&& !nullOrEmpty(fromEntity.getDataProducts())
|
||||||
|
&& !nullOrEmpty(toEntity.getDataProducts());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldAddDomainsLineage(EntityInterface fromEntity, EntityInterface toEntity) {
|
||||||
|
return Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_DOMAIN)
|
||||||
|
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_DOMAIN)
|
||||||
|
&& fromEntity.getDomain() != null
|
||||||
|
&& toEntity.getDomain() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldAddServiceLineage(EntityInterface fromEntity, EntityInterface toEntity) {
|
||||||
|
return Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_SERVICE)
|
||||||
|
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_SERVICE)
|
||||||
|
&& fromEntity.getService() != null
|
||||||
|
&& toEntity.getService() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LineageDetails getOrCreateLineageDetails(
|
||||||
|
UUID fromId, UUID toId, LineageDetails entityLineageDetails, boolean childRelationExists) {
|
||||||
|
|
||||||
|
CollectionDAO.EntityRelationshipObject existingRelation =
|
||||||
|
dao.relationshipDAO().getRecord(fromId, toId, Relationship.UPSTREAM.ordinal());
|
||||||
|
|
||||||
|
if (existingRelation != null) {
|
||||||
|
LineageDetails lineageDetails =
|
||||||
|
JsonUtils.readValue(existingRelation.getJson(), LineageDetails.class);
|
||||||
|
if (!childRelationExists) {
|
||||||
|
lineageDetails.withAssetEdges(lineageDetails.getAssetEdges() + 1);
|
||||||
|
}
|
||||||
|
return lineageDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LineageDetails()
|
||||||
|
.withCreatedAt(entityLineageDetails.getCreatedAt())
|
||||||
|
.withCreatedBy(entityLineageDetails.getCreatedBy())
|
||||||
|
.withUpdatedAt(entityLineageDetails.getUpdatedAt())
|
||||||
|
.withUpdatedBy(entityLineageDetails.getUpdatedBy())
|
||||||
|
.withSource(LineageDetails.Source.CHILD_ASSETS)
|
||||||
|
.withAssetEdges(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertLineage(
|
||||||
|
EntityReference from, EntityReference to, LineageDetails lineageDetails) {
|
||||||
|
dao.relationshipDAO()
|
||||||
|
.insert(
|
||||||
|
from.getId(),
|
||||||
|
to.getId(),
|
||||||
|
from.getType(),
|
||||||
|
to.getType(),
|
||||||
|
Relationship.UPSTREAM.ordinal(),
|
||||||
|
JsonUtils.pojoToJson(lineageDetails));
|
||||||
|
addLineageToSearch(from, to, lineageDetails);
|
||||||
|
}
|
||||||
|
|
||||||
private String getExtendedLineageFields(boolean service, boolean domain, boolean dataProducts) {
|
private String getExtendedLineageFields(boolean service, boolean domain, boolean dataProducts) {
|
||||||
StringBuilder fieldsBuilder = new StringBuilder();
|
StringBuilder fieldsBuilder = new StringBuilder();
|
||||||
|
|
||||||
@ -535,6 +569,12 @@ public class LineageRepository {
|
|||||||
JsonNode fromEntity = entityMap.getOrDefault(fromEntityId, null);
|
JsonNode fromEntity = entityMap.getOrDefault(fromEntityId, null);
|
||||||
JsonNode toEntity = entityMap.getOrDefault(toEntityId, null);
|
JsonNode toEntity = entityMap.getOrDefault(toEntityId, null);
|
||||||
|
|
||||||
|
if (fromEntity == null || toEntity == null) {
|
||||||
|
LOG.error(
|
||||||
|
"Entity not found for IDs: fromEntityId={}, toEntityId={}", fromEntityId, toEntityId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, String> baseRow = new HashMap<>();
|
Map<String, String> baseRow = new HashMap<>();
|
||||||
baseRow.put("fromEntityFQN", getText(fromEntity, FIELD_FULLY_QUALIFIED_NAME));
|
baseRow.put("fromEntityFQN", getText(fromEntity, FIELD_FULLY_QUALIFIED_NAME));
|
||||||
baseRow.put("fromServiceName", getText(fromEntity.path(FIELD_SERVICE), FIELD_NAME));
|
baseRow.put("fromServiceName", getText(fromEntity.path(FIELD_SERVICE), FIELD_NAME));
|
||||||
@ -906,99 +946,88 @@ public class LineageRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUpExtendedLineage(EntityReference from, EntityReference to) {
|
private void cleanUpExtendedLineage(EntityReference from, EntityReference to) {
|
||||||
boolean addService =
|
boolean addService = hasField(from, FIELD_SERVICE) && hasField(to, FIELD_SERVICE);
|
||||||
Entity.entityHasField(from.getType(), FIELD_SERVICE)
|
boolean addDomain = hasField(from, FIELD_DOMAIN) && hasField(to, FIELD_DOMAIN);
|
||||||
&& Entity.entityHasField(to.getType(), FIELD_SERVICE);
|
|
||||||
boolean addDomain =
|
|
||||||
Entity.entityHasField(from.getType(), FIELD_DOMAIN)
|
|
||||||
&& Entity.entityHasField(to.getType(), FIELD_DOMAIN);
|
|
||||||
boolean addDataProduct =
|
boolean addDataProduct =
|
||||||
Entity.entityHasField(from.getType(), FIELD_DATA_PRODUCTS)
|
hasField(from, FIELD_DATA_PRODUCTS) && hasField(to, FIELD_DATA_PRODUCTS);
|
||||||
&& Entity.entityHasField(to.getType(), FIELD_DATA_PRODUCTS);
|
|
||||||
|
|
||||||
String fields = getExtendedLineageFields(addService, addDomain, addDataProduct);
|
String fields = getExtendedLineageFields(addService, addDomain, addDataProduct);
|
||||||
EntityInterface fromEntity =
|
EntityInterface fromEntity =
|
||||||
Entity.getEntity(from.getType(), from.getId(), fields, Include.ALL);
|
Entity.getEntity(from.getType(), from.getId(), fields, Include.ALL);
|
||||||
EntityInterface toEntity = Entity.getEntity(to.getType(), to.getId(), fields, Include.ALL);
|
EntityInterface toEntity = Entity.getEntity(to.getType(), to.getId(), fields, Include.ALL);
|
||||||
|
|
||||||
cleanUpServiceLineage(fromEntity, toEntity);
|
cleanUpLineage(fromEntity, toEntity, FIELD_SERVICE, EntityInterface::getService);
|
||||||
cleanUpDomainLineage(fromEntity, toEntity);
|
cleanUpLineage(fromEntity, toEntity, FIELD_DOMAIN, EntityInterface::getDomain);
|
||||||
|
cleanUpLineageForDataProducts(
|
||||||
|
fromEntity, toEntity, FIELD_DATA_PRODUCTS, EntityInterface::getDataProducts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUpServiceLineage(EntityInterface fromEntity, EntityInterface toEntity) {
|
private boolean hasField(EntityReference entity, String field) {
|
||||||
boolean hasServiceField =
|
return Entity.entityHasField(entity.getType(), field);
|
||||||
Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_SERVICE)
|
}
|
||||||
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_SERVICE);
|
|
||||||
if (hasServiceField && fromEntity.getService() != null && toEntity.getService() != null) {
|
private void cleanUpLineage(
|
||||||
EntityReference fromService = fromEntity.getService();
|
EntityInterface fromEntity,
|
||||||
EntityReference toService = toEntity.getService();
|
EntityInterface toEntity,
|
||||||
CollectionDAO.EntityRelationshipObject serviceRelation =
|
String field,
|
||||||
dao.relationshipDAO()
|
Function<EntityInterface, EntityReference> getter) {
|
||||||
.getRecord(fromService.getId(), toService.getId(), Relationship.UPSTREAM.ordinal());
|
boolean hasField =
|
||||||
LineageDetails serviceLineageDetails;
|
hasField(fromEntity.getEntityReference(), field)
|
||||||
if (serviceRelation != null) {
|
&& hasField(toEntity.getEntityReference(), field);
|
||||||
serviceLineageDetails =
|
if (!hasField) return;
|
||||||
JsonUtils.readValue(serviceRelation.getJson(), LineageDetails.class);
|
|
||||||
if (serviceLineageDetails.getAssetEdges() - 1 < 1) {
|
EntityReference fromRef = getter.apply(fromEntity);
|
||||||
dao.relationshipDAO()
|
EntityReference toRef = getter.apply(toEntity);
|
||||||
.delete(
|
processExtendedLineageCleanup(fromRef, toRef);
|
||||||
fromService.getId(),
|
}
|
||||||
fromService.getType(),
|
|
||||||
toService.getId(),
|
private void cleanUpLineageForDataProducts(
|
||||||
toService.getType(),
|
EntityInterface fromEntity,
|
||||||
Relationship.UPSTREAM.ordinal());
|
EntityInterface toEntity,
|
||||||
deleteLineageFromSearch(fromService, toService, serviceLineageDetails);
|
String field,
|
||||||
} else {
|
Function<EntityInterface, List<EntityReference>> getter) {
|
||||||
serviceLineageDetails.withAssetEdges(serviceLineageDetails.getAssetEdges() - 1);
|
boolean hasField =
|
||||||
dao.relationshipDAO()
|
hasField(fromEntity.getEntityReference(), field)
|
||||||
.insert(
|
&& hasField(toEntity.getEntityReference(), field);
|
||||||
fromService.getId(),
|
if (!hasField) return;
|
||||||
toService.getId(),
|
|
||||||
fromService.getType(),
|
for (EntityReference fromRef : getter.apply(fromEntity)) {
|
||||||
toService.getType(),
|
for (EntityReference toRef : getter.apply(toEntity)) {
|
||||||
Relationship.UPSTREAM.ordinal(),
|
processExtendedLineageCleanup(fromRef, toRef);
|
||||||
JsonUtils.pojoToJson(serviceLineageDetails));
|
|
||||||
addLineageToSearch(fromService, toService, serviceLineageDetails);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanUpDomainLineage(EntityInterface fromEntity, EntityInterface toEntity) {
|
private void processExtendedLineageCleanup(EntityReference fromRef, EntityReference toRef) {
|
||||||
boolean hasDomainField =
|
if (fromRef == null || toRef == null) return;
|
||||||
Entity.entityHasField(fromEntity.getEntityReference().getType(), FIELD_DOMAIN)
|
|
||||||
&& Entity.entityHasField(toEntity.getEntityReference().getType(), FIELD_DOMAIN);
|
CollectionDAO.EntityRelationshipObject relation =
|
||||||
if (hasDomainField && fromEntity.getDomain() != null && toEntity.getDomain() != null) {
|
dao.relationshipDAO()
|
||||||
EntityReference fromDomain = fromEntity.getDomain();
|
.getRecord(fromRef.getId(), toRef.getId(), Relationship.UPSTREAM.ordinal());
|
||||||
EntityReference toDomain = toEntity.getDomain();
|
|
||||||
CollectionDAO.EntityRelationshipObject domainRelation =
|
if (relation == null) return;
|
||||||
dao.relationshipDAO()
|
|
||||||
.getRecord(fromDomain.getId(), toDomain.getId(), Relationship.UPSTREAM.ordinal());
|
LineageDetails lineageDetails = JsonUtils.readValue(relation.getJson(), LineageDetails.class);
|
||||||
LineageDetails domainLineageDetails;
|
if (lineageDetails.getAssetEdges() - 1 < 1) {
|
||||||
if (domainRelation != null) {
|
dao.relationshipDAO()
|
||||||
domainLineageDetails = JsonUtils.readValue(domainRelation.getJson(), LineageDetails.class);
|
.delete(
|
||||||
if (domainLineageDetails.getAssetEdges() - 1 < 1) {
|
fromRef.getId(),
|
||||||
dao.relationshipDAO()
|
fromRef.getType(),
|
||||||
.delete(
|
toRef.getId(),
|
||||||
fromDomain.getId(),
|
toRef.getType(),
|
||||||
fromDomain.getType(),
|
Relationship.UPSTREAM.ordinal());
|
||||||
toDomain.getId(),
|
deleteLineageFromSearch(fromRef, toRef, lineageDetails);
|
||||||
toDomain.getType(),
|
} else {
|
||||||
Relationship.UPSTREAM.ordinal());
|
lineageDetails.withAssetEdges(lineageDetails.getAssetEdges() - 1);
|
||||||
deleteLineageFromSearch(fromDomain, toDomain, domainLineageDetails);
|
dao.relationshipDAO()
|
||||||
} else {
|
.insert(
|
||||||
domainLineageDetails.withAssetEdges(domainLineageDetails.getAssetEdges() - 1);
|
fromRef.getId(),
|
||||||
dao.relationshipDAO()
|
toRef.getId(),
|
||||||
.insert(
|
fromRef.getType(),
|
||||||
fromDomain.getId(),
|
toRef.getType(),
|
||||||
toDomain.getId(),
|
Relationship.UPSTREAM.ordinal(),
|
||||||
fromDomain.getType(),
|
JsonUtils.pojoToJson(lineageDetails));
|
||||||
toDomain.getType(),
|
addLineageToSearch(fromRef, toRef, lineageDetails);
|
||||||
Relationship.UPSTREAM.ordinal(),
|
|
||||||
JsonUtils.pojoToJson(domainLineageDetails));
|
|
||||||
addLineageToSearch(fromDomain, toDomain, domainLineageDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.openmetadata.service.migration.mysql.v170;
|
|||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.createServiceCharts;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.createServiceCharts;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNonNullColumn;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNonNullColumn;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNullColumn;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNullColumn;
|
||||||
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDataProductsLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDomainLineage;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDomainLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationServiceLineage;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationServiceLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.updateDataInsightsApplication;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.updateDataInsightsApplication;
|
||||||
@ -32,6 +33,7 @@ public class Migration extends MigrationProcessImpl {
|
|||||||
runLineageMigrationForNonNullColumn(handle);
|
runLineageMigrationForNonNullColumn(handle);
|
||||||
runMigrationServiceLineage(handle);
|
runMigrationServiceLineage(handle);
|
||||||
runMigrationForDomainLineage(handle);
|
runMigrationForDomainLineage(handle);
|
||||||
|
runMigrationForDataProductsLineage(handle);
|
||||||
|
|
||||||
// DI
|
// DI
|
||||||
createServiceCharts();
|
createServiceCharts();
|
||||||
|
@ -3,6 +3,7 @@ package org.openmetadata.service.migration.postgres.v170;
|
|||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.createServiceCharts;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.createServiceCharts;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNonNullColumn;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNonNullColumn;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNullColumn;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runLineageMigrationForNullColumn;
|
||||||
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDataProductsLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDomainLineage;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationForDomainLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationServiceLineage;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.runMigrationServiceLineage;
|
||||||
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.updateDataInsightsApplication;
|
import static org.openmetadata.service.migration.utils.v170.MigrationUtil.updateDataInsightsApplication;
|
||||||
@ -32,6 +33,7 @@ public class Migration extends MigrationProcessImpl {
|
|||||||
runLineageMigrationForNonNullColumn(handle);
|
runLineageMigrationForNonNullColumn(handle);
|
||||||
runMigrationServiceLineage(handle);
|
runMigrationServiceLineage(handle);
|
||||||
runMigrationForDomainLineage(handle);
|
runMigrationForDomainLineage(handle);
|
||||||
|
runMigrationForDataProductsLineage(handle);
|
||||||
|
|
||||||
// DI
|
// DI
|
||||||
createServiceCharts();
|
createServiceCharts();
|
||||||
|
@ -19,12 +19,14 @@ import org.openmetadata.schema.ServiceEntityInterface;
|
|||||||
import org.openmetadata.schema.dataInsight.custom.DataInsightCustomChart;
|
import org.openmetadata.schema.dataInsight.custom.DataInsightCustomChart;
|
||||||
import org.openmetadata.schema.dataInsight.custom.LineChart;
|
import org.openmetadata.schema.dataInsight.custom.LineChart;
|
||||||
import org.openmetadata.schema.dataInsight.custom.LineChartMetric;
|
import org.openmetadata.schema.dataInsight.custom.LineChartMetric;
|
||||||
|
import org.openmetadata.schema.entity.domains.DataProduct;
|
||||||
import org.openmetadata.schema.entity.domains.Domain;
|
import org.openmetadata.schema.entity.domains.Domain;
|
||||||
import org.openmetadata.schema.entity.policies.Policy;
|
import org.openmetadata.schema.entity.policies.Policy;
|
||||||
import org.openmetadata.schema.entity.policies.accessControl.Rule;
|
import org.openmetadata.schema.entity.policies.accessControl.Rule;
|
||||||
import org.openmetadata.schema.governance.workflows.WorkflowConfiguration;
|
import org.openmetadata.schema.governance.workflows.WorkflowConfiguration;
|
||||||
import org.openmetadata.schema.governance.workflows.WorkflowDefinition;
|
import org.openmetadata.schema.governance.workflows.WorkflowDefinition;
|
||||||
import org.openmetadata.schema.governance.workflows.elements.WorkflowNodeDefinitionInterface;
|
import org.openmetadata.schema.governance.workflows.elements.WorkflowNodeDefinitionInterface;
|
||||||
|
import org.openmetadata.schema.type.EntityReference;
|
||||||
import org.openmetadata.schema.type.Include;
|
import org.openmetadata.schema.type.Include;
|
||||||
import org.openmetadata.schema.type.LineageDetails;
|
import org.openmetadata.schema.type.LineageDetails;
|
||||||
import org.openmetadata.schema.type.MetadataOperation;
|
import org.openmetadata.schema.type.MetadataOperation;
|
||||||
@ -36,6 +38,7 @@ import org.openmetadata.service.governance.workflows.flowable.MainWorkflow;
|
|||||||
import org.openmetadata.service.jdbi3.AppMarketPlaceRepository;
|
import org.openmetadata.service.jdbi3.AppMarketPlaceRepository;
|
||||||
import org.openmetadata.service.jdbi3.AppRepository;
|
import org.openmetadata.service.jdbi3.AppRepository;
|
||||||
import org.openmetadata.service.jdbi3.DataInsightSystemChartRepository;
|
import org.openmetadata.service.jdbi3.DataInsightSystemChartRepository;
|
||||||
|
import org.openmetadata.service.jdbi3.DataProductRepository;
|
||||||
import org.openmetadata.service.jdbi3.DomainRepository;
|
import org.openmetadata.service.jdbi3.DomainRepository;
|
||||||
import org.openmetadata.service.jdbi3.ListFilter;
|
import org.openmetadata.service.jdbi3.ListFilter;
|
||||||
import org.openmetadata.service.jdbi3.PolicyRepository;
|
import org.openmetadata.service.jdbi3.PolicyRepository;
|
||||||
@ -63,7 +66,7 @@ public class MigrationUtil {
|
|||||||
|
|
||||||
private MigrationUtil() {}
|
private MigrationUtil() {}
|
||||||
|
|
||||||
public static final String DOMAIN_LINEAGE =
|
public static final String DOMAIN_AND_PRODUCTS_LINEAGE =
|
||||||
"select count(*) from entity_relationship where fromId in (select toId from entity_relationship where fromId = '%s' and relation = 10) AND toId in (select toId from entity_relationship where fromId = '%s' and relation = 10) and relation = 13";
|
"select count(*) from entity_relationship where fromId in (select toId from entity_relationship where fromId = '%s' and relation = 10) AND toId in (select toId from entity_relationship where fromId = '%s' and relation = 10) and relation = 13";
|
||||||
|
|
||||||
public static final String SERVICE_ENTITY_MIGRATION =
|
public static final String SERVICE_ENTITY_MIGRATION =
|
||||||
@ -383,36 +386,11 @@ public class MigrationUtil {
|
|||||||
|
|
||||||
public static void runMigrationForDomainLineage(Handle handle) {
|
public static void runMigrationForDomainLineage(Handle handle) {
|
||||||
try {
|
try {
|
||||||
LOG.info("MIGRATION 1.7.0 - STARTING MIGRATION FOR DOMAIN LINEAGE");
|
|
||||||
List<Domain> allDomains = getAllDomains();
|
List<Domain> allDomains = getAllDomains();
|
||||||
for (Domain fromDomain : allDomains) {
|
for (Domain fromDomain : allDomains) {
|
||||||
for (Domain toDomain : allDomains) {
|
for (Domain toDomain : allDomains) {
|
||||||
if (fromDomain.getId().equals(toDomain.getId())) {
|
insertDomainAndDataProductLineage(
|
||||||
continue;
|
handle, fromDomain.getEntityReference(), toDomain.getEntityReference());
|
||||||
}
|
|
||||||
String sql =
|
|
||||||
String.format(
|
|
||||||
DOMAIN_LINEAGE, fromDomain.getId().toString(), toDomain.getId().toString());
|
|
||||||
int count = handle.createQuery(sql).mapTo(Integer.class).one();
|
|
||||||
if (count > 0) {
|
|
||||||
LineageDetails domainLineageDetails =
|
|
||||||
new LineageDetails()
|
|
||||||
.withCreatedAt(System.currentTimeMillis())
|
|
||||||
.withUpdatedAt(System.currentTimeMillis())
|
|
||||||
.withCreatedBy(ADMIN_USER_NAME)
|
|
||||||
.withUpdatedBy(ADMIN_USER_NAME)
|
|
||||||
.withSource(LineageDetails.Source.CHILD_ASSETS)
|
|
||||||
.withAssetEdges(count);
|
|
||||||
Entity.getCollectionDAO()
|
|
||||||
.relationshipDAO()
|
|
||||||
.insert(
|
|
||||||
fromDomain.getId(),
|
|
||||||
toDomain.getId(),
|
|
||||||
fromDomain.getEntityReference().getType(),
|
|
||||||
toDomain.getEntityReference().getType(),
|
|
||||||
Relationship.UPSTREAM.ordinal(),
|
|
||||||
JsonUtils.pojoToJson(domainLineageDetails));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,6 +401,57 @@ public class MigrationUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void runMigrationForDataProductsLineage(Handle handle) {
|
||||||
|
try {
|
||||||
|
List<DataProduct> allDataProducts = getAllDataProducts();
|
||||||
|
for (DataProduct fromDataProduct : allDataProducts) {
|
||||||
|
for (DataProduct toDataProduct : allDataProducts) {
|
||||||
|
insertDomainAndDataProductLineage(
|
||||||
|
handle, fromDataProduct.getEntityReference(), toDataProduct.getEntityReference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.error(
|
||||||
|
"Error while updating null json rows with createdAt, createdBy, updatedAt and updatedBy for lineage.",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void insertDomainAndDataProductLineage(
|
||||||
|
Handle handle, EntityReference fromRef, EntityReference toRef) {
|
||||||
|
LOG.info(
|
||||||
|
"MIGRATION 1.7.0 - STARTING MIGRATION FOR DOMAIN/DATA_PRODUCT LINEAGE, FROM: {} TO: {}",
|
||||||
|
fromRef.getFullyQualifiedName(),
|
||||||
|
toRef.getFullyQualifiedName());
|
||||||
|
if (fromRef.getId().equals(toRef.getId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String sql =
|
||||||
|
String.format(
|
||||||
|
DOMAIN_AND_PRODUCTS_LINEAGE, fromRef.getId().toString(), toRef.getId().toString());
|
||||||
|
int count = handle.createQuery(sql).mapTo(Integer.class).one();
|
||||||
|
if (count > 0) {
|
||||||
|
LineageDetails domainLineageDetails =
|
||||||
|
new LineageDetails()
|
||||||
|
.withCreatedAt(System.currentTimeMillis())
|
||||||
|
.withUpdatedAt(System.currentTimeMillis())
|
||||||
|
.withCreatedBy(ADMIN_USER_NAME)
|
||||||
|
.withUpdatedBy(ADMIN_USER_NAME)
|
||||||
|
.withSource(LineageDetails.Source.CHILD_ASSETS)
|
||||||
|
.withAssetEdges(count);
|
||||||
|
Entity.getCollectionDAO()
|
||||||
|
.relationshipDAO()
|
||||||
|
.insert(
|
||||||
|
fromRef.getId(),
|
||||||
|
toRef.getId(),
|
||||||
|
fromRef.getType(),
|
||||||
|
toRef.getType(),
|
||||||
|
Relationship.UPSTREAM.ordinal(),
|
||||||
|
JsonUtils.pojoToJson(domainLineageDetails));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void runMigrationServiceLineage(Handle handle) {
|
public static void runMigrationServiceLineage(Handle handle) {
|
||||||
try {
|
try {
|
||||||
List<ServiceEntityInterface> allServices = getAllServicesForLineage();
|
List<ServiceEntityInterface> allServices = getAllServicesForLineage();
|
||||||
@ -441,7 +470,10 @@ public class MigrationUtil {
|
|||||||
private static void insertServiceLineageDetails(
|
private static void insertServiceLineageDetails(
|
||||||
Handle handle, ServiceEntityInterface fromService, ServiceEntityInterface toService) {
|
Handle handle, ServiceEntityInterface fromService, ServiceEntityInterface toService) {
|
||||||
try {
|
try {
|
||||||
LOG.info("MIGRATION 1.7.0 - STARTING MIGRATION FOR SERVICES LINEAGE");
|
LOG.info(
|
||||||
|
"MIGRATION 1.7.0 - STARTING MIGRATION FOR SERVICES LINEAGE , FROM: {} TO: {}",
|
||||||
|
fromService.getFullyQualifiedName(),
|
||||||
|
toService.getFullyQualifiedName());
|
||||||
|
|
||||||
if (fromService.getId().equals(toService.getId())
|
if (fromService.getId().equals(toService.getId())
|
||||||
&& fromService
|
&& fromService
|
||||||
@ -510,6 +542,12 @@ public class MigrationUtil {
|
|||||||
return repository.listAll(repository.getFields("id"), new ListFilter(Include.ALL));
|
return repository.listAll(repository.getFields("id"), new ListFilter(Include.ALL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<DataProduct> getAllDataProducts() {
|
||||||
|
DataProductRepository repository =
|
||||||
|
(DataProductRepository) Entity.getEntityRepository(Entity.DATA_PRODUCT);
|
||||||
|
return repository.listAll(repository.getFields("id"), new ListFilter(Include.ALL));
|
||||||
|
}
|
||||||
|
|
||||||
public static void updateLineageBotPolicy() {
|
public static void updateLineageBotPolicy() {
|
||||||
PolicyRepository policyRepository =
|
PolicyRepository policyRepository =
|
||||||
(PolicyRepository) Entity.getEntityRepository(Entity.POLICY);
|
(PolicyRepository) Entity.getEntityRepository(Entity.POLICY);
|
||||||
|
@ -261,7 +261,7 @@ public class LineageResource {
|
|||||||
@Parameter(description = "view (service or domain)")
|
@Parameter(description = "view (service or domain)")
|
||||||
@QueryParam("view")
|
@QueryParam("view")
|
||||||
@Pattern(
|
@Pattern(
|
||||||
regexp = "service|domain|all",
|
regexp = "service|domain|dataProduct|all",
|
||||||
message = "Invalid type. Allowed values: service, domain.")
|
message = "Invalid type. Allowed values: service, domain.")
|
||||||
String view,
|
String view,
|
||||||
@Parameter(
|
@Parameter(
|
||||||
@ -273,6 +273,12 @@ public class LineageResource {
|
|||||||
@QueryParam("includeDeleted")
|
@QueryParam("includeDeleted")
|
||||||
boolean deleted)
|
boolean deleted)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
if (Entity.getSearchRepository().getIndexMapping(view) != null) {
|
||||||
|
view =
|
||||||
|
Entity.getSearchRepository()
|
||||||
|
.getIndexMapping(view)
|
||||||
|
.getIndexName(Entity.getSearchRepository().getClusterAlias());
|
||||||
|
}
|
||||||
return Entity.getSearchRepository().searchPlatformLineage(view, queryFilter, deleted);
|
return Entity.getSearchRepository().searchPlatformLineage(view, queryFilter, deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ public record DataProductIndex(DataProduct dataProduct) implements SearchIndex {
|
|||||||
ParseTags parseTags = new ParseTags(Entity.getEntityTags(Entity.DATA_PRODUCT, dataProduct));
|
ParseTags parseTags = new ParseTags(Entity.getEntityTags(Entity.DATA_PRODUCT, dataProduct));
|
||||||
doc.put("tags", parseTags.getTags());
|
doc.put("tags", parseTags.getTags());
|
||||||
doc.putAll(commonAttributes);
|
doc.putAll(commonAttributes);
|
||||||
|
doc.put("upstreamLineage", SearchIndex.getLineageData(dataProduct.getEntityReference()));
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ export const SIDEBAR_LIST_ITEMS = {
|
|||||||
[SidebarItem.GLOSSARY]: [SidebarItem.GOVERNANCE, SidebarItem.GLOSSARY],
|
[SidebarItem.GLOSSARY]: [SidebarItem.GOVERNANCE, SidebarItem.GLOSSARY],
|
||||||
[SidebarItem.TAGS]: [SidebarItem.GOVERNANCE, SidebarItem.TAGS],
|
[SidebarItem.TAGS]: [SidebarItem.GOVERNANCE, SidebarItem.TAGS],
|
||||||
[SidebarItem.METRICS]: [SidebarItem.GOVERNANCE, SidebarItem.METRICS],
|
[SidebarItem.METRICS]: [SidebarItem.GOVERNANCE, SidebarItem.METRICS],
|
||||||
[SidebarItem.LINEAGE]: [SidebarItem.GOVERNANCE, SidebarItem.LINEAGE],
|
|
||||||
|
|
||||||
// Profile Dropdown
|
// Profile Dropdown
|
||||||
'user-name': ['dropdown-profile', 'user-name'],
|
'user-name': ['dropdown-profile', 'user-name'],
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 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.
|
||||||
|
*/
|
||||||
|
import test, { expect } from '@playwright/test';
|
||||||
|
import { SidebarItem } from '../../constant/sidebar';
|
||||||
|
import { redirectToHomePage } from '../../utils/common';
|
||||||
|
import { sidebarClick } from '../../utils/sidebar';
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
storageState: 'playwright/.auth/admin.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Verify Platform Lineage View', async ({ page }) => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
const lineageRes = page.waitForResponse(
|
||||||
|
'/api/v1/lineage/getPlatformLineage?view=service*'
|
||||||
|
);
|
||||||
|
await sidebarClick(page, SidebarItem.LINEAGE);
|
||||||
|
await lineageRes;
|
||||||
|
|
||||||
|
await expect(page.getByTestId('lineage-export')).not.toBeVisible();
|
||||||
|
|
||||||
|
await page.getByTestId('lineage-layer-btn').click();
|
||||||
|
|
||||||
|
await page.waitForSelector(
|
||||||
|
'[data-testid="lineage-layer-domain-btn"]:not(.active)'
|
||||||
|
);
|
||||||
|
|
||||||
|
const domainRes = page.waitForResponse(
|
||||||
|
'/api/v1/lineage/getPlatformLineage?view=domain*'
|
||||||
|
);
|
||||||
|
await page.getByTestId('lineage-layer-domain-btn').click();
|
||||||
|
await domainRes;
|
||||||
|
|
||||||
|
await page.getByTestId('lineage-layer-btn').click();
|
||||||
|
const dataProductRes = page.waitForResponse(
|
||||||
|
'/api/v1/lineage/getPlatformLineage?view=dataProduct*'
|
||||||
|
);
|
||||||
|
await page.getByTestId('lineage-layer-data-product-btn').click();
|
||||||
|
await dataProductRes;
|
||||||
|
});
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.8 KiB |
@ -354,7 +354,7 @@ const Suggestions = ({
|
|||||||
className="m-b-md w-100 text-left d-flex items-center p-0"
|
className="m-b-md w-100 text-left d-flex items-center p-0"
|
||||||
data-testid="nlp-suggestions-button"
|
data-testid="nlp-suggestions-button"
|
||||||
icon={
|
icon={
|
||||||
<div className="nlp-button active w-6 h-6 flex-center m-r-md">
|
<div className="nlp-button w-6 h-6 flex-center m-r-md">
|
||||||
<IconSuggestionsBlue />
|
<IconSuggestionsBlue />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -135,19 +135,21 @@ const LineageControlButtons: FC<LineageControlButtonsProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
{entityType && (
|
||||||
className="lineage-button"
|
<Button
|
||||||
data-testid="lineage-export"
|
className="lineage-button"
|
||||||
disabled={isEditMode}
|
data-testid="lineage-export"
|
||||||
icon={
|
disabled={isEditMode}
|
||||||
<span className="anticon">
|
icon={
|
||||||
<ExportIcon height={18} width={18} />
|
<span className="anticon">
|
||||||
</span>
|
<ExportIcon height={18} width={18} />
|
||||||
}
|
</span>
|
||||||
title={t('label.export-entity', { entity: t('label.lineage') })}
|
}
|
||||||
type="text"
|
title={t('label.export-entity', { entity: t('label.lineage') })}
|
||||||
onClick={onExportClick}
|
type="text"
|
||||||
/>
|
onClick={onExportClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{handleFullScreenViewClick && (
|
{handleFullScreenViewClick && (
|
||||||
<Button
|
<Button
|
||||||
|
@ -16,6 +16,7 @@ import classNames from 'classnames';
|
|||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ReactComponent as DataQualityIcon } from '../../../../assets/svg/ic-data-contract.svg';
|
import { ReactComponent as DataQualityIcon } from '../../../../assets/svg/ic-data-contract.svg';
|
||||||
|
import { ReactComponent as DataProductIcon } from '../../../../assets/svg/ic-data-product.svg';
|
||||||
import { ReactComponent as DomainIcon } from '../../../../assets/svg/ic-domain.svg';
|
import { ReactComponent as DomainIcon } from '../../../../assets/svg/ic-domain.svg';
|
||||||
import { ReactComponent as Layers } from '../../../../assets/svg/ic-layers.svg';
|
import { ReactComponent as Layers } from '../../../../assets/svg/ic-layers.svg';
|
||||||
import { ReactComponent as ServiceView } from '../../../../assets/svg/services.svg';
|
import { ReactComponent as ServiceView } from '../../../../assets/svg/services.svg';
|
||||||
@ -23,6 +24,7 @@ import { SERVICE_TYPES } from '../../../../constants/Services.constant';
|
|||||||
import { useLineageProvider } from '../../../../context/LineageProvider/LineageProvider';
|
import { useLineageProvider } from '../../../../context/LineageProvider/LineageProvider';
|
||||||
import { LineagePlatformView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
import { LineagePlatformView } from '../../../../context/LineageProvider/LineageProvider.interface';
|
||||||
import { EntityType } from '../../../../enums/entity.enum';
|
import { EntityType } from '../../../../enums/entity.enum';
|
||||||
|
import { Table } from '../../../../generated/entity/data/table';
|
||||||
import { LineageLayer } from '../../../../generated/settings/settings';
|
import { LineageLayer } from '../../../../generated/settings/settings';
|
||||||
import searchClassBase from '../../../../utils/SearchClassBase';
|
import searchClassBase from '../../../../utils/SearchClassBase';
|
||||||
import { AssetsUnion } from '../../../DataAssets/AssetsSelectionModal/AssetSelectionModal.interface';
|
import { AssetsUnion } from '../../../DataAssets/AssetsSelectionModal/AssetSelectionModal.interface';
|
||||||
@ -48,7 +50,7 @@ const LayerButton: React.FC<LayerButtonProps> = React.memo(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const LineageLayers = ({ entityType }: LineageLayersProps) => {
|
const LineageLayers = ({ entityType, entity }: LineageLayersProps) => {
|
||||||
const {
|
const {
|
||||||
activeLayer,
|
activeLayer,
|
||||||
onUpdateLayerView,
|
onUpdateLayerView,
|
||||||
@ -116,7 +118,9 @@ const LineageLayers = ({ entityType }: LineageLayersProps) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{(isPlatformLineage ||
|
{(isPlatformLineage ||
|
||||||
(entityType && entityType !== EntityType.DOMAIN)) && (
|
(entityType &&
|
||||||
|
entityType !== EntityType.DOMAIN &&
|
||||||
|
entity?.domain)) && (
|
||||||
<LayerButton
|
<LayerButton
|
||||||
icon={<DomainIcon />}
|
icon={<DomainIcon />}
|
||||||
isActive={platformView === LineagePlatformView.Domain}
|
isActive={platformView === LineagePlatformView.Domain}
|
||||||
@ -125,6 +129,21 @@ const LineageLayers = ({ entityType }: LineageLayersProps) => {
|
|||||||
onClick={() => handlePlatformViewChange(LineagePlatformView.Domain)}
|
onClick={() => handlePlatformViewChange(LineagePlatformView.Domain)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{(isPlatformLineage ||
|
||||||
|
(entityType &&
|
||||||
|
entityType !== EntityType.DOMAIN &&
|
||||||
|
((entity as Table)?.dataProducts ?? [])?.length > 0)) && (
|
||||||
|
<LayerButton
|
||||||
|
icon={<DataProductIcon />}
|
||||||
|
isActive={platformView === LineagePlatformView.DataProduct}
|
||||||
|
label={t('label.data-product')}
|
||||||
|
testId="lineage-layer-data-product-btn"
|
||||||
|
onClick={() =>
|
||||||
|
handlePlatformViewChange(LineagePlatformView.DataProduct)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
@ -25,6 +25,7 @@ import React, {
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { ReactComponent as IconCloseCircleOutlined } from '../../assets/svg/close-circle-outlined.svg';
|
import { ReactComponent as IconCloseCircleOutlined } from '../../assets/svg/close-circle-outlined.svg';
|
||||||
|
import { ReactComponent as IconSuggestionsActive } from '../../assets/svg/ic-suggestions-active.svg';
|
||||||
import { ReactComponent as IconSuggestionsBlue } from '../../assets/svg/ic-suggestions-blue.svg';
|
import { ReactComponent as IconSuggestionsBlue } from '../../assets/svg/ic-suggestions-blue.svg';
|
||||||
import { ReactComponent as IconSearch } from '../../assets/svg/search.svg';
|
import { ReactComponent as IconSearch } from '../../assets/svg/search.svg';
|
||||||
import { TOUR_SEARCH_TERM } from '../../constants/constants';
|
import { TOUR_SEARCH_TERM } from '../../constants/constants';
|
||||||
@ -185,7 +186,13 @@ export const GlobalSearchBar = () => {
|
|||||||
active: isNLPActive,
|
active: isNLPActive,
|
||||||
})}
|
})}
|
||||||
data-testid="nlp-suggestions-button"
|
data-testid="nlp-suggestions-button"
|
||||||
icon={<Icon component={IconSuggestionsBlue} />}
|
icon={
|
||||||
|
<Icon
|
||||||
|
component={
|
||||||
|
isNLPActive ? IconSuggestionsActive : IconSuggestionsBlue
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
type="text"
|
type="text"
|
||||||
onClick={() => setNLPActive(!isNLPActive)}
|
onClick={() => setNLPActive(!isNLPActive)}
|
||||||
/>
|
/>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
@import (reference) '../../styles/variables.less';
|
@import (reference) '../../styles/variables.less';
|
||||||
|
|
||||||
|
@nlp-border-color: #b9e6fe;
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
border: 1px solid #eaecf5;
|
border: 1px solid #eaecf5;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@ -20,8 +22,9 @@
|
|||||||
padding: 6px 20px;
|
padding: 6px 20px;
|
||||||
|
|
||||||
.nlp-button {
|
.nlp-button {
|
||||||
border: 0.5px solid @border-color !important;
|
border: 0.5px solid @nlp-border-color !important;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
background-color: @blue-11 !important;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
@ -29,8 +32,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: @blue-11 !important;
|
padding: 0;
|
||||||
border: 0.5px solid #b9e6fe !important;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none !important;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,7 +662,7 @@ const AssetsTabs = forwardRef(
|
|||||||
activeEntity &&
|
activeEntity &&
|
||||||
permissions.Create &&
|
permissions.Create &&
|
||||||
data.length > 0 && (
|
data.length > 0 && (
|
||||||
<div className="w-full d-flex justify-between items-center">
|
<div className="w-full d-flex justify-between items-center m-b-sm">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className="assets-checkbox p-x-sm"
|
className="assets-checkbox p-x-sm"
|
||||||
onChange={(e) => onSelectAll(e.target.checked)}>
|
onChange={(e) => onSelectAll(e.target.checked)}>
|
||||||
|
@ -196,7 +196,7 @@ const Lineage = ({
|
|||||||
<MiniMap pannable zoomable position="bottom-right" />
|
<MiniMap pannable zoomable position="bottom-right" />
|
||||||
|
|
||||||
<Panel position="bottom-left">
|
<Panel position="bottom-left">
|
||||||
<LineageLayers entityType={entityType} />
|
<LineageLayers entity={entity} entityType={entityType} />
|
||||||
</Panel>
|
</Panel>
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
|
@ -47,6 +47,7 @@ export enum LineagePlatformView {
|
|||||||
None = 'None',
|
None = 'None',
|
||||||
Service = 'Service',
|
Service = 'Service',
|
||||||
Domain = 'Domain',
|
Domain = 'Domain',
|
||||||
|
DataProduct = 'DataProduct',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LineageContextType {
|
export interface LineageContextType {
|
||||||
|
@ -65,6 +65,7 @@ import { EntityLineageNodeType, EntityType } from '../../enums/entity.enum';
|
|||||||
import { AddLineage } from '../../generated/api/lineage/addLineage';
|
import { AddLineage } from '../../generated/api/lineage/addLineage';
|
||||||
import { LineageDirection } from '../../generated/api/lineage/lineageDirection';
|
import { LineageDirection } from '../../generated/api/lineage/lineageDirection';
|
||||||
import { LineageSettings } from '../../generated/configuration/lineageSettings';
|
import { LineageSettings } from '../../generated/configuration/lineageSettings';
|
||||||
|
import { Table } from '../../generated/entity/data/table';
|
||||||
import { LineageLayer } from '../../generated/settings/settings';
|
import { LineageLayer } from '../../generated/settings/settings';
|
||||||
import {
|
import {
|
||||||
ColumnLineage,
|
ColumnLineage,
|
||||||
@ -94,6 +95,7 @@ import {
|
|||||||
getConnectedNodesEdges,
|
getConnectedNodesEdges,
|
||||||
getEdgeDataFromEdge,
|
getEdgeDataFromEdge,
|
||||||
getELKLayoutedElements,
|
getELKLayoutedElements,
|
||||||
|
getEntityTypeFromPlatformView,
|
||||||
getLineageEdge,
|
getLineageEdge,
|
||||||
getLineageEdgeForAPI,
|
getLineageEdgeForAPI,
|
||||||
getLoadingStatusValue,
|
getLoadingStatusValue,
|
||||||
@ -346,7 +348,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fetchPlatformLineage = useCallback(
|
const fetchPlatformLineage = useCallback(
|
||||||
async (view: 'service' | 'domain', config?: LineageConfig) => {
|
async (view: string, config?: LineageConfig) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setInit(false);
|
setInit(false);
|
||||||
@ -1329,17 +1331,26 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
|||||||
entity?.domain.type,
|
entity?.domain.type,
|
||||||
lineageConfig
|
lineageConfig
|
||||||
);
|
);
|
||||||
|
} else if (
|
||||||
|
platformView === LineagePlatformView.DataProduct &&
|
||||||
|
((entity as Table)?.dataProducts ?? [])?.length > 0
|
||||||
|
) {
|
||||||
|
fetchLineageData(
|
||||||
|
(entity as Table)?.dataProducts?.[0]?.fullyQualifiedName ?? '',
|
||||||
|
(entity as Table)?.dataProducts?.[0]?.type ?? '',
|
||||||
|
lineageConfig
|
||||||
|
);
|
||||||
} else if (platformView === LineagePlatformView.None) {
|
} else if (platformView === LineagePlatformView.None) {
|
||||||
fetchLineageData(decodedFqn, entityType, lineageConfig);
|
fetchLineageData(decodedFqn, entityType, lineageConfig);
|
||||||
} else if (isPlatformLineage) {
|
} else if (isPlatformLineage) {
|
||||||
fetchPlatformLineage(
|
fetchPlatformLineage(
|
||||||
platformView === LineagePlatformView.Domain ? 'domain' : 'service',
|
getEntityTypeFromPlatformView(platformView),
|
||||||
lineageConfig
|
lineageConfig
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (isPlatformLineage) {
|
} else if (isPlatformLineage) {
|
||||||
fetchPlatformLineage(
|
fetchPlatformLineage(
|
||||||
platformView === LineagePlatformView.Domain ? 'domain' : 'service',
|
getEntityTypeFromPlatformView(platformView),
|
||||||
lineageConfig
|
lineageConfig
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ export const getPlatformLineage = async ({
|
|||||||
}: {
|
}: {
|
||||||
config?: LineageConfig;
|
config?: LineageConfig;
|
||||||
queryFilter?: string;
|
queryFilter?: string;
|
||||||
view: 'service' | 'domain';
|
view: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { upstreamDepth = 1, downstreamDepth = 1 } = config ?? {};
|
const { upstreamDepth = 1, downstreamDepth = 1 } = config ?? {};
|
||||||
const API_PATH = `lineage/getPlatformLineage`;
|
const API_PATH = `lineage/getPlatformLineage`;
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
@text-highlighter: #ffc34e40;
|
@text-highlighter: #ffc34e40;
|
||||||
@team-avatar-bg: #0950c51a;
|
@team-avatar-bg: #0950c51a;
|
||||||
@om-navbar-height: ~'var(--ant-navbar-height)';
|
@om-navbar-height: ~'var(--ant-navbar-height)';
|
||||||
@sidebar-width: 60px;
|
@sidebar-width: 84px;
|
||||||
@alert-text-color: @text-color-tertiary;
|
@alert-text-color: @text-color-tertiary;
|
||||||
@alert-info-bg: @blue-14;
|
@alert-info-bg: @blue-14;
|
||||||
@alert-success-bg: #f6fef9;
|
@alert-success-bg: #f6fef9;
|
||||||
|
@ -63,6 +63,7 @@ import {
|
|||||||
ZOOM_TRANSITION_DURATION,
|
ZOOM_TRANSITION_DURATION,
|
||||||
ZOOM_VALUE,
|
ZOOM_VALUE,
|
||||||
} from '../constants/Lineage.constants';
|
} from '../constants/Lineage.constants';
|
||||||
|
import { LineagePlatformView } from '../context/LineageProvider/LineageProvider.interface';
|
||||||
import {
|
import {
|
||||||
EntityLineageDirection,
|
EntityLineageDirection,
|
||||||
EntityLineageNodeType,
|
EntityLineageNodeType,
|
||||||
@ -1748,3 +1749,16 @@ export const getLineageEntityExclusionFilter = () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getEntityTypeFromPlatformView = (
|
||||||
|
platformView: LineagePlatformView
|
||||||
|
): string => {
|
||||||
|
switch (platformView) {
|
||||||
|
case LineagePlatformView.DataProduct:
|
||||||
|
return EntityType.DATA_PRODUCT;
|
||||||
|
case LineagePlatformView.Domain:
|
||||||
|
return EntityType.DOMAIN;
|
||||||
|
default:
|
||||||
|
return 'service';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user