You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by yh...@apache.org on 2016/05/18 13:03:59 UTC
[3/5] incubator-atlas git commit: ATLAS-491 Business Catalog /
Taxonomy (jspeidel via yhemanth)
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQueryExpression.java
new file mode 100644
index 0000000..2364ee5
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/BaseQueryExpression.java
@@ -0,0 +1,105 @@
+/**
+ * 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.query;
+
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.filter.FilterFunctionPipe;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * Base query expression class.
+ */
+public abstract class BaseQueryExpression implements QueryExpression {
+ protected String m_field;
+ protected final String m_expectedValue;
+ protected final ResourceDefinition resourceDefinition;
+ protected boolean negate = false;
+ protected Collection<String> properties = new HashSet<>();
+
+ protected BaseQueryExpression(String field, String expectedValue, ResourceDefinition resourceDefinition) {
+ m_field = field;
+ if (field != null) {
+ properties.add(field);
+ }
+ m_expectedValue = expectedValue;
+ this.resourceDefinition = resourceDefinition;
+ }
+
+ @Override
+ public boolean evaluate(VertexWrapper vWrapper) {
+ return negate ^ evaluate(vWrapper.getProperty(m_field));
+ }
+
+ @Override
+ public Collection<String> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ // subclasses which don't override evaluate(VertexWrapper) should implement this
+ return false;
+ }
+
+ //todo: use 'has' instead of closure where possible for performance
+ public Pipe asPipe() {
+ return new FilterFunctionPipe(new PipeFunction<Vertex, Boolean>() {
+ @Override
+ public Boolean compute(Vertex vertex) {
+ return evaluate(new VertexWrapper(vertex, resourceDefinition));
+ }
+ });
+ }
+
+ @Override
+ public String getField() {
+ return m_field;
+ }
+
+ @Override
+ public String getExpectedValue() {
+ return m_expectedValue;
+ }
+
+ @Override
+ public void setField(String field) {
+ m_field = field;
+ }
+
+ @Override
+ public void setNegate() {
+ this.negate = true;
+ }
+
+ @Override
+ public boolean isNegate() {
+ return negate;
+ }
+
+ @Override
+ public boolean isProjectionExpression() {
+ return getField() != null && getField().contains(QueryFactory.PATH_SEP_TOKEN);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/BooleanQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/BooleanQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/BooleanQueryExpression.java
new file mode 100644
index 0000000..b4d759a
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/BooleanQueryExpression.java
@@ -0,0 +1,141 @@
+/**
+ * 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.query;
+
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.filter.AndFilterPipe;
+import com.tinkerpop.pipes.filter.OrFilterPipe;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+
+import java.util.*;
+
+/**
+ * Expression where operands are other expressions and operator is logical AND or OR
+ */
+public class BooleanQueryExpression extends BaseQueryExpression {
+ private final BooleanClause[] clauses;
+ private final QueryFactory queryFactory;
+
+ public BooleanQueryExpression(BooleanQuery query, ResourceDefinition resourceDefinition, QueryFactory queryFactory) {
+ super(null, null, resourceDefinition);
+ clauses = query.getClauses();
+ this.queryFactory = queryFactory;
+ }
+
+ @Override
+ public Pipe asPipe() {
+ Map<BooleanClause.Occur, Collection<BooleanClause>> groupedClauses = groupClauses();
+
+ Pipe andPipe = null;
+ Collection<Pipe> andPipes = processAndClauses(groupedClauses);
+ andPipes.addAll(processNotClauses(groupedClauses));
+ if (! andPipes.isEmpty()) {
+ andPipe = new AndFilterPipe(andPipes.toArray(new Pipe[andPipes.size()]));
+ }
+
+ Collection<Pipe> orPipes = processOrClauses(groupedClauses);
+ if (! orPipes.isEmpty()) {
+ if (andPipe != null) {
+ orPipes.add(andPipe);
+ }
+ return new OrFilterPipe(orPipes.toArray(new Pipe[orPipes.size()]));
+ } else {
+ return andPipe;
+ }
+ }
+
+ private Map<BooleanClause.Occur, Collection<BooleanClause>> groupClauses() {
+ Map<BooleanClause.Occur, Collection<BooleanClause>> groupedClauses = new HashMap<>();
+ for (BooleanClause clause : clauses) {
+ BooleanClause.Occur occur = resolveClauseOccur(clause);
+ Collection<BooleanClause> clauseGrouping = groupedClauses.get(occur);
+ if (clauseGrouping == null) {
+ clauseGrouping = new ArrayList<>();
+ groupedClauses.put(occur, clauseGrouping);
+ }
+ clauseGrouping.add(clause);
+ }
+ return groupedClauses;
+ }
+
+ private BooleanClause.Occur resolveClauseOccur(BooleanClause clause) {
+ BooleanClause.Occur occur = clause.getOccur();
+ if (negate) {
+ switch (occur) {
+ case SHOULD:
+ occur = BooleanClause.Occur.MUST_NOT;
+ break;
+ case MUST:
+ occur = BooleanClause.Occur.SHOULD;
+ break;
+ case MUST_NOT:
+ occur = BooleanClause.Occur.SHOULD;
+ break;
+ }
+ }
+ return occur;
+ }
+
+ private Collection<Pipe> processAndClauses(Map<BooleanClause.Occur, Collection<BooleanClause>> groupedClauses) {
+ Collection<BooleanClause> andClauses = groupedClauses.get(BooleanClause.Occur.MUST);
+ Collection<Pipe> andPipes = new ArrayList<>();
+ if (andClauses != null) {
+ for (BooleanClause andClause : andClauses) {
+ QueryExpression queryExpression = queryFactory.create(andClause.getQuery(), resourceDefinition);
+ properties.addAll(queryExpression.getProperties());
+ andPipes.add(queryExpression.asPipe());
+ }
+ }
+ return andPipes;
+ }
+
+
+ private Collection<Pipe> processOrClauses(Map<BooleanClause.Occur, Collection<BooleanClause>> groupedClauses) {
+ Collection<BooleanClause> shouldClauses = groupedClauses.get(BooleanClause.Occur.SHOULD);
+ Collection<Pipe> orPipes = new ArrayList<>();
+ if (shouldClauses != null) {
+ for (BooleanClause shouldClause : shouldClauses) {
+ QueryExpression queryExpression = queryFactory.create(shouldClause.getQuery(), resourceDefinition);
+ // don't negate expression if we negated MUST_NOT -> SHOULD
+ if (negate && shouldClause.getOccur() != BooleanClause.Occur.MUST_NOT) {
+ queryExpression.setNegate();
+ }
+ properties.addAll(queryExpression.getProperties());
+ orPipes.add(queryExpression.asPipe());
+ }
+ }
+ return orPipes;
+ }
+
+ private Collection<Pipe> processNotClauses(Map<BooleanClause.Occur, Collection<BooleanClause>> groupedClauses) {
+ Collection<BooleanClause> notClauses = groupedClauses.get(BooleanClause.Occur.MUST_NOT);
+ Collection<Pipe> notPipes = new ArrayList<>();
+ if (notClauses != null) {
+ for (BooleanClause notClause : notClauses) {
+ QueryExpression queryExpression = queryFactory.create(notClause.getQuery(), resourceDefinition);
+ queryExpression.setNegate();
+ properties.addAll(queryExpression.getProperties());
+ notPipes.add(queryExpression.asPipe());
+ }
+ }
+ return notPipes;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/PrefixQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/PrefixQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/PrefixQueryExpression.java
new file mode 100644
index 0000000..6b43667
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/PrefixQueryExpression.java
@@ -0,0 +1,39 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.search.PrefixQuery;
+
+/**
+ * Expression that evaluates whether a property starts with a prefix.
+ */
+public class PrefixQueryExpression extends BaseQueryExpression {
+
+ // query 'f*' results in a PrefixQuery
+ public PrefixQueryExpression(PrefixQuery query, ResourceDefinition resourceDefinition) {
+ super(query.getPrefix().field(), query.getPrefix().text(), resourceDefinition);
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ return value != null && String.valueOf(value).startsWith(getExpectedValue());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/ProjectionQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/ProjectionQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/ProjectionQueryExpression.java
new file mode 100644
index 0000000..b915877
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/ProjectionQueryExpression.java
@@ -0,0 +1,122 @@
+/**
+ * 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.query;
+
+import com.thinkaurelius.titan.core.attribute.Text;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
+import com.tinkerpop.pipes.Pipe;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.filter.FilterFunctionPipe;
+import org.apache.atlas.catalog.VertexWrapper;
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.atlas.catalog.projection.ProjectionResult;
+import org.apache.atlas.catalog.projection.Relation;
+
+import java.util.*;
+
+/**
+ * Query expression wrapper which handles projection queries.
+ */
+public class ProjectionQueryExpression extends BaseQueryExpression {
+
+ private final QueryExpression underlyingExpression;
+ private final ResourceDefinition resourceDefinition;
+
+ private final String[] fieldSegments;
+
+ protected ProjectionQueryExpression(QueryExpression underlyingExpression, ResourceDefinition resourceDefinition) {
+ super(underlyingExpression.getField(), underlyingExpression.getExpectedValue(), resourceDefinition);
+
+ this.underlyingExpression = underlyingExpression;
+ this.resourceDefinition = resourceDefinition;
+ this.fieldSegments = getField().split(QueryFactory.PATH_SEP_TOKEN);
+ }
+
+ @Override
+ public Pipe asPipe() {
+ //todo: encapsulate all of this path logic including path sep escaping and normalizing
+ final int sepIdx = getField().indexOf(QueryFactory.PATH_SEP_TOKEN);
+ final String edgeToken = getField().substring(0, sepIdx);
+ GremlinPipeline pipeline = new GremlinPipeline();
+
+ Relation relation = resourceDefinition.getRelations().get(fieldSegments[0]);
+ if (relation != null) {
+ pipeline = pipeline.outE();
+ pipeline.add(relation.asPipe()).inV();
+ } else {
+ if (resourceDefinition.getProjections().get(fieldSegments[0]) != null) {
+ return super.asPipe();
+ } else {
+ //todo: default Relation implementation
+ pipeline = pipeline.outE().has("label", Text.REGEX, String.format(".*\\.%s", edgeToken)).inV();
+ }
+ }
+ //todo: set resource definition from relation on underlying expression where appropriate
+ String childFieldName = getField().substring(sepIdx + QueryFactory.PATH_SEP_TOKEN.length());
+ underlyingExpression.setField(childFieldName);
+
+ Pipe childPipe;
+ if (childFieldName.contains(QueryFactory.PATH_SEP_TOKEN)) {
+ childPipe = new ProjectionQueryExpression(underlyingExpression, resourceDefinition).asPipe();
+ } else {
+ childPipe = underlyingExpression.asPipe();
+ }
+ pipeline.add(childPipe);
+
+ return negate ? new FilterFunctionPipe(new ExcludePipeFunction(pipeline)) : pipeline;
+ }
+
+ @Override
+ public boolean evaluate(VertexWrapper vWrapper) {
+ boolean result = false;
+ Iterator<ProjectionResult> projectionIterator = resourceDefinition.getProjections().
+ get(fieldSegments[0]).values(vWrapper).iterator();
+
+ while (! result && projectionIterator.hasNext()) {
+ ProjectionResult projectionResult = projectionIterator.next();
+ for (Map<String, Object> propertyMap : projectionResult.getPropertyMaps()) {
+ Object val = propertyMap.get(fieldSegments[1]);
+ if (val != null && underlyingExpression.evaluate(QueryFactory.escape(val))) {
+ result = true;
+ break;
+ }
+ }
+ }
+ return negate ^ result;
+ }
+
+ private static class ExcludePipeFunction implements PipeFunction<Object, Boolean> {
+ private final GremlinPipeline excludePipeline;
+
+ public ExcludePipeFunction(GremlinPipeline excludePipeline) {
+ this.excludePipeline = excludePipeline;
+ }
+
+ @Override
+ public Boolean compute(Object vertices) {
+ GremlinPipeline p = new GremlinPipeline(Collections.singleton(vertices));
+ p.add(excludePipeline);
+ return p.gather().toList().isEmpty();
+ }
+ }
+
+ protected QueryExpression getUnderlyingExpression() {
+ return underlyingExpression;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/QueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/QueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/QueryExpression.java
new file mode 100644
index 0000000..78436c0
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/QueryExpression.java
@@ -0,0 +1,99 @@
+/**
+ * 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.query;
+
+import com.tinkerpop.pipes.Pipe;
+import org.apache.atlas.catalog.VertexWrapper;
+
+import java.util.Collection;
+
+/**
+ * Represents a query expression.
+ */
+public interface QueryExpression {
+ /**
+ * Evaluate the expression based on properties of the provied vertex.
+ *
+ * @param vWrapper vertex wrapper that expression is applied to
+ * @return result of expression evaluation
+ */
+ boolean evaluate(VertexWrapper vWrapper);
+
+ /**
+ * Evaluate the expression based on the provided value.
+ *
+ * @param value value used to evaluate expression
+ * @return
+ */
+ boolean evaluate(Object value);
+
+ /**
+ * Get the complete set of properties which are contained in the expression.
+ *
+ * @return collection of expression properties
+ */
+ Collection<String> getProperties();
+
+ /**
+ * Get the pipe representation of the expression.
+ *
+ * @return pipe representation
+ */
+ Pipe asPipe();
+
+ /**
+ * Negate the expression.
+ */
+ void setNegate();
+
+ /**
+ * Get the negate status of the expression.
+ *
+ * @return true if the expression is negated, false otherwise
+ */
+ boolean isNegate();
+
+ /**
+ * Determine whether the expression is being applied to a projection.
+ *
+ * @return true if expression is being applied to a projection, false otherwise
+ */
+ boolean isProjectionExpression();
+
+ /**
+ * Get the field name used in the expression.
+ *
+ * @return expression field name or null if there is no field name
+ */
+ String getField();
+
+ /**
+ * Set the expressions field name.
+ *
+ * @param fieldName field name
+ */
+ public void setField(String fieldName);
+
+ /**
+ * Get the expected value for the expression.
+ *
+ * @return expected value or null if there isn't a expected value
+ */
+ String getExpectedValue();
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/QueryFactory.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/QueryFactory.java b/catalog/src/main/java/org/apache/atlas/catalog/query/QueryFactory.java
new file mode 100644
index 0000000..39ce11a
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/QueryFactory.java
@@ -0,0 +1,182 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.Request;
+import org.apache.atlas.catalog.TermPath;
+import org.apache.atlas.catalog.definition.*;
+import org.apache.atlas.catalog.exception.CatalogRuntimeException;
+import org.apache.atlas.catalog.exception.InvalidQueryException;
+import org.apache.lucene.analysis.core.KeywordAnalyzer;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.sandbox.queries.regex.RegexQuery;
+import org.apache.lucene.search.*;
+import org.apache.lucene.util.Version;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Factory used to create QueryAdapter instances.
+ */
+public class QueryFactory {
+ private static final Logger LOG = LoggerFactory.getLogger(QueryFactory.class);
+ public static final String PATH_SEP_TOKEN = "__slash__";
+
+ private final Map<Class<? extends Query>, ExpressionCreateFunction<? extends Query>>
+ expressionCreateFunctions = new HashMap<>();
+
+ public QueryFactory() {
+ registerExpressionCreateFunctions();
+ }
+
+ public AtlasQuery createTaxonomyQuery(Request request) throws InvalidQueryException {
+ ResourceDefinition taxonomyDefinition = new TaxonomyResourceDefinition();
+ QueryExpression queryExpression = create(request, taxonomyDefinition);
+ return new AtlasTaxonomyQuery(queryExpression, taxonomyDefinition, request);
+ }
+
+ public AtlasQuery createTermQuery(Request request) throws InvalidQueryException {
+ ResourceDefinition termDefinition = new TermResourceDefinition();
+ QueryExpression queryExpression = create(request, termDefinition);
+ TermPath termPath = request.getProperty("termPath");
+ return new AtlasTermQuery(queryExpression, termDefinition, termPath, request);
+ }
+
+ public AtlasQuery createEntityQuery(Request request) throws InvalidQueryException {
+ ResourceDefinition entityDefinition = new EntityResourceDefinition();
+ QueryExpression queryExpression = create(request, entityDefinition);
+ return new AtlasEntityQuery(queryExpression, entityDefinition, request);
+ }
+
+ public AtlasQuery createEntityTagQuery(Request request) throws InvalidQueryException {
+ ResourceDefinition entityTagDefinition = new EntityTagResourceDefinition();
+ QueryExpression queryExpression = create(request, entityTagDefinition);
+ String guid = request.getProperty("id");
+ return new AtlasEntityTagQuery(queryExpression, entityTagDefinition, guid, request);
+ }
+
+ private QueryExpression create(Request request, ResourceDefinition resourceDefinition) throws InvalidQueryException {
+ String queryString;
+ if (request.getCardinality() == Request.Cardinality.INSTANCE) {
+ String idPropertyName = resourceDefinition.getIdPropertyName();
+ queryString = String.format("%s:%s", idPropertyName, request.<String>getProperty(idPropertyName));
+ } else {
+ queryString = request.getQueryString();
+ }
+
+ QueryExpression queryExpression;
+ if (queryString != null && !queryString.isEmpty()) {
+ QueryParser queryParser = new QueryParser(Version.LUCENE_48, "name", new KeywordAnalyzer());
+ queryParser.setLowercaseExpandedTerms(false);
+ Query query;
+ try {
+ query = queryParser.parse((String) escape(queryString));
+ } catch (ParseException e) {
+ throw new InvalidQueryException(e.getMessage());
+ }
+ LOG.info("LuceneQuery: " + query);
+ queryExpression = create(query, resourceDefinition);
+ } else {
+ queryExpression = new AlwaysQueryExpression();
+ }
+ // add query properties to request so that they are returned
+ request.addAdditionalSelectProperties(queryExpression.getProperties());
+ return queryExpression;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <T extends Query> QueryExpression create(T query, ResourceDefinition resourceDefinition) {
+ if (! expressionCreateFunctions.containsKey(query.getClass())) {
+ throw new CatalogRuntimeException("Query type currently not supported: " + query.getClass(), 400);
+ }
+ //todo: fix generic typing
+ ExpressionCreateFunction expressionCreateFunction = expressionCreateFunctions.get(query.getClass());
+ return expressionCreateFunction.createExpression(query, resourceDefinition);
+
+ }
+
+ // "escapes" characters as necessary for lucene parser
+ //todo: currently '/' characters are blindly being replaced but this will not allow regex queries to be used
+ protected static Object escape(Object val) {
+ if (val instanceof String) {
+ return ((String)val).replaceAll("/", PATH_SEP_TOKEN);
+ } else {
+ return val;
+ }
+ }
+
+ private abstract static class ExpressionCreateFunction<T extends Query> {
+ QueryExpression createExpression(T query, ResourceDefinition resourceDefinition) {
+ QueryExpression expression = create(query, resourceDefinition);
+ return expression.isProjectionExpression() ?
+ new ProjectionQueryExpression(expression, resourceDefinition) :
+ expression;
+ }
+
+ protected abstract QueryExpression create(T query, ResourceDefinition resourceDefinition);
+ }
+
+ private void registerExpressionCreateFunctions() {
+ expressionCreateFunctions.put(WildcardQuery.class, new ExpressionCreateFunction<WildcardQuery>() {
+ @Override
+ public QueryExpression create(WildcardQuery query, ResourceDefinition definition) {
+ return new WildcardQueryExpression(query, definition);
+ }
+ });
+
+ expressionCreateFunctions.put(PrefixQuery.class, new ExpressionCreateFunction<PrefixQuery>() {
+ @Override
+ public QueryExpression create(PrefixQuery query, ResourceDefinition definition) {
+ return new PrefixQueryExpression(query, definition);
+ }
+ });
+
+ expressionCreateFunctions.put(TermQuery.class, new ExpressionCreateFunction<TermQuery>() {
+ @Override
+ public QueryExpression create(TermQuery query, ResourceDefinition definition) {
+ return new TermQueryExpression(query, definition);
+ }
+ });
+
+ expressionCreateFunctions.put(TermRangeQuery.class, new ExpressionCreateFunction<TermRangeQuery>() {
+ @Override
+ public QueryExpression create(TermRangeQuery query, ResourceDefinition definition) {
+ return new TermRangeQueryExpression(query, definition);
+ }
+ });
+
+ expressionCreateFunctions.put(RegexQuery.class, new ExpressionCreateFunction<RegexQuery>() {
+ @Override
+ public QueryExpression create(RegexQuery query, ResourceDefinition definition) {
+ return new RegexQueryExpression(query, definition);
+ }
+ });
+
+ expressionCreateFunctions.put(BooleanQuery.class, new ExpressionCreateFunction<BooleanQuery>() {
+ @Override
+ public QueryExpression create(BooleanQuery query, ResourceDefinition definition) {
+ return new BooleanQueryExpression(query, definition, QueryFactory.this);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/RegexQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/RegexQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/RegexQueryExpression.java
new file mode 100644
index 0000000..c28d4d5
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/RegexQueryExpression.java
@@ -0,0 +1,41 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.sandbox.queries.regex.RegexQuery;
+
+import java.util.regex.Pattern;
+
+/**
+ * Query expression which evaluates a property against a regular expression.
+ */
+public class RegexQueryExpression extends BaseQueryExpression {
+
+ public RegexQueryExpression(RegexQuery query, ResourceDefinition resourceDefinition) {
+ super(query.getField(), query.getTerm().text(), resourceDefinition);
+
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ Pattern p = Pattern.compile(getExpectedValue());
+ return value != null && p.matcher(String.valueOf(value)).matches();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/TermQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/TermQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/TermQueryExpression.java
new file mode 100644
index 0000000..a790866
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/TermQueryExpression.java
@@ -0,0 +1,52 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.search.TermQuery;
+
+import java.util.Collection;
+
+/**
+ * Query expression which evaluates whether a property equals a value.
+ */
+public class TermQueryExpression extends BaseQueryExpression {
+
+ public TermQueryExpression(TermQuery query, ResourceDefinition resourceDefinition) {
+ super(query.getTerm().field(), query.getTerm().text(), resourceDefinition);
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ String expectedValue = getExpectedValue();
+ if (value == null) {
+ return expectedValue.equals("null");
+ //todo: refactor; we shouldn't need to use instanceof/cast here
+ } else if (value instanceof Collection) {
+ return ((Collection)value).contains(expectedValue);
+ } else {
+ return expectedValue.equals(QueryFactory.escape(String.valueOf(value)));
+ }
+ }
+
+ public String getExpectedValue() {
+ return m_expectedValue;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/TermRangeQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/TermRangeQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/TermRangeQueryExpression.java
new file mode 100644
index 0000000..44cfb72
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/TermRangeQueryExpression.java
@@ -0,0 +1,61 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.search.TermRangeQuery;
+import org.apache.lucene.util.BytesRef;
+
+/**
+ * Query expression which evaluates whether a property value is within a range.
+ */
+//todo: for month and year which are expressed via a single digit, must ensure that
+//todo: a leading '0' is provided. For example, "2016-1-5" must be converted to "2016-01-05".
+//todo: Month and day values aren't currently validated.
+public class TermRangeQueryExpression extends BaseQueryExpression {
+ private final BytesRef m_lowerTerm;
+ private final BytesRef m_upperTerm;
+ private final boolean m_lowerInclusive;
+ private final boolean m_upperInclusive;
+
+ public TermRangeQueryExpression(TermRangeQuery query, ResourceDefinition resourceDefinition) {
+ super(query.getField(), null, resourceDefinition);
+ m_lowerTerm = query.getLowerTerm();
+ m_upperTerm = query.getUpperTerm();
+ m_lowerInclusive = query.includesLower();
+ m_upperInclusive = query.includesUpper();
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ BytesRef valueBytes = new BytesRef(String.valueOf(value));
+ return compareLowerBound(valueBytes) && compareUpperBound(valueBytes);
+ }
+
+ private boolean compareLowerBound(BytesRef valueBytes) {
+ return m_lowerTerm == null || (m_lowerInclusive ? valueBytes.compareTo(m_lowerTerm) > 0 :
+ valueBytes.compareTo(m_lowerTerm) >= 0);
+ }
+
+ private boolean compareUpperBound(BytesRef valueBytes) {
+ return m_upperTerm == null || (m_upperInclusive ? valueBytes.compareTo(m_upperTerm) < 0 :
+ valueBytes.compareTo(m_upperTerm) <= 0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/main/java/org/apache/atlas/catalog/query/WildcardQueryExpression.java
----------------------------------------------------------------------
diff --git a/catalog/src/main/java/org/apache/atlas/catalog/query/WildcardQueryExpression.java b/catalog/src/main/java/org/apache/atlas/catalog/query/WildcardQueryExpression.java
new file mode 100644
index 0000000..689891f
--- /dev/null
+++ b/catalog/src/main/java/org/apache/atlas/catalog/query/WildcardQueryExpression.java
@@ -0,0 +1,43 @@
+/**
+ * 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.query;
+
+import org.apache.atlas.catalog.definition.ResourceDefinition;
+import org.apache.lucene.search.WildcardQuery;
+
+import java.util.regex.Pattern;
+
+/**
+ * Query expression which evaluates values with wildcards.
+ * This differs from PrefixQueryExpression which handles expressions which end with a wildcard.
+ */
+public class WildcardQueryExpression extends BaseQueryExpression {
+
+ public WildcardQueryExpression(WildcardQuery query, ResourceDefinition resourceDefinition) {
+ super(query.getTerm().field(), query.getTerm().text(), resourceDefinition);
+ }
+
+ @Override
+ public boolean evaluate(Object value) {
+ // replace '*' with ".*"
+ // replace '?' with '.'
+ String regex = getExpectedValue().replaceAll("\\*", ".*").replaceAll("\\?", ".");
+ return Pattern.compile(regex).matcher(String.valueOf(value)).matches();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/CollectionRequestTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/CollectionRequestTest.java b/catalog/src/test/java/org/apache/atlas/catalog/CollectionRequestTest.java
new file mode 100644
index 0000000..0a2bace
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/CollectionRequestTest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.testng.annotations.Test;
+
+import java.util.*;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Unit tests for CollectionRequest.
+ */
+public class CollectionRequestTest {
+ @Test
+ public void testNoProperties() {
+ String query = "name:foo*";
+ Request request = new CollectionRequest(null, query);
+
+ assertEquals(Request.Cardinality.COLLECTION, request.getCardinality());
+ assertTrue(request.getProperties().isEmpty());
+ assertNull(request.getProperty("foo"));
+ assertTrue(request.getAdditionalSelectProperties().isEmpty());
+ }
+
+ @Test
+ public void testWithProperties() {
+ String query = "name:foo*";
+ Map<String, Object> properties = new HashMap<>();
+ properties.put("foo", "fooValue");
+ properties.put("someBoolean", true);
+ Request request = new CollectionRequest(properties, query);
+
+ assertEquals(Request.Cardinality.COLLECTION, request.getCardinality());
+ assertEquals(properties, request.getProperties());
+ assertEquals("fooValue", request.getProperty("foo"));
+ assertTrue(request.<Boolean>getProperty("someBoolean"));
+ assertNull(request.getProperty("other"));
+ assertTrue(request.getAdditionalSelectProperties().isEmpty());
+ }
+
+ @Test
+ public void testSelectProperties() {
+ String query = "name:foo*";
+ Request request = new CollectionRequest(null, query);
+ Collection<String> additionalSelectProps = new ArrayList<>();
+ additionalSelectProps.add("foo");
+ additionalSelectProps.add("bar");
+ request.addAdditionalSelectProperties(additionalSelectProps);
+ Collection<String> requestAdditionalSelectProps = request.getAdditionalSelectProperties();
+ assertEquals(2, requestAdditionalSelectProps.size());
+ assertTrue(requestAdditionalSelectProps.contains("foo"));
+ assertTrue(requestAdditionalSelectProps.contains("bar"));
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/DefaultDateFormatterTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/DefaultDateFormatterTest.java b/catalog/src/test/java/org/apache/atlas/catalog/DefaultDateFormatterTest.java
new file mode 100644
index 0000000..bbc98c5
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/DefaultDateFormatterTest.java
@@ -0,0 +1,41 @@
+/**
+ * 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.testng.annotations.Test;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Unit tests for DefaultDateFormatter.
+ */
+public class DefaultDateFormatterTest {
+ @Test
+ public void test() {
+ Calendar calendar = new GregorianCalendar(2016, 0, 20, 5, 10, 15);
+ long millis = calendar.getTimeInMillis();
+
+ DefaultDateFormatter dateFormatter = new DefaultDateFormatter();
+ // month starts at 0 so we need to add 1
+ assertEquals("2016-01-20:05:10:15", dateFormatter.format(millis));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/DefaultPropertyMapperTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/DefaultPropertyMapperTest.java b/catalog/src/test/java/org/apache/atlas/catalog/DefaultPropertyMapperTest.java
new file mode 100644
index 0000000..d37c041
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/DefaultPropertyMapperTest.java
@@ -0,0 +1,177 @@
+/**
+ * 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.typesystem.types.AttributeInfo;
+import org.apache.atlas.typesystem.types.FieldMapping;
+import org.apache.atlas.typesystem.types.HierarchicalType;
+import org.testng.annotations.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Unit tests for DefaultPropertyMapper.
+ */
+public class DefaultPropertyMapperTest {
+ @Test
+ public void testToCleanName_defaultMappings() {
+ String typeName = "testType";
+ HierarchicalType dataType = createNiceMock(HierarchicalType.class);
+
+ // currently only use key in map
+ Map<String, AttributeInfo> fields = new HashMap<>();
+ fields.put("foo", null);
+ fields.put("prop", null);
+ // can't mock FieldMapping due to direct access to final instance var 'fields'
+ FieldMapping fieldMapping = new FieldMapping(fields, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ // mock expectations
+ expect(dataType.fieldMapping()).andReturn(fieldMapping).anyTimes();
+ replay(dataType);
+
+ PropertyMapper propertyMapper = new TestDefaultPropertyMapper(dataType);
+ assertEquals(propertyMapper.toCleanName("Prefix.prop", typeName), "prop");
+ assertEquals(propertyMapper.toCleanName("foo", typeName), "foo");
+ assertEquals(propertyMapper.toCleanName("other", typeName), "other");
+ assertEquals(propertyMapper.toCleanName("Prefix.other", typeName), "Prefix.other");
+
+ verify(dataType);
+ }
+
+ @Test
+ public void testToQualifiedName_defaultMappings() throws Exception {
+ String typeName = "testType";
+ HierarchicalType dataType = createNiceMock(HierarchicalType.class);
+
+ // currently only use key in map
+ Map<String, AttributeInfo> fields = new HashMap<>();
+ fields.put("foo", null);
+ fields.put("prop", null);
+ // can't mock FieldMapping due to direct access to final instance var 'fields'
+ FieldMapping fieldMapping = new FieldMapping(fields, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ // mock expectations
+ expect(dataType.fieldMapping()).andReturn(fieldMapping).anyTimes();
+ expect(dataType.getQualifiedName("foo")).andReturn("foo");
+ expect(dataType.getQualifiedName("prop")).andReturn("Prefix.prop");
+ replay(dataType);
+
+ PropertyMapper propertyMapper = new TestDefaultPropertyMapper(dataType);
+ assertEquals(propertyMapper.toFullyQualifiedName("foo", typeName), "foo");
+ assertEquals(propertyMapper.toFullyQualifiedName("prop", typeName), "Prefix.prop");
+ assertEquals(propertyMapper.toFullyQualifiedName("other", typeName), "other");
+ assertEquals(propertyMapper.toFullyQualifiedName("Prefix.other", typeName), "Prefix.other");
+
+ verify(dataType);
+ }
+
+ @Test
+ public void testToCleanName_specifiedMappings() {
+ String typeName = "testType";
+ HierarchicalType dataType = createNiceMock(HierarchicalType.class);
+
+ // currently only use key in map
+ Map<String, AttributeInfo> fields = new HashMap<>();
+ fields.put("foo", null);
+ fields.put("prop", null);
+ // can't mock FieldMapping due to direct access to final instance var 'fields'
+ FieldMapping fieldMapping = new FieldMapping(fields, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ // mock expectations
+ expect(dataType.fieldMapping()).andReturn(fieldMapping).anyTimes();
+ replay(dataType);
+
+ Map<String, String> cleanToQualifiedMap = new HashMap<>();
+ cleanToQualifiedMap.put("prop1", "property_1");
+ Map<String, String> qualifiedToCleanMap = new HashMap<>();
+ qualifiedToCleanMap.put("property_1", "prop1");
+
+ PropertyMapper propertyMapper = new TestDefaultPropertyMapper(
+ typeName, qualifiedToCleanMap, cleanToQualifiedMap, dataType);
+
+ assertEquals(propertyMapper.toCleanName("property_1", typeName), "prop1");
+ assertEquals(propertyMapper.toCleanName("Prefix.prop", typeName), "prop");
+ assertEquals(propertyMapper.toCleanName("foo", typeName), "foo");
+ assertEquals(propertyMapper.toCleanName("other", typeName), "other");
+ assertEquals(propertyMapper.toCleanName("Prefix.other", typeName), "Prefix.other");
+
+ verify(dataType);
+ }
+
+ @Test
+ public void testToQualifiedName_specifiedMappings() throws Exception {
+ String typeName = "testType";
+ HierarchicalType dataType = createNiceMock(HierarchicalType.class);
+
+ // currently only use key in map
+ Map<String, AttributeInfo> fields = new HashMap<>();
+ fields.put("foo", null);
+ fields.put("prop", null);
+ // can't mock FieldMapping due to direct access to final instance var 'fields'
+ FieldMapping fieldMapping = new FieldMapping(fields, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ // mock expectations
+ expect(dataType.fieldMapping()).andReturn(fieldMapping).anyTimes();
+ expect(dataType.getQualifiedName("foo")).andReturn("foo");
+ expect(dataType.getQualifiedName("prop")).andReturn("Prefix.prop");
+ replay(dataType);
+
+ Map<String, String> cleanToQualifiedMap = new HashMap<>();
+ cleanToQualifiedMap.put("prop1", "property_1");
+ Map<String, String> qualifiedToCleanMap = new HashMap<>();
+ qualifiedToCleanMap.put("property_1", "prop1");
+
+ PropertyMapper propertyMapper = new TestDefaultPropertyMapper(
+ typeName, qualifiedToCleanMap, cleanToQualifiedMap, dataType);
+
+ assertEquals(propertyMapper.toFullyQualifiedName("prop1", typeName), "property_1");
+ assertEquals(propertyMapper.toFullyQualifiedName("foo", typeName), "foo");
+ assertEquals(propertyMapper.toFullyQualifiedName("prop", typeName), "Prefix.prop");
+ assertEquals(propertyMapper.toFullyQualifiedName("other", typeName), "other");
+ assertEquals(propertyMapper.toFullyQualifiedName("Prefix.other", typeName), "Prefix.other");
+
+ verify(dataType);
+ }
+
+ private static class TestDefaultPropertyMapper extends DefaultPropertyMapper {
+ private HierarchicalType dataType;
+ public TestDefaultPropertyMapper(HierarchicalType dataType) {
+ super();
+ this.dataType = dataType;
+ }
+
+ public TestDefaultPropertyMapper(String type,
+ Map<String, String> qualifiedToCleanMap,
+ Map<String, String> cleanToQualifiedMap,
+ HierarchicalType dataType) {
+
+ super(qualifiedToCleanMap, cleanToQualifiedMap);
+ this.dataType = dataType;
+ }
+
+ @Override
+ protected HierarchicalType createDataType(String type) {
+ return dataType;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/EntityResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/EntityResourceProviderTest.java b/catalog/src/test/java/org/apache/atlas/catalog/EntityResourceProviderTest.java
new file mode 100644
index 0000000..2f29103
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/EntityResourceProviderTest.java
@@ -0,0 +1,215 @@
+/**
+ * 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.exception.ResourceNotFoundException;
+import org.apache.atlas.catalog.query.AtlasQuery;
+import org.apache.atlas.catalog.query.QueryFactory;
+import org.easymock.Capture;
+import org.testng.annotations.Test;
+
+import java.util.*;
+
+import static org.easymock.EasyMock.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Unit Tests for EntityResourceProvider.
+ */
+public class EntityResourceProviderTest {
+ @Test
+ public void testGetResource() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+ Map<String, Object> queryResultRow = new HashMap<>();
+ queryResult.add(queryResultRow);
+ queryResultRow.put("id", "1");
+ queryResultRow.put("creation_time", "04/20/2016");
+
+ // mock expectations
+ expect(queryFactory.createEntityQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ Result result = provider.getResourceById(userRequest);
+
+ assertEquals(1, result.getPropertyMaps().size());
+ assertEquals(queryResultRow, result.getPropertyMaps().iterator().next());
+
+ Request request = requestCapture.getValue();
+ assertNull(request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+ assertEquals(requestProperties, request.getProperties());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test(expectedExceptions = ResourceNotFoundException.class)
+ public void testGetResource_404() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ // empty response should result in a ResourceNotFoundException
+ Collection<Map<String, Object>> emptyResponse = new ArrayList<>();
+
+ // mock expectations
+ expect(queryFactory.createEntityQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(emptyResponse);
+ replay(typeSystem, queryFactory, query);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request request = new InstanceRequest(requestProperties);
+
+ provider.getResourceById(request);
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test
+ public void testGetResources() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+ Map<String, Object> queryResultRow1 = new HashMap<>();
+ queryResult.add(queryResultRow1);
+ queryResultRow1.put("mame", "entity1");
+ queryResultRow1.put("description", "test entity description");
+ queryResultRow1.put("creation_time", "04/20/2016");
+
+ Map<String, Object> queryResultRow2 = new HashMap<>();
+ queryResult.add(queryResultRow2);
+ queryResultRow2.put("mame", "entity2");
+ queryResultRow2.put("description", "test entity description 2");
+ queryResultRow2.put("creation_time", "04/21/2016");
+
+ // mock expectations
+ expect(queryFactory.createEntityQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Request userRequest = new CollectionRequest(Collections.<String, Object>emptyMap(), "name:entity*");
+ Result result = provider.getResources(userRequest);
+
+ assertEquals(2, result.getPropertyMaps().size());
+ assertTrue(result.getPropertyMaps().contains(queryResultRow1));
+ assertTrue(result.getPropertyMaps().contains(queryResultRow2));
+
+ Request request = requestCapture.getValue();
+ assertEquals("name:entity*", request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+ assertEquals(0, request.getProperties().size());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test
+ public void testGetResources_noResults() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ // empty result shouldn't result in exception for collection query
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+
+ // mock expectations
+ expect(queryFactory.createEntityQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Request userRequest = new CollectionRequest(Collections.<String, Object>emptyMap(), "name:entity*");
+ Result result = provider.getResources(userRequest);
+
+ assertEquals(0, result.getPropertyMaps().size());
+
+ Request request = requestCapture.getValue();
+ assertEquals("name:entity*", request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+ assertEquals(0, request.getProperties().size());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test(expectedExceptions = UnsupportedOperationException.class)
+ public void testCreateResource() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+
+ // mock expectations
+ replay(typeSystem, queryFactory, query);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ provider.createResource(userRequest);
+ }
+
+ @Test(expectedExceptions = UnsupportedOperationException.class)
+ public void testCreateResources() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+
+ // mock expectations
+ replay(typeSystem, queryFactory, query);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ EntityResourceProvider provider = new EntityResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ provider.createResources(userRequest);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/aaf2971a/catalog/src/test/java/org/apache/atlas/catalog/EntityTagResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/catalog/src/test/java/org/apache/atlas/catalog/EntityTagResourceProviderTest.java b/catalog/src/test/java/org/apache/atlas/catalog/EntityTagResourceProviderTest.java
new file mode 100644
index 0000000..a95b966
--- /dev/null
+++ b/catalog/src/test/java/org/apache/atlas/catalog/EntityTagResourceProviderTest.java
@@ -0,0 +1,444 @@
+/**
+ * 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.exception.CatalogException;
+import org.apache.atlas.catalog.exception.InvalidPayloadException;
+import org.apache.atlas.catalog.exception.ResourceAlreadyExistsException;
+import org.apache.atlas.catalog.exception.ResourceNotFoundException;
+import org.apache.atlas.catalog.query.AtlasQuery;
+import org.apache.atlas.catalog.query.QueryFactory;
+import org.easymock.Capture;
+import org.testng.annotations.Test;
+
+import java.util.*;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.replay;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Unit tests for EntityTagResourceProvider.
+ */
+public class EntityTagResourceProviderTest {
+ @Test
+ public void testGetResource() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+ Map<String, Object> queryResultRow = new HashMap<>();
+ queryResult.add(queryResultRow);
+ queryResultRow.put("name", "taxonomyName.termName");
+ queryResultRow.put("description", "test term description");
+
+ // mock expectations
+ expect(queryFactory.createEntityTagQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityTagResourceProvider provider = new EntityTagResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("name", "taxonomyName.termName");
+ requestProperties.put("id", "1");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ Result result = provider.getResourceById(userRequest);
+
+ assertEquals(1, result.getPropertyMaps().size());
+ assertEquals(queryResultRow, result.getPropertyMaps().iterator().next());
+
+ Request request = requestCapture.getValue();
+ assertNull(request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+ assertEquals(2, request.getProperties().size());
+ assertEquals("taxonomyName.termName", request.getProperties().get("name"));
+ assertEquals(Request.Cardinality.INSTANCE, request.getCardinality());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test(expectedExceptions = ResourceNotFoundException.class)
+ public void testGetResource_404() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ // empty response should result in a ResourceNotFoundException
+ Collection<Map<String, Object>> emptyResponse = new ArrayList<>();
+
+ // mock expectations
+ expect(queryFactory.createEntityTagQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(emptyResponse);
+ replay(typeSystem, queryFactory, query);
+
+ EntityTagResourceProvider provider = new EntityTagResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("name", "taxonomyName.termName");
+ requestProperties.put("id", "1");
+ Request request = new InstanceRequest(requestProperties);
+
+ provider.getResourceById(request);
+ }
+
+ @Test
+ public void testGetResources() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+ Map<String, Object> queryResultRow1 = new HashMap<>();
+ queryResult.add(queryResultRow1);
+ queryResultRow1.put("name", "testTaxonomy.termName");
+ queryResultRow1.put("description", "test term description");
+
+ Map<String, Object> queryResultRow2 = new HashMap<>();
+ queryResult.add(queryResultRow2);
+ queryResultRow2.put("name", "testTaxonomy.termName2");
+ queryResultRow2.put("description", "test term 2 description");
+ // mock expectations
+ expect(queryFactory.createEntityTagQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityTagResourceProvider provider = new EntityTagResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request userRequest = new CollectionRequest(requestProperties, "name:testTaxonomy.*");
+ // invoke test method
+ Result result = provider.getResources(userRequest);
+
+ assertEquals(2, result.getPropertyMaps().size());
+ assertTrue(result.getPropertyMaps().contains(queryResultRow1));
+ assertTrue(result.getPropertyMaps().contains(queryResultRow2));
+
+ Request request = requestCapture.getValue();
+ assertEquals("name:testTaxonomy.*", request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test
+ public void testGetResources_noResults() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ Capture<Request> requestCapture = newCapture();
+
+ Collection<Map<String, Object>> queryResult = new ArrayList<>();
+
+ // mock expectations
+ expect(queryFactory.createEntityTagQuery(capture(requestCapture))).andReturn(query);
+ expect(query.execute()).andReturn(queryResult);
+ replay(typeSystem, queryFactory, query);
+
+ EntityTagResourceProvider provider = new EntityTagResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("id", "1");
+ Request userRequest = new CollectionRequest(requestProperties, "name:testTaxonomy.*");
+ // invoke test method
+ Result result = provider.getResources(userRequest);
+
+ assertEquals(0, result.getPropertyMaps().size());
+
+ Request request = requestCapture.getValue();
+ assertEquals("name:testTaxonomy.*", request.getQueryString());
+ assertEquals(0, request.getAdditionalSelectProperties().size());
+
+ verify(typeSystem, queryFactory, query);
+ }
+
+ @Test(expectedExceptions = InvalidPayloadException.class)
+ public void testCreateResource_invalidRequest__noName() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+
+ replay(typeSystem, queryFactory, query);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ // missing name name should result in InvalidPayloadException
+ requestProperties.put("description", "description");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ EntityTagResourceProvider provider = new EntityTagResourceProvider(typeSystem);
+ provider.setQueryFactory(queryFactory);
+
+ provider.createResource(userRequest);
+ }
+
+ @Test
+ public void testCreateResource() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ ResourceProvider termResourceProvider = createStrictMock(TermResourceProvider.class);
+ Capture<Request> termRequestCapture = newCapture();
+
+ Collection<Map<String, Object>> termQueryResult = new ArrayList<>();
+ Map<String, Object> termQueryResultRow = new HashMap<>();
+ termQueryResult.add(termQueryResultRow);
+ termQueryResultRow.put("name", "testTaxonomy.termName");
+ termQueryResultRow.put("type", "testTaxonomy.termName");
+ termQueryResultRow.put("available_as_tag", true);
+ termQueryResultRow.put("description", "term description");
+ Result termResult = new Result(termQueryResult);
+
+ // mock expectations
+ expect(termResourceProvider.getResourceById(capture(termRequestCapture))).andReturn(termResult);
+ Map<String, Object> tagProperties = new HashMap<>();
+ tagProperties.put("name", "testTaxonomy.termName");
+ tagProperties.put("description", "term description");
+ typeSystem.createTraitInstance("11-22-33", "testTaxonomy.termName", tagProperties);
+ replay(typeSystem, queryFactory, query, termResourceProvider);
+
+ EntityTagResourceProvider provider = new TestEntityTagResourceProvider(typeSystem, termResourceProvider);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("name", "testTaxonomy.termName");
+ requestProperties.put("id", "11-22-33");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ provider.createResource(userRequest);
+
+ Request termRequest = termRequestCapture.getValue();
+ Map<String, Object> termRequestProps = termRequest.getProperties();
+ assertEquals(1, termRequestProps.size());
+ TermPath termPath = (TermPath) termRequestProps.get("termPath");
+ assertEquals("testTaxonomy.termName", termPath.getFullyQualifiedName());
+ assertEquals(1, termRequest.getAdditionalSelectProperties().size());
+ assertEquals("type", termRequest.getAdditionalSelectProperties().iterator().next());
+ assertNull(termRequest.getQueryString());
+
+ verify(typeSystem, queryFactory, query, termResourceProvider);
+ }
+
+ @Test(expectedExceptions = CatalogException.class)
+ public void testCreateResource_invalidRequest__termNotAvailableForTagging() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ ResourceProvider termResourceProvider = createStrictMock(TermResourceProvider.class);
+ Capture<Request> termRequestCapture = newCapture();
+
+ Collection<Map<String, Object>> termQueryResult = new ArrayList<>();
+ Map<String, Object> termQueryResultRow = new HashMap<>();
+ termQueryResult.add(termQueryResultRow);
+ termQueryResultRow.put("name", "testTaxonomy.termName");
+ termQueryResultRow.put("type", "testTaxonomy.termName");
+ // false value for 'available_as_tag' should result in an exception
+ termQueryResultRow.put("available_as_tag", false);
+ termQueryResultRow.put("description", "term description");
+ Result termResult = new Result(termQueryResult);
+
+ // mock expectations
+ expect(termResourceProvider.getResourceById(capture(termRequestCapture))).andReturn(termResult);
+ replay(typeSystem, queryFactory, query, termResourceProvider);
+
+ EntityTagResourceProvider provider = new TestEntityTagResourceProvider(typeSystem, termResourceProvider);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("name", "testTaxonomy.termName");
+ requestProperties.put("id", "11-22-33");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ provider.createResource(userRequest);
+ }
+
+ @Test(expectedExceptions = ResourceAlreadyExistsException.class)
+ public void testCreateResource_invalidRequest__alreadyExists() throws Exception {
+ AtlasTypeSystem typeSystem = createStrictMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery query = createStrictMock(AtlasQuery.class);
+ ResourceProvider termResourceProvider = createStrictMock(TermResourceProvider.class);
+ Capture<Request> termRequestCapture = newCapture();
+
+ Collection<Map<String, Object>> termQueryResult = new ArrayList<>();
+ Map<String, Object> termQueryResultRow = new HashMap<>();
+ termQueryResult.add(termQueryResultRow);
+ termQueryResultRow.put("name", "testTaxonomy.termName");
+ termQueryResultRow.put("type", "testTaxonomy.termName");
+ termQueryResultRow.put("available_as_tag", true);
+ termQueryResultRow.put("description", "term description");
+ Result termResult = new Result(termQueryResult);
+
+ // mock expectations
+ expect(termResourceProvider.getResourceById(capture(termRequestCapture))).andReturn(termResult);
+ Map<String, Object> tagProperties = new HashMap<>();
+ tagProperties.put("name", "testTaxonomy.termName");
+ tagProperties.put("description", "term description");
+ typeSystem.createTraitInstance("11-22-33", "testTaxonomy.termName", tagProperties);
+ expectLastCall().andThrow(new ResourceAlreadyExistsException(""));
+ replay(typeSystem, queryFactory, query, termResourceProvider);
+
+ EntityTagResourceProvider provider = new TestEntityTagResourceProvider(typeSystem, termResourceProvider);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProperties = new HashMap<>();
+ requestProperties.put("name", "testTaxonomy.termName");
+ requestProperties.put("id", "11-22-33");
+ Request userRequest = new InstanceRequest(requestProperties);
+
+ provider.createResource(userRequest);
+ }
+
+ @Test
+ public void testCreateResources() throws Exception {
+ AtlasTypeSystem typeSystem = createMock(AtlasTypeSystem.class);
+ QueryFactory queryFactory = createStrictMock(QueryFactory.class);
+ AtlasQuery entityQuery = createMock(AtlasQuery.class);
+ ResourceProvider termResourceProvider = createMock(TermResourceProvider.class);
+ Capture<Request> entityRequestCapture = newCapture();
+ Capture<Request> termRequestCapture1 = newCapture();
+ Capture<Request> termRequestCapture2 = newCapture();
+
+ Collection<Map<String, Object>> entityQueryResult = new ArrayList<>();
+ Map<String, Object> entityQueryResultRow = new HashMap<>();
+ entityQueryResultRow.put("id", "1");
+ entityQueryResult.add(entityQueryResultRow);
+
+ Map<String, Object> entityQueryResultRow2 = new HashMap<>();
+ entityQueryResultRow2.put("id", "2");
+ entityQueryResult.add(entityQueryResultRow2);
+
+ Collection<Map<String, Object>> termQueryResult1 = new ArrayList<>();
+ Map<String, Object> termQueryResultRow1 = new HashMap<>();
+ termQueryResult1.add(termQueryResultRow1);
+ termQueryResultRow1.put("name", "testTaxonomy.termName1");
+ termQueryResultRow1.put("type", "testTaxonomy.termName1");
+ termQueryResultRow1.put("available_as_tag", true);
+ termQueryResultRow1.put("description", "term description");
+ Result termResult1 = new Result(termQueryResult1);
+
+ Collection<Map<String, Object>> termQueryResult2 = new ArrayList<>();
+ Map<String, Object> termQueryResultRow2 = new HashMap<>();
+ termQueryResult2.add(termQueryResultRow2);
+ termQueryResultRow2.put("name", "testTaxonomy.termName2");
+ termQueryResultRow2.put("type", "testTaxonomy.termName2");
+ termQueryResultRow2.put("available_as_tag", true);
+ termQueryResultRow2.put("description", "term 2 description");
+ Result termResult2 = new Result(termQueryResult2);
+
+ // mock expectations
+ expect(queryFactory.createEntityQuery(capture(entityRequestCapture))).andReturn(entityQuery);
+ expect(entityQuery.execute()).andReturn(entityQueryResult);
+
+ expect(termResourceProvider.getResourceById(capture(termRequestCapture1))).andReturn(termResult1);
+ expect(termResourceProvider.getResourceById(capture(termRequestCapture2))).andReturn(termResult2);
+
+ Map<String, Object> tagProperties1 = new HashMap<>();
+ tagProperties1.put("name", "testTaxonomy.termName1");
+ tagProperties1.put("description", "term description");
+ // each tag is associated with each entity
+ typeSystem.createTraitInstance("1", "testTaxonomy.termName1", tagProperties1);
+ typeSystem.createTraitInstance("2", "testTaxonomy.termName1", tagProperties1);
+
+ Map<String, Object> tagProperties2 = new HashMap<>();
+ tagProperties2.put("name", "testTaxonomy.termName2");
+ tagProperties2.put("description", "term 2 description");
+ // each tag is associated with each entity
+ typeSystem.createTraitInstance("1", "testTaxonomy.termName2", tagProperties2);
+ typeSystem.createTraitInstance("2", "testTaxonomy.termName2", tagProperties2);
+
+ replay(typeSystem, queryFactory, entityQuery, termResourceProvider);
+ // end mock expectations
+
+ EntityTagResourceProvider provider = new TestEntityTagResourceProvider(typeSystem, termResourceProvider);
+ provider.setQueryFactory(queryFactory);
+
+ Map<String, Object> requestProps = new HashMap<>();
+ Collection<Map<String, String>> tagMaps = new ArrayList<>();
+ requestProps.put("tags", tagMaps);
+ Map<String, String> tagMap1 = new HashMap<>();
+ tagMap1.put("name", "testTaxonomy.termName1");
+ tagMaps.add(tagMap1);
+ Map<String, String> tagMap2 = new HashMap<>();
+ tagMap2.put("name", "testTaxonomy.termName2");
+ tagMaps.add(tagMap2);
+
+ Request userRequest = new CollectionRequest(requestProps, "name:foo*");
+ // invoke method being tested
+ Collection<String> createResult = provider.createResources(userRequest);
+
+ assertEquals(4, createResult.size());
+ assertTrue(createResult.contains("v1/entities/1/tags/testTaxonomy.termName1"));
+ assertTrue(createResult.contains("v1/entities/1/tags/testTaxonomy.termName2"));
+ assertTrue(createResult.contains("v1/entities/2/tags/testTaxonomy.termName1"));
+ assertTrue(createResult.contains("v1/entities/2/tags/testTaxonomy.termName2"));
+
+ Request entityRequest = entityRequestCapture.getValue();
+ assertEquals("name:foo*", entityRequest.getQueryString());
+ assertEquals(Request.Cardinality.COLLECTION, entityRequest.getCardinality());
+
+ Request termRequest1 = termRequestCapture1.getValue();
+ assertNull(termRequest1.getQueryString());
+ assertEquals(Request.Cardinality.INSTANCE, termRequest1.getCardinality());
+ Map<String, Object> termRequestProps = termRequest1.getProperties();
+ assertEquals(1, termRequestProps.size());
+ TermPath termPath = (TermPath) termRequestProps.get("termPath");
+ assertEquals("testTaxonomy.termName1", termPath.getFullyQualifiedName());
+
+ Request termRequest2 = termRequestCapture2.getValue();
+ assertNull(termRequest2.getQueryString());
+ assertEquals(Request.Cardinality.INSTANCE, termRequest2.getCardinality());
+ Map<String, Object> termRequestProps2 = termRequest2.getProperties();
+ assertEquals(1, termRequestProps2.size());
+ TermPath termPath2 = (TermPath) termRequestProps2.get("termPath");
+ assertEquals("testTaxonomy.termName2", termPath2.getFullyQualifiedName());
+
+ verify(typeSystem, queryFactory, entityQuery, termResourceProvider);
+ }
+
+ //todo: test behavior of createResources in case of partial success after behavior is defined
+
+
+ private static class TestEntityTagResourceProvider extends EntityTagResourceProvider {
+
+ private ResourceProvider testTermResourceProvider;
+
+ public TestEntityTagResourceProvider(AtlasTypeSystem typeSystem, ResourceProvider termResourceProvider) {
+ super(typeSystem);
+ testTermResourceProvider = termResourceProvider;
+ }
+
+ @Override
+ protected synchronized ResourceProvider getTermResourceProvider() {
+ return testTermResourceProvider;
+ }
+ }
+}