/* * Copyright 2021 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.service; import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; import javax.ws.rs.core.UriInfo; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.openmetadata.schema.EntityInterface; import org.openmetadata.schema.type.EntityReference; import org.openmetadata.schema.type.Include; import org.openmetadata.service.exception.CatalogExceptionMessage; import org.openmetadata.service.exception.EntityNotFoundException; import org.openmetadata.service.jdbi3.EntityDAO; import org.openmetadata.service.jdbi3.EntityRepository; import org.openmetadata.service.util.EntityUtil; @Slf4j public final class Entity { // Fully qualified name separator public static final String SEPARATOR = "."; // Canonical entity name to corresponding EntityDAO map private static final Map> DAO_MAP = new HashMap<>(); // Canonical entity name to corresponding EntityRepository map private static final Map> ENTITY_REPOSITORY_MAP = new HashMap<>(); // List of all the entities private static final List ENTITY_LIST = new ArrayList<>(); // Common field names public static final String FIELD_OWNER = "owner"; public static final String FIELD_NAME = "name"; public static final String FIELD_DESCRIPTION = "description"; public static final String FIELD_SERVICE = "service"; public static final String FIELD_FOLLOWERS = "followers"; public static final String FIELD_TAGS = "tags"; public static final String FIELD_DELETED = "deleted"; public static final String FIELD_PIPELINE_STATUSES = "pipelineStatuses"; public static final String FIELD_DISPLAY_NAME = "displayName"; public static final String FIELD_EXTENSION = "extension"; public static final String FIELD_USAGE_SUMMARY = "usageSummary"; // // Service entities // public static final String DATABASE_SERVICE = "databaseService"; public static final String MESSAGING_SERVICE = "messagingService"; public static final String DASHBOARD_SERVICE = "dashboardService"; public static final String PIPELINE_SERVICE = "pipelineService"; public static final String STORAGE_SERVICE = "storageService"; public static final String MLMODEL_SERVICE = "mlmodelService"; // // Data asset entities // public static final String TABLE = "table"; public static final String DATABASE = "database"; public static final String DATABASE_SCHEMA = "databaseSchema"; public static final String METRICS = "metrics"; public static final String DASHBOARD = "dashboard"; public static final String PIPELINE = "pipeline"; public static final String CHART = "chart"; public static final String REPORT = "report"; public static final String TOPIC = "topic"; public static final String MLMODEL = "mlmodel"; public static final String BOT = "bot"; public static final String THREAD = "THREAD"; public static final String LOCATION = "location"; public static final String GLOSSARY = "glossary"; public static final String GLOSSARY_TERM = "glossaryTerm"; public static final String TAG = "tag"; public static final String TAG_CATEGORY = "tagCategory"; public static final String TYPE = "type"; public static final String TEST_DEFINITION = "testDefinition"; public static final String TEST_SUITE = "testSuite"; public static final String TEST_CASE = "testCase"; // // Policy entity // public static final String POLICY = "policy"; public static final String POLICIES = "policies"; // // Service // public static final String SERVICE = "service"; // // Role, team and user entities // public static final String ROLE = "role"; public static final String USER = "user"; public static final String TEAM = "team"; // // Operation related entities // public static final String INGESTION_PIPELINE = "ingestionPipeline"; public static final String WEBHOOK = "webhook"; // // Reserved names in OpenMetadata // public static final String ORGANIZATION_NAME = "Organization"; public static final String DATACONSUMER_ROLE = "DataConsumer"; // // List of entities whose changes should not be published to the Activity Feed // public static final List ACTIVITY_FEED_EXCLUDED_ENTITIES = List.of( USER, TEAM, ROLE, POLICY, BOT, INGESTION_PIPELINE, DATABASE_SERVICE, PIPELINE_SERVICE, DASHBOARD_SERVICE, STORAGE_SERVICE, MESSAGING_SERVICE); private Entity() {} public static void registerEntity( Class clazz, String entity, EntityDAO dao, EntityRepository entityRepository) { DAO_MAP.put(entity, dao); ENTITY_REPOSITORY_MAP.put(entity, entityRepository); EntityInterface.CANONICAL_ENTITY_NAME_MAP.put(entity.toLowerCase(Locale.ROOT), entity); ENTITY_LIST.add(entity); Collections.sort(ENTITY_LIST); LOG.info("Registering entity {} {}", clazz, entity); } public static List getEntityList() { return Collections.unmodifiableList(ENTITY_LIST); } public static EntityReference getEntityReference(EntityReference ref) throws IOException { return ref == null ? null : getEntityReferenceById(ref.getType(), ref.getId(), Include.NON_DELETED); } public static EntityReference getEntityReferenceById(@NonNull String entityType, @NonNull UUID id, Include include) throws IOException { EntityRepository repository = ENTITY_REPOSITORY_MAP.get(entityType); if (repository == null) { throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityType)); } include = repository.supportsSoftDelete ? Include.ALL : include; return repository.dao.findEntityReferenceById(id, include); } public static EntityReference getEntityReferenceByName( @NonNull String entityType, @NonNull String fqn, Include include) { EntityDAO dao = DAO_MAP.get(entityType); if (dao == null) { throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityType)); } return dao.findEntityReferenceByName(fqn, include); } public static EntityReference getOwner(@NonNull EntityReference reference) throws IOException { EntityRepository repository = getEntityRepository(reference.getType()); return repository.getOwner(reference); } public static void withHref(UriInfo uriInfo, List list) { listOrEmpty(list).forEach(ref -> withHref(uriInfo, ref)); } public static void withHref(UriInfo uriInfo, EntityReference ref) { if (ref == null) { return; } String entityType = ref.getType(); EntityRepository entityRepository = getEntityRepository(entityType); URI href = entityRepository.getHref(uriInfo, ref.getId()); ref.withHref(href); } public static boolean shouldHaveOwner(@NonNull String entityType) { // Team does not have an owner. (yet?) return !entityType.equals(TEAM); } /** Returns true if the change events of the given entity type should be published to the activity feed. */ public static boolean shouldDisplayEntityChangeOnFeed(@NonNull String entityType) { return !ACTIVITY_FEED_EXCLUDED_ENTITIES.contains(entityType); } public static T getEntity(EntityReference ref, EntityUtil.Fields fields, Include include) throws IOException { return getEntity(ref.getType(), ref.getId(), fields, include); } /** Retrieve the entity using id from given entity reference and fields */ public static T getEntity(String entityType, UUID id, EntityUtil.Fields fields, Include include) throws IOException { EntityRepository entityRepository = Entity.getEntityRepository(entityType); @SuppressWarnings("unchecked") T entity = (T) entityRepository.get(null, id, fields, include); if (entity == null) { throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityNotFound(entityType, id)); } return entity; } /** Retrieve the corresponding entity repository for a given entity name. */ public static EntityRepository getEntityRepository(@NonNull String entityType) { @SuppressWarnings("unchecked") EntityRepository entityRepository = (EntityRepository) ENTITY_REPOSITORY_MAP.get(entityType); if (entityRepository == null) { throw EntityNotFoundException.byMessage(CatalogExceptionMessage.entityTypeNotFound(entityType)); } return entityRepository; } public static void deleteEntity( String updatedBy, String entityType, UUID entityId, boolean recursive, boolean hardDelete) throws IOException { EntityRepository dao = getEntityRepository(entityType); dao.delete(updatedBy, entityId, recursive, hardDelete); } public static void restoreEntity(String updatedBy, String entityType, UUID entityId) throws IOException { EntityRepository dao = getEntityRepository(entityType); dao.restoreEntity(updatedBy, entityType, entityId); } public static String getEntityTypeFromClass(Class clz) { return EntityInterface.CANONICAL_ENTITY_NAME_MAP.get(clz.getSimpleName().toLowerCase(Locale.ROOT)); } public static String getEntityTypeFromObject(Object object) { return EntityInterface.CANONICAL_ENTITY_NAME_MAP.get(object.getClass().getSimpleName().toLowerCase(Locale.ROOT)); } /** * Get list of all the entity field names from JsonPropertyOrder annotation from generated java class from entity.json */ public static List getEntityFields(Class clz) { JsonPropertyOrder propertyOrder = clz.getAnnotation(JsonPropertyOrder.class); return new ArrayList<>(Arrays.asList(propertyOrder.value())); } public static List getAllowedFields(Class clz) { String entityType = getEntityTypeFromClass(clz); EntityRepository repository = getEntityRepository(entityType); return repository.getAllowedFields(); } /** Class for getting validated entity list from a queryParam with list of entities. */ public static class EntityList { private EntityList() {} public static List getEntityList(String name, String entitiesParam) { if (entitiesParam == null) { return Collections.emptyList(); } entitiesParam = entitiesParam.replace(" ", ""); if (entitiesParam.equals("*")) { return List.of("*"); } List list = Arrays.asList(entitiesParam.split(",")); validateEntities(name, list); return list; } private static void validateEntities(String name, List list) { for (String entity : list) { if (ENTITY_REPOSITORY_MAP.get(entity) == null) { throw new IllegalArgumentException(String.format("Invalid entity %s in query param %s", entity, name)); } } } } }