blob: 3202d040d539a29e3b4acce59f0d108ef6cd4e05 [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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.catalog;
import org.apache.atlas.catalog.definition.TermResourceDefinition;
import org.apache.atlas.catalog.exception.*;
import org.apache.atlas.catalog.query.AtlasQuery;
import java.util.*;
/**
* Provider for Term resources.
*/
public class TermResourceProvider extends BaseResourceProvider implements ResourceProvider {
private ResourceProvider taxonomyResourceProvider;
private ResourceProvider entityResourceProvider;
private ResourceProvider entityTagResourceProvider;
public TermResourceProvider(AtlasTypeSystem typeSystem) {
super(typeSystem, new TermResourceDefinition());
}
@Override
public Result getResourceById(Request request) throws ResourceNotFoundException {
//todo: shouldn't need to add this here
request.getQueryProperties().put("name", request.<TermPath>getProperty("termPath").getFullyQualifiedName());
AtlasQuery atlasQuery;
try {
atlasQuery = queryFactory.createTermQuery(request);
} catch (InvalidQueryException e) {
throw new CatalogRuntimeException("Unable to compile internal Term query: " + e, e);
}
Collection<Map<String, Object>> results = atlasQuery.execute();
if (results.isEmpty()) {
throw new ResourceNotFoundException(String.format("Term '%s' not found.",
request.<TermPath>getProperty("termPath").getFullyQualifiedName()));
}
return new Result(results);
}
public Result getResources(Request request)
throws InvalidQueryException, ResourceNotFoundException {
TermPath termPath = request.getProperty("termPath");
String queryString = doQueryStringConversions(termPath, request.getQueryString());
Request queryRequest = new CollectionRequest(request.getQueryProperties(), queryString);
AtlasQuery atlasQuery = queryFactory.createTermQuery(queryRequest);
Collection<Map<String, Object>> result = atlasQuery.execute();
return new Result(result);
}
public void createResource(Request request)
throws InvalidPayloadException, ResourceAlreadyExistsException, ResourceNotFoundException {
TermPath termPath = (TermPath) request.getQueryProperties().remove("termPath");
String qualifiedTermName = termPath.getFullyQualifiedName();
request.getQueryProperties().put("name", qualifiedTermName);
resourceDefinition.validateCreatePayload(request);
// get taxonomy
Request taxonomyRequest = new InstanceRequest(
Collections.<String, Object>singletonMap("name", termPath.getTaxonomyName()));
taxonomyRequest.addAdditionalSelectProperties(Collections.singleton("id"));
Result taxonomyResult = getTaxonomyResourceProvider().getResourceById(taxonomyRequest);
Map<String, Object> taxonomyPropertyMap = taxonomyResult.getPropertyMaps().iterator().next();
// ensure that parent exists if not a root level term
if (! termPath.getPath().equals("/")) {
Map<String, Object> parentProperties = new HashMap<>(request.getQueryProperties());
parentProperties.put("termPath", termPath.getParent());
getResourceById(new InstanceRequest(parentProperties));
}
typeSystem.createTraitType(resourceDefinition, qualifiedTermName,
request.<String>getProperty("description"));
typeSystem.createTraitInstance(String.valueOf(taxonomyPropertyMap.get("id")),
qualifiedTermName, request.getQueryProperties());
}
@Override
public Collection<String> createResources(Request request) throws InvalidQueryException, ResourceNotFoundException {
throw new UnsupportedOperationException("Creating multiple Terms in a request is not currently supported");
}
@Override
public void updateResourceById(Request request) throws ResourceNotFoundException, InvalidPayloadException {
resourceDefinition.validateUpdatePayload(request);
String termName = request.<TermPath>getProperty("termPath").getFullyQualifiedName();
request.getQueryProperties().put("name", termName);
AtlasQuery atlasQuery;
try {
atlasQuery = queryFactory.createTermQuery(request);
} catch (InvalidQueryException e) {
throw new CatalogRuntimeException("Unable to compile internal Term query: " + e, e);
}
Map<String, Object> updateProperties = request.getUpdateProperties();
Collection<Map<String, Object>> results = atlasQuery.execute(updateProperties);
if (results.isEmpty()) {
throw new ResourceNotFoundException(String.format("Term '%s' not found.",
termName));
}
// only the term 'description' property is set on entity tags
if (updateProperties.containsKey("description")) {
// 'description' property is being updated so we need to update tags
String tagQueryString = String.format("name:%s", termName);
Request tagRequest = new CollectionRequest(
Collections.<String, Object>singletonMap("id", "*"), tagQueryString, null);
AtlasQuery tagQuery;
try {
tagQuery = queryFactory.createEntityTagQuery(tagRequest);
} catch (InvalidQueryException e) {
throw new CatalogRuntimeException("Unable to compile internal Entity Tag query: " + e, e);
}
tagQuery.execute(Collections.singletonMap("description", updateProperties.get("description")));
}
}
@Override
public void deleteResourceById(Request request) throws ResourceNotFoundException, InvalidPayloadException {
// will result in expected ResourceNotFoundException if term doesn't exist
getResourceById(request);
TermPath termPath = (TermPath) request.getQueryProperties().get("termPath");
String taxonomyId = getTaxonomyId(termPath);
deleteChildren(taxonomyId, termPath);
deleteTerm(taxonomyId, termPath);
}
protected void deleteChildren(String taxonomyId, TermPath termPath)
throws ResourceNotFoundException, InvalidPayloadException {
TermPath collectionTermPath = new TermPath(termPath.getFullyQualifiedName() + ".");
Request queryRequest = new CollectionRequest(Collections.<String, Object>singletonMap("termPath",
collectionTermPath), null);
AtlasQuery collectionQuery;
try {
collectionQuery = queryFactory.createTermQuery(queryRequest);
} catch (InvalidQueryException e) {
throw new CatalogRuntimeException("Failed to compile internal predicate: " + e, e);
}
Collection<Map<String, Object>> children = collectionQuery.execute();
for (Map<String, Object> childMap : children) {
deleteTerm(taxonomyId, new TermPath(String.valueOf(childMap.get("name"))));
}
}
private void deleteTerm(String taxonomyId, TermPath termPath)
throws ResourceNotFoundException, InvalidPayloadException {
String fullyQualifiedName = termPath.getFullyQualifiedName();
deleteEntityTagsForTerm(fullyQualifiedName);
// delete term instance associated with the taxonomy
typeSystem.deleteTag(taxonomyId, fullyQualifiedName);
//todo: Currently no way to delete type via MetadataService or MetadataRepository
}
private void deleteEntityTagsForTerm(String fullyQualifiedName) throws ResourceNotFoundException {
String entityQueryStr = String.format("tags/name:%s", fullyQualifiedName);
Request entityRequest = new CollectionRequest(Collections.<String, Object>emptyMap(), entityQueryStr);
Result entityResult;
try {
entityResult = getEntityResourceProvider().getResources(entityRequest);
} catch (InvalidQueryException e) {
throw new CatalogRuntimeException(String.format(
"Failed to compile internal predicate for query '%s': %s", entityQueryStr, e), e);
}
for (Map<String, Object> entityResultMap : entityResult.getPropertyMaps()) {
Map<String, Object> tagRequestProperties = new HashMap<>();
tagRequestProperties.put("id", String.valueOf(entityResultMap.get("id")));
tagRequestProperties.put("name", fullyQualifiedName);
try {
getEntityTagResourceProvider().deleteResourceById(new InstanceRequest(tagRequestProperties));
} catch (InvalidPayloadException e) {
throw new CatalogRuntimeException(
"An internal error occurred while trying to delete an entity tag: " + e, e);
}
}
}
private String getTaxonomyId(TermPath termPath) throws ResourceNotFoundException {
Request taxonomyRequest = new InstanceRequest(Collections.<String, Object>singletonMap(
"name", termPath.getTaxonomyName()));
taxonomyRequest.addAdditionalSelectProperties(Collections.singleton("id"));
// will result in proper ResourceNotFoundException if taxonomy doesn't exist
Result taxonomyResult = getTaxonomyResourceProvider().getResourceById(taxonomyRequest);
Map<String, Object> taxonomyResultMap = taxonomyResult.getPropertyMaps().iterator().next();
return String.valueOf(taxonomyResultMap.get("id"));
}
//todo: add generic support for pre-query modification of expected value
//todo: similar path parsing code is used in several places in this class
private String doQueryStringConversions(TermPath termPath, String queryStr) throws InvalidQueryException {
String hierarchyPathProp = "hierarchy/path";
// replace "."
if (queryStr != null && queryStr.contains(String.format("%s:.", hierarchyPathProp))) {
//todo: regular expression replacement
queryStr = queryStr.replaceAll(String.format("%s:.", hierarchyPathProp),
String.format("%s:%s", hierarchyPathProp, termPath.getPath()));
}
return queryStr;
}
protected synchronized ResourceProvider getTaxonomyResourceProvider() {
if (taxonomyResourceProvider == null) {
taxonomyResourceProvider = new TaxonomyResourceProvider(typeSystem);
}
return taxonomyResourceProvider;
}
protected synchronized ResourceProvider getEntityResourceProvider() {
if (entityResourceProvider == null) {
entityResourceProvider = new EntityResourceProvider(typeSystem);
}
return entityResourceProvider;
}
protected synchronized ResourceProvider getEntityTagResourceProvider() {
if (entityTagResourceProvider == null) {
entityTagResourceProvider = new EntityTagResourceProvider(typeSystem);
}
return entityTagResourceProvider;
}
}