blob: 88de8b679f268fa2516ffc73ecb10e45ffc78823 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.atlas.web.rest;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
import org.apache.atlas.AtlasErrorCode;
import org.apache.atlas.EntityAuditEvent;
import org.apache.atlas.bulkimport.BulkImportResponse;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.TypeCategory;
import org.apache.atlas.model.audit.EntityAuditEventV2;
import org.apache.atlas.model.audit.EntityAuditEventV2.EntityAuditActionV2;
import org.apache.atlas.model.instance.AtlasClassification;
import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo;
import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
import org.apache.atlas.model.instance.AtlasEntityHeader;
import org.apache.atlas.model.instance.AtlasEntityHeaders;
import org.apache.atlas.model.instance.ClassificationAssociateRequest;
import org.apache.atlas.model.instance.EntityMutationResponse;
import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef;
import org.apache.atlas.repository.audit.EntityAuditRepository;
import org.apache.atlas.repository.converters.AtlasInstanceConverter;
import org.apache.atlas.repository.store.graph.AtlasEntityStore;
import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream;
import org.apache.atlas.repository.store.graph.v2.ClassificationAssociator;
import org.apache.atlas.repository.store.graph.v2.EntityStream;
import org.apache.atlas.type.AtlasClassificationType;
import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.util.FileUtils;
import org.apache.atlas.utils.AtlasPerfTracer;
import org.apache.atlas.web.util.Servlets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* REST for a single entity
*/
@Path("v2/entity")
@Singleton
@Service
@Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON})
@Produces({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON})
public class EntityREST {
private static final Logger LOG = LoggerFactory.getLogger(EntityREST.class);
private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("rest.EntityREST");
public static final String PREFIX_ATTR = "attr:";
public static final String PREFIX_ATTR_ = "attr_";
private final AtlasTypeRegistry typeRegistry;
private final AtlasEntityStore entitiesStore;
private final EntityAuditRepository auditRepository;
private final AtlasInstanceConverter instanceConverter;
@Inject
public EntityREST(AtlasTypeRegistry typeRegistry, AtlasEntityStore entitiesStore,
EntityAuditRepository auditRepository, AtlasInstanceConverter instanceConverter) {
this.typeRegistry = typeRegistry;
this.entitiesStore = entitiesStore;
this.auditRepository = auditRepository;
this.instanceConverter = instanceConverter;
}
/**
* Fetch complete definition of an entity given its GUID.
* @param guid GUID for the entity
* @return AtlasEntity
* @throws AtlasBaseException
*/
@GET
@Path("/guid/{guid}")
public AtlasEntityWithExtInfo getById(@PathParam("guid") String guid, @QueryParam("minExtInfo") @DefaultValue("false") boolean minExtInfo, @QueryParam("ignoreRelationships") @DefaultValue("false") boolean ignoreRelationships) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getById(" + guid + ", " + minExtInfo + " )");
}
return entitiesStore.getById(guid, minExtInfo, ignoreRelationships);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Get entity header given its GUID.
* @param guid GUID for the entity
* @return AtlasEntity
* @throws AtlasBaseException
*/
@GET
@Path("/guid/{guid}/header")
public AtlasEntityHeader getHeaderById(@PathParam("guid") String guid) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getHeaderById(" + guid + ")");
}
return entitiesStore.getHeaderById(guid);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Fetch AtlasEntityHeader given its type and unique attribute.
*
* In addition to the typeName path parameter, attribute key-value pair(s) can be provided in the following format
*
* attr:<attrName>=<attrValue>
*
* NOTE: The attrName and attrValue should be unique across entities, eg. qualifiedName
*
* The REST request would look something like this
*
* GET /v2/entity/uniqueAttribute/type/aType/header?attr:aTypeAttribute=someValue
*
* @param typeName
* @return AtlasEntityHeader
* @throws AtlasBaseException
*/
@GET
@Path("/uniqueAttribute/type/{typeName}/header")
public AtlasEntityHeader getEntityHeaderByUniqueAttributes(@PathParam("typeName") String typeName,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
Map<String, Object> attributes = getAttributes(servletRequest);
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getEntityHeaderByUniqueAttributes(" + typeName + "," + attributes + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
validateUniqueAttribute(entityType, attributes);
return entitiesStore.getEntityHeaderByUniqueAttributes(entityType, attributes);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Fetch complete definition of an entity given its type and unique attribute.
*
* In addition to the typeName path parameter, attribute key-value pair(s) can be provided in the following format
*
* attr:<attrName>=<attrValue>
*
* NOTE: The attrName and attrValue should be unique across entities, eg. qualifiedName
*
* The REST request would look something like this
*
* GET /v2/entity/uniqueAttribute/type/aType?attr:aTypeAttribute=someValue
*
* @param typeName
* @param minExtInfo
* @param ignoreRelationships
* @return AtlasEntityWithExtInfo
* @throws AtlasBaseException
*/
@GET
@Path("/uniqueAttribute/type/{typeName}")
public AtlasEntityWithExtInfo getByUniqueAttributes(@PathParam("typeName") String typeName, @QueryParam("minExtInfo") @DefaultValue("false") boolean minExtInfo,
@QueryParam("ignoreRelationships") @DefaultValue("false") boolean ignoreRelationships, @Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
Map<String, Object> attributes = getAttributes(servletRequest);
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getByUniqueAttributes(" + typeName + "," + attributes + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
validateUniqueAttribute(entityType, attributes);
return entitiesStore.getByUniqueAttributes(entityType, attributes, minExtInfo, ignoreRelationships);
} finally {
AtlasPerfTracer.log(perf);
}
}
/*******
* Entity Partial Update - Allows a subset of attributes to be updated on
* an entity which is identified by its type and unique attribute eg: Referenceable.qualifiedName.
* Null updates are not possible
*
* In addition to the typeName path parameter, attribute key-value pair(s) can be provided in the following format
*
* attr:<attrName>=<attrValue>
*
* NOTE: The attrName and attrValue should be unique across entities, eg. qualifiedName
*
* The REST request would look something like this
*
* PUT /v2/entity/uniqueAttribute/type/aType?attr:aTypeAttribute=someValue
*
*******/
@PUT
@Path("/uniqueAttribute/type/{typeName}")
public EntityMutationResponse partialUpdateEntityByUniqueAttrs(@PathParam("typeName") String typeName,
@Context HttpServletRequest servletRequest,
AtlasEntityWithExtInfo entityInfo) throws Exception {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
Map<String, Object> uniqueAttributes = getAttributes(servletRequest);
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.partialUpdateEntityByUniqueAttrs(" + typeName + "," + uniqueAttributes + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
validateUniqueAttribute(entityType, uniqueAttributes);
return entitiesStore.updateByUniqueAttributes(entityType, uniqueAttributes, entityInfo);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Delete an entity identified by its type and unique attributes.
*
* In addition to the typeName path parameter, attribute key-value pair(s) can be provided in the following format
*
* attr:<attrName>=<attrValue>
*
* NOTE: The attrName and attrValue should be unique across entities, eg. qualifiedName
*
* The REST request would look something like this
*
* DELETE /v2/entity/uniqueAttribute/type/aType?attr:aTypeAttribute=someValue
*
* @param typeName - entity type to be deleted
* @param servletRequest - request containing unique attributes/values
* @return EntityMutationResponse
*/
@DELETE
@Path("/uniqueAttribute/type/{typeName}")
public EntityMutationResponse deleteByUniqueAttribute(@PathParam("typeName") String typeName,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
Map<String, Object> attributes = getAttributes(servletRequest);
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteByUniqueAttribute(" + typeName + "," + attributes + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
return entitiesStore.deleteByUniqueAttributes(entityType, attributes);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Create new entity or update existing entity in Atlas.
* Existing entity is matched using its unique guid if supplied or by its unique attributes eg: qualifiedName
* @param entity
* @return EntityMutationResponse
* @throws AtlasBaseException
*/
@POST
public EntityMutationResponse createOrUpdate(AtlasEntityWithExtInfo entity) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.createOrUpdate()");
}
return entitiesStore.createOrUpdate(new AtlasEntityStream(entity), false);
} finally {
AtlasPerfTracer.log(perf);
}
}
/*******
* Entity Partial Update - Add/Update entity attribute identified by its GUID.
* Supports only uprimitive attribute type and entity references.
* does not support updation of complex types like arrays, maps
* Null updates are not possible
*******/
@PUT
@Path("/guid/{guid}")
public EntityMutationResponse partialUpdateEntityAttrByGuid(@PathParam("guid") String guid,
@QueryParam("name") String attrName,
Object attrValue) throws Exception {
Servlets.validateQueryParamLength("guid", guid);
Servlets.validateQueryParamLength("name", attrName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.partialUpdateEntityAttrByGuid(" + guid + "," + attrName + ")");
}
return entitiesStore.updateEntityAttributeByGuid(guid, attrName, attrValue);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Delete an entity identified by its GUID.
* @param guid GUID for the entity
* @return EntityMutationResponse
*/
@DELETE
@Path("/guid/{guid}")
public EntityMutationResponse deleteByGuid(@PathParam("guid") final String guid) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteByGuid(" + guid + ")");
}
return entitiesStore.deleteById(guid);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Gets the list of classifications for a given entity represented by a guid.
* @param guid globally unique identifier for the entity
* @return classification for the given entity guid
*/
@GET
@Path("/guid/{guid}/classification/{classificationName}")
public AtlasClassification getClassification(@PathParam("guid") String guid, @PathParam("classificationName") final String classificationName) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
Servlets.validateQueryParamLength("classificationName", classificationName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getClassification(" + guid + "," + classificationName + ")");
}
if (StringUtils.isEmpty(guid)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
}
ensureClassificationType(classificationName);
return entitiesStore.getClassification(guid, classificationName);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Gets the list of classifications for a given entity represented by a guid.
* @param guid globally unique identifier for the entity
* @return a list of classifications for the given entity guid
*/
@GET
@Path("/guid/{guid}/classifications")
public AtlasClassification.AtlasClassifications getClassifications(@PathParam("guid") String guid) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getClassifications(" + guid + ")");
}
if (StringUtils.isEmpty(guid)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
}
return new AtlasClassification.AtlasClassifications(entitiesStore.getClassifications(guid));
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Adds classification to the entity identified by its type and unique attributes.
* @param typeName
*/
@POST
@Path("/uniqueAttribute/type/{typeName}/classifications")
public void addClassificationsByUniqueAttribute(@PathParam("typeName") String typeName, @Context HttpServletRequest servletRequest, List<AtlasClassification> classifications) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addClassificationsByUniqueAttribute(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.addClassifications(guid, classifications);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Adds classifications to an existing entity represented by a guid.
* @param guid globally unique identifier for the entity
*/
@POST
@Path("/guid/{guid}/classifications")
public void addClassifications(@PathParam("guid") final String guid, List<AtlasClassification> classifications) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addClassifications(" + guid + ")");
}
if (StringUtils.isEmpty(guid)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
}
entitiesStore.addClassifications(guid, classifications);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Updates classification on an entity identified by its type and unique attributes.
* @param typeName
*/
@PUT
@Path("/uniqueAttribute/type/{typeName}/classifications")
public void updateClassificationsByUniqueAttribute(@PathParam("typeName") String typeName, @Context HttpServletRequest servletRequest, List<AtlasClassification> classifications) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.updateClassificationsByUniqueAttribute(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.updateClassifications(guid, classifications);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Updates classifications to an existing entity represented by a guid.
* @param guid globally unique identifier for the entity
* @return classification for the given entity guid
*/
@PUT
@Path("/guid/{guid}/classifications")
public void updateClassifications(@PathParam("guid") final String guid, List<AtlasClassification> classifications) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.updateClassifications(" + guid + ")");
}
if (StringUtils.isEmpty(guid)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
}
entitiesStore.updateClassifications(guid, classifications);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Deletes a given classification from an entity identified by its type and unique attributes.
* @param typeName
* @param classificationName name of the classification
*/
@DELETE
@Path("/uniqueAttribute/type/{typeName}/classification/{classificationName}")
public void deleteClassificationByUniqueAttribute(@PathParam("typeName") String typeName, @Context HttpServletRequest servletRequest,@PathParam("classificationName") String classificationName) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
Servlets.validateQueryParamLength("classificationName", classificationName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteClassificationByUniqueAttribute(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.deleteClassification(guid, classificationName);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Deletes a given classification from an existing entity represented by a guid.
* @param guid globally unique identifier for the entity
* @param classificationName name of the classifcation
*/
@DELETE
@Path("/guid/{guid}/classification/{classificationName}")
public void deleteClassification(@PathParam("guid") String guid,
@PathParam("classificationName") final String classificationName,
@QueryParam("associatedEntityGuid") final String associatedEntityGuid) throws AtlasBaseException {
Servlets.validateQueryParamLength("guid", guid);
Servlets.validateQueryParamLength("classificationName", classificationName);
Servlets.validateQueryParamLength("associatedEntityGuid", associatedEntityGuid);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteClassification(" + guid + "," + classificationName + "," + associatedEntityGuid + ")");
}
if (StringUtils.isEmpty(guid)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guid);
}
ensureClassificationType(classificationName);
entitiesStore.deleteClassification(guid, classificationName, associatedEntityGuid);
} finally {
AtlasPerfTracer.log(perf);
}
}
/******************************************************************/
/** Bulk API operations **/
/******************************************************************/
/**
* Bulk API to retrieve list of entities identified by its unique attributes.
*
* In addition to the typeName path parameter, attribute key-value pair(s) can be provided in the following format
*
* typeName=<typeName>&attr_1:<attrName>=<attrValue>&attr_2:<attrName>=<attrValue>&attr_3:<attrName>=<attrValue>
*
* NOTE: The attrName should be an unique attribute for the given entity-type
*
* The REST request would look something like this
*
* GET /v2/entity/bulk/uniqueAttribute/type/hive_db?attr_0:qualifiedName=db1@cl1&attr_2:qualifiedName=db2@cl1
*
* @param typeName
* @param minExtInfo
* @param ignoreRelationships
* @return AtlasEntitiesWithExtInfo
* @throws AtlasBaseException
*/
@GET
@Path("/bulk/uniqueAttribute/type/{typeName}")
public AtlasEntitiesWithExtInfo getEntitiesByUniqueAttributes(@PathParam("typeName") String typeName,
@QueryParam("minExtInfo") @DefaultValue("false") boolean minExtInfo,
@QueryParam("ignoreRelationships") @DefaultValue("false") boolean ignoreRelationships,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
List<Map<String, Object>> uniqAttributesList = getAttributesList(servletRequest);
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getEntitiesByUniqueAttributes(" + typeName + "," + uniqAttributesList + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
for (Map<String, Object> uniqAttributes : uniqAttributesList) {
validateUniqueAttribute(entityType, uniqAttributes);
}
return entitiesStore.getEntitiesByUniqueAttributes(entityType, uniqAttributesList, minExtInfo, ignoreRelationships);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Bulk API to retrieve list of entities identified by its GUIDs.
*/
@GET
@Path("/bulk")
public AtlasEntitiesWithExtInfo getByGuids(@QueryParam("guid") List<String> guids, @QueryParam("minExtInfo") @DefaultValue("false") boolean minExtInfo, @QueryParam("ignoreRelationships") @DefaultValue("false") boolean ignoreRelationships) throws AtlasBaseException {
if (CollectionUtils.isNotEmpty(guids)) {
for (String guid : guids) {
Servlets.validateQueryParamLength("guid", guid);
}
}
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getByGuids(" + guids + ")");
}
if (CollectionUtils.isEmpty(guids)) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_GUID_NOT_FOUND, guids);
}
return entitiesStore.getByIds(guids, minExtInfo, ignoreRelationships);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Bulk API to create new entities or updates existing entities in Atlas.
* Existing entity is matched using its unique guid if supplied or by its unique attributes eg: qualifiedName
*/
@POST
@Path("/bulk")
public EntityMutationResponse createOrUpdate(AtlasEntitiesWithExtInfo entities) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.createOrUpdate(entityCount=" +
(CollectionUtils.isEmpty(entities.getEntities()) ? 0 : entities.getEntities().size()) + ")");
}
EntityStream entityStream = new AtlasEntityStream(entities);
return entitiesStore.createOrUpdate(entityStream, false);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Bulk API to delete list of entities identified by its GUIDs
*/
@DELETE
@Path("/bulk")
public EntityMutationResponse deleteByGuids(@QueryParam("guid") final List<String> guids) throws AtlasBaseException {
if (CollectionUtils.isNotEmpty(guids)) {
for (String guid : guids) {
Servlets.validateQueryParamLength("guid", guid);
}
}
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteByGuids(" + guids + ")");
}
return entitiesStore.deleteByIds(guids);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Bulk API to associate a tag to multiple entities
*/
@POST
@Path("/bulk/classification")
public void addClassification(ClassificationAssociateRequest request) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addClassification(" + request + ")");
}
AtlasClassification classification = request == null ? null : request.getClassification();
List<String> entityGuids = request == null ? null : request.getEntityGuids();
if (classification == null || StringUtils.isEmpty(classification.getTypeName())) {
throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "no classification");
}
if (CollectionUtils.isEmpty(entityGuids)) {
throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS, "empty guid list");
}
entitiesStore.addClassification(entityGuids, classification);
} finally {
AtlasPerfTracer.log(perf);
}
}
@GET
@Path("{guid}/audit")
public List<EntityAuditEventV2> getAuditEvents(@PathParam("guid") String guid, @QueryParam("startKey") String startKey,
@QueryParam("auditAction") EntityAuditActionV2 auditAction,
@QueryParam("count") @DefaultValue("100") short count) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getAuditEvents(" + guid + ", " + startKey + ", " + count + ")");
}
// following call enforces authorization for entity-read
entitiesStore.getHeaderById(guid);
List<EntityAuditEventV2> ret = new ArrayList<>();
if(auditAction != null) {
ret = auditRepository.listEventsV2(guid, auditAction, startKey, count);
} else {
List events = auditRepository.listEvents(guid, startKey, count);
for (Object event : events) {
if (event instanceof EntityAuditEventV2) {
ret.add((EntityAuditEventV2) event);
} else if (event instanceof EntityAuditEvent) {
ret.add(instanceConverter.toV2AuditEvent((EntityAuditEvent) event));
} else {
LOG.warn("unknown entity-audit event type {}. Ignored", event != null ? event.getClass().getCanonicalName() : "null");
}
}
}
return ret;
} finally {
AtlasPerfTracer.log(perf);
}
}
@GET
@Path("bulk/headers")
@Produces(Servlets.JSON_MEDIA_TYPE)
public AtlasEntityHeaders getEntityHeaders(@QueryParam("tagUpdateStartTime") long tagUpdateStartTime) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
long tagUpdateEndTime = System.currentTimeMillis();
if (tagUpdateStartTime > tagUpdateEndTime) {
throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "fromTimestamp should be less than toTimestamp");
}
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.getEntityHeaders(" + tagUpdateStartTime + ", " + tagUpdateEndTime + ")");
}
ClassificationAssociator.Retriever associator = new ClassificationAssociator.Retriever(typeRegistry, auditRepository);
return associator.get(tagUpdateStartTime, tagUpdateEndTime);
} finally {
AtlasPerfTracer.log(perf);
}
}
@POST
@Path("bulk/setClassifications")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public String setClassifications(AtlasEntityHeaders entityHeaders) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.setClassifications()");
}
ClassificationAssociator.Updater associator = new ClassificationAssociator.Updater(typeRegistry, entitiesStore);
return associator.setClassifications(entityHeaders.getGuidHeaderMap());
} finally {
AtlasPerfTracer.log(perf);
}
}
@POST
@Path("/guid/{guid}/businessmetadata")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void addOrUpdateBusinessAttributes(@PathParam("guid") final String guid, @QueryParam("isOverwrite") @DefaultValue("false") boolean isOverwrite, Map<String, Map<String, Object>> businessAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addOrUpdateBusinessAttributes(" + guid + ", isOverwrite=" + isOverwrite + ")");
}
entitiesStore.addOrUpdateBusinessAttributes(guid, businessAttributes, isOverwrite);
} finally {
AtlasPerfTracer.log(perf);
}
}
@DELETE
@Path("/guid/{guid}/businessmetadata")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void removeBusinessAttributes(@PathParam("guid") final String guid, Map<String, Map<String, Object>> businessAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.removeBusinessAttributes(" + guid + ")");
}
entitiesStore.removeBusinessAttributes(guid, businessAttributes);
} finally {
AtlasPerfTracer.log(perf);
}
}
@POST
@Path("/guid/{guid}/businessmetadata/{bmName}")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void addOrUpdateBusinessAttributes(@PathParam("guid") final String guid, @PathParam("bmName") final String bmName, Map<String, Object> businessAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addOrUpdateBusinessAttributes(" + guid + ", " + bmName + ")");
}
entitiesStore.addOrUpdateBusinessAttributes(guid, Collections.singletonMap(bmName, businessAttributes), false);
} finally {
AtlasPerfTracer.log(perf);
}
}
@DELETE
@Path("/guid/{guid}/businessmetadata/{bmName}")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void removeBusinessAttributes(@PathParam("guid") final String guid, @PathParam("bmName") final String bmName, Map<String, Object> businessAttributes) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.removeBusinessAttributes(" + guid + ", " + bmName + ")");
}
entitiesStore.removeBusinessAttributes(guid, Collections.singletonMap(bmName, businessAttributes));
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* Set labels to a given entity
* @param guid - Unique entity identifier
* @param labels - set of labels to be set to the entity
* @throws AtlasBaseException
*/
@POST
@Path("/guid/{guid}/labels")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void setLabels(@PathParam("guid") final String guid, Set<String> labels) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.setLabels()");
}
entitiesStore.setLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* delete given labels to a given entity
* @param guid - Unique entity identifier
* @throws AtlasBaseException
*/
@DELETE
@Path("/guid/{guid}/labels")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void removeLabels(@PathParam("guid") final String guid, Set<String> labels) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.deleteLabels()");
}
entitiesStore.removeLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
/**
* add given labels to a given entity
* @param guid - Unique entity identifier
* @throws AtlasBaseException
*/
@PUT
@Path("/guid/{guid}/labels")
@Produces(Servlets.JSON_MEDIA_TYPE)
@Consumes(Servlets.JSON_MEDIA_TYPE)
public void addLabels(@PathParam("guid") final String guid, Set<String> labels) throws AtlasBaseException {
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addLabels()");
}
entitiesStore.addLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
@POST
@Path("/uniqueAttribute/type/{typeName}/labels")
public void setLabels(@PathParam("typeName") String typeName, Set<String> labels,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.setLabels(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.setLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
@PUT
@Path("/uniqueAttribute/type/{typeName}/labels")
public void addLabels(@PathParam("typeName") String typeName, Set<String> labels,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.addLabels(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.addLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
@DELETE
@Path("/uniqueAttribute/type/{typeName}/labels")
public void removeLabels(@PathParam("typeName") String typeName, Set<String> labels,
@Context HttpServletRequest servletRequest) throws AtlasBaseException {
Servlets.validateQueryParamLength("typeName", typeName);
AtlasPerfTracer perf = null;
try {
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "EntityREST.removeLabels(" + typeName + ")");
}
AtlasEntityType entityType = ensureEntityType(typeName);
Map<String, Object> attributes = getAttributes(servletRequest);
String guid = entitiesStore.getGuidByUniqueAttributes(entityType, attributes);
if (guid == null) {
throw new AtlasBaseException(AtlasErrorCode.INSTANCE_BY_UNIQUE_ATTRIBUTE_NOT_FOUND, typeName, attributes.toString());
}
entitiesStore.removeLabels(guid, labels);
} finally {
AtlasPerfTracer.log(perf);
}
}
private AtlasEntityType ensureEntityType(String typeName) throws AtlasBaseException {
AtlasEntityType ret = typeRegistry.getEntityTypeByName(typeName);
if (ret == null) {
throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_INVALID, TypeCategory.ENTITY.name(), typeName);
}
return ret;
}
private AtlasClassificationType ensureClassificationType(String typeName) throws AtlasBaseException {
AtlasClassificationType ret = typeRegistry.getClassificationTypeByName(typeName);
if (ret == null) {
throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_INVALID, TypeCategory.CLASSIFICATION.name(), typeName);
}
return ret;
}
// attr:qualifiedName=db1@cl1 ==> { qualifiedName:db1@cl1 }
private Map<String, Object> getAttributes(HttpServletRequest request) {
Map<String, Object> attributes = new HashMap<>();
if (MapUtils.isNotEmpty(request.getParameterMap())) {
for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
String key = e.getKey();
if (key != null && key.startsWith(PREFIX_ATTR)) {
String[] values = e.getValue();
String value = values != null && values.length > 0 ? values[0] : null;
attributes.put(key.substring(PREFIX_ATTR.length()), value);
}
}
}
return attributes;
}
// attr_1:qualifiedName=db1@cl1&attr_2:qualifiedName=db2@cl1 ==> [ { qualifiedName:db1@cl1 }, { qualifiedName:db2@cl1 } ]
private List<Map<String, Object>> getAttributesList(HttpServletRequest request) {
Map<String, Map<String, Object>> ret = new HashMap<>();
if (MapUtils.isNotEmpty(request.getParameterMap())) {
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
String key = entry.getKey();
if (key == null || !key.startsWith(PREFIX_ATTR_)) {
continue;
}
int sepPos = key.indexOf(':', PREFIX_ATTR_.length());
String[] values = entry.getValue();
String value = values != null && values.length > 0 ? values[0] : null;
if (sepPos == -1 || value == null) {
continue;
}
String attrName = key.substring(sepPos + 1);
String listIdx = key.substring(PREFIX_ATTR_.length(), sepPos);
Map<String, Object> attributes = ret.get(listIdx);
if (attributes == null) {
attributes = new HashMap<>();
ret.put(listIdx, attributes);
}
attributes.put(attrName, value);
}
}
return new ArrayList<>(ret.values());
}
/**
* Validate that each attribute given is an unique attribute
* @param entityType the entity type
* @param attributes attributes
*/
private void validateUniqueAttribute(AtlasEntityType entityType, Map<String, Object> attributes) throws AtlasBaseException {
if (MapUtils.isEmpty(attributes)) {
throw new AtlasBaseException(AtlasErrorCode.ATTRIBUTE_UNIQUE_INVALID, entityType.getTypeName(), "");
}
for (String attributeName : attributes.keySet()) {
AtlasAttributeDef attribute = entityType.getAttributeDef(attributeName);
if (attribute == null || !attribute.getIsUnique()) {
throw new AtlasBaseException(AtlasErrorCode.ATTRIBUTE_UNIQUE_INVALID, entityType.getTypeName(), attributeName);
}
}
}
/**
* Get the sample Template for uploading/creating bulk BusinessMetaData
*
* @return Template File
* @throws AtlasBaseException
* @HTTP 400 If the provided fileType is not supported
*/
@GET
@Path("/businessmetadata/import/template")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response produceTemplate() {
return Response.ok(new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
outputStream.write(FileUtils.getBusinessMetadataHeaders().getBytes());
}
}).header("Content-Disposition", "attachment; filename=\"template_business_metadata\"").build();
}
/**
* Upload the file for creating Business Metadata in BULK
*
* @param uploadedInputStream InputStream of file
* @param fileDetail FormDataContentDisposition metadata of file
* @return
* @throws AtlasBaseException
* @HTTP 200 If Business Metadata creation was successful
* @HTTP 400 If Business Metadata definition has invalid or missing information
* @HTTP 409 If Business Metadata already exists (duplicate qualifiedName)
*/
@POST
@Path("/businessmetadata/import")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public BulkImportResponse importBMAttributes(@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) throws AtlasBaseException {
return entitiesStore.bulkCreateOrUpdateBusinessAttributes(uploadedInputStream, fileDetail.getFileName());
}
}