diff options
Diffstat (limited to 'src/main/java/org/onap/aai/rest')
8 files changed, 1162 insertions, 0 deletions
diff --git a/src/main/java/org/onap/aai/rest/ExceptionHandler.java b/src/main/java/org/onap/aai/rest/ExceptionHandler.java new file mode 100644 index 0000000..14c45da --- /dev/null +++ b/src/main/java/org/onap/aai/rest/ExceptionHandler.java @@ -0,0 +1,127 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.sun.istack.SAXParseException2; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import java.util.ArrayList; +import java.util.List; + +/** + * The Class ExceptionHandler. + */ +@Provider +public class ExceptionHandler implements ExceptionMapper<Exception> { + + @Context + private HttpServletRequest request; + + @Context + private HttpHeaders headers; + + /** + * @{inheritDoc} + */ + @Override + public Response toResponse(Exception exception) { + + Response response = null; + ArrayList<String> templateVars = new ArrayList<String>(); + + //the general case is that cxf will give us a WebApplicationException + //with a linked exception + if (exception instanceof WebApplicationException) { + WebApplicationException e = (WebApplicationException) exception; + if (e.getCause() != null) { + if (e.getCause() instanceof SAXParseException2) { + templateVars.add("UnmarshalException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + } + } else if (exception instanceof JsonParseException) { + //jackson does it differently so we get the direct JsonParseException + templateVars.add("JsonParseException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } else if (exception instanceof JsonMappingException) { + //jackson does it differently so we get the direct JsonParseException + templateVars.add("JsonMappingException"); + AAIException ex = new AAIException("AAI_4007", exception); + response = Response + .status(400) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + + // it didn't get set above, we wrap a general fault here + if (response == null) { + + Exception actual_e = exception; + if (exception instanceof WebApplicationException) { + WebApplicationException e = (WebApplicationException) exception; + response = e.getResponse(); + } else { + templateVars.add(request.getMethod()); + templateVars.add("unknown"); + AAIException ex = new AAIException("AAI_4000", actual_e); + List<MediaType> mediaTypes = headers.getAcceptableMediaTypes(); + int setError = 0; + + for (MediaType mediaType : mediaTypes) { + if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) { + response = Response + .status(400) + .type(MediaType.APPLICATION_XML_TYPE) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + setError = 1; + } + } + if (setError == 0) { + response = Response + .status(400) + .type(MediaType.APPLICATION_JSON_TYPE) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + } + } + } + return response; + } +} diff --git a/src/main/java/org/onap/aai/rest/QueryConsumer.java b/src/main/java/org/onap/aai/rest/QueryConsumer.java new file mode 100644 index 0000000..85665da --- /dev/null +++ b/src/main/java/org/onap/aai/rest/QueryConsumer.java @@ -0,0 +1,217 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.onap.aai.concurrent.AaiCallable; +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.rest.dsl.DslQueryProcessor; +import org.onap.aai.logging.LoggingContext; +import org.onap.aai.logging.StopWatch; +import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.search.GenericQueryProcessor; +import org.onap.aai.rest.search.QueryProcessorType; +import org.onap.aai.restcore.HttpMethod; +import org.onap.aai.restcore.RESTAPI; +import org.onap.aai.serialization.db.DBSerializer; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.queryformats.Format; +import org.onap.aai.serialization.queryformats.FormatFactory; +import org.onap.aai.serialization.queryformats.Formatter; +import org.onap.aai.serialization.queryformats.SubGraphStyle; +import org.onap.aai.setup.SchemaVersion; +import org.onap.aai.setup.SchemaVersions; +import org.onap.aai.util.AAIConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.*; +import javax.ws.rs.core.*; +import javax.ws.rs.core.Response.Status; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +@Path("{version: v[1-9][0-9]*|latest}/dbquery") +public class QueryConsumer extends RESTAPI { + + /** The introspector factory type. */ + private ModelType introspectorFactoryType = ModelType.MOXY; + + private QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY; + + private static final String TARGET_ENTITY = "DB"; + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(QueryConsumer.class); + + private HttpEntry traversalUriHttpEntry; + + private DslQueryProcessor dslQueryProcessor; + + private SchemaVersions schemaVersions; + + private String basePath; + + @Autowired + public QueryConsumer( + HttpEntry traversalUriHttpEntry, + DslQueryProcessor dslQueryProcessor, + SchemaVersions schemaVersions, + @Value("${schema.uri.base.path}") String basePath + ){ + this.traversalUriHttpEntry = traversalUriHttpEntry; + this.dslQueryProcessor = dslQueryProcessor; + this.basePath = basePath; + this.schemaVersions = schemaVersions; + } + + + @PUT + @Consumes({ MediaType.APPLICATION_JSON}) + @Produces({ MediaType.APPLICATION_JSON}) + public Response executeQuery(String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("graphson") @QueryParam("format") String queryFormat,@DefaultValue("no_op") @QueryParam("subgraph") String subgraph, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req){ + return runner(AAIConstants.AAI_GRAPHADMIN_TIMEOUT_ENABLED, + AAIConstants.AAI_GRAPHADMIN_TIMEOUT_APP, + AAIConstants.AAI_GRAPHADMIN_TIMEOUT_LIMIT, + headers, + info, + HttpMethod.GET, + new AaiCallable<Response>() { + @Override + public Response process() { + return processExecuteQuery(content, versionParam, uri, queryFormat, subgraph, headers, info, req); + } + } + ); + } + + public Response processExecuteQuery(String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("graphson") @QueryParam("format") String queryFormat,@DefaultValue("no_op") @QueryParam("subgraph") String subgraph, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + String methodName = "executeQuery"; + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor"); + QueryProcessorType processorType = this.processorType; + Response response = null; + TransactionalGraphEngine dbEngine = null; + try { + LoggingContext.save(); + this.checkQueryParams(info.getQueryParameters()); + Format format = Format.getFormat(queryFormat); + if (queryProcessor != null) { + processorType = QueryProcessorType.valueOf(queryProcessor); + } + SubGraphStyle subGraphStyle = SubGraphStyle.valueOf(subgraph); + JsonParser parser = new JsonParser(); + + JsonObject input = parser.parse(content).getAsJsonObject(); + + JsonElement gremlinElement = input.get("gremlin"); + JsonElement dslElement = input.get("dsl"); + String queryURI = ""; + String gremlin = ""; + String dsl = ""; + + SchemaVersion version = new SchemaVersion(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + traversalUriHttpEntry.setHttpEntryProperties(version, type); + dbEngine = traversalUriHttpEntry.getDbEngine(); + + if (gremlinElement != null) { + gremlin = gremlinElement.getAsString(); + } + if (dslElement != null) { + dsl = dslElement.getAsString(); + } + GenericQueryProcessor processor = null; + + LoggingContext.targetEntity(TARGET_ENTITY); + LoggingContext.targetServiceName(methodName); + LoggingContext.startTime(); + StopWatch.conditionalStart(); + + if(!dsl.equals("")){ + processor = new GenericQueryProcessor.Builder(dbEngine) + .queryFrom(dsl, "dsl") + .queryProcessor(dslQueryProcessor) + .processWith(processorType).create(); + }else { + processor = new GenericQueryProcessor.Builder(dbEngine) + .queryFrom(gremlin, "gremlin") + .processWith(processorType).create(); + } + + String result = ""; + List<Object> vertices = processor.execute(subGraphStyle); + + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + FormatFactory ff = new FormatFactory(traversalUriHttpEntry.getLoader(), serializer, schemaVersions, basePath); + + Formatter formater = ff.get(format, info.getQueryParameters()); + + result = formater.output(vertices).toString(); + + double msecs = StopWatch.stopIfStarted(); + LoggingContext.elapsedTime((long)msecs,TimeUnit.MILLISECONDS); + LoggingContext.successStatusFields(); + LOGGER.info ("Completed"); + + response = Response.status(Status.OK) + .type(MediaType.APPLICATION_JSON) + .entity(result).build(); + + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, e); + } catch (Exception e ) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, ex); + } finally { + LoggingContext.restoreIfPossible(); + LoggingContext.successStatusFields(); + if (dbEngine != null) { + dbEngine.rollback(); + } + + } + + return response; + } + + public void checkQueryParams(MultivaluedMap<String, String> params) throws AAIException { + + if (params.containsKey("depth") && params.getFirst("depth").matches("\\d+")) { + String depth = params.getFirst("depth"); + Integer i = Integer.parseInt(depth); + if (i > 1) { + throw new AAIException("AAI_3303"); + } + } + + + } + +} diff --git a/src/main/java/org/onap/aai/rest/dsl/DslListener.java b/src/main/java/org/onap/aai/rest/dsl/DslListener.java new file mode 100644 index 0000000..e41a946 --- /dev/null +++ b/src/main/java/org/onap/aai/rest/dsl/DslListener.java @@ -0,0 +1,314 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.dsl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.onap.aai.AAIDslBaseListener; +import org.onap.aai.AAIDslParser; +import org.onap.aai.edges.EdgeIngestor; +import org.onap.aai.edges.EdgeRule; +import org.onap.aai.edges.EdgeRuleQuery; +import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException; +import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The Class DslListener. + */ +public class DslListener extends AAIDslBaseListener { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DslQueryProcessor.class); + private final EdgeIngestor edgeRules; + + //TODO Use StringBuilder to build the query than concat + String query = ""; + + Map<Integer, String> unionMap = new HashMap<>(); + Map<String, String> flags = new HashMap<>(); + + String currentNode = ""; + String prevsNode = ""; + int commas = 0; + + int unionKey = 0; + int unionMembers = 0; + boolean isUnionBeg = false; + boolean isUnionTraversal = false; + + boolean isTraversal = false; + boolean isWhereTraversal = false; + String whereTraversalNode = ""; + + String limitQuery = ""; + boolean isNot = false; + + /** + * Instantiates a new DslListener. + */ + @Autowired + public DslListener(EdgeIngestor edgeIngestor) { + this.edgeRules = edgeIngestor; + } + + @Override + public void enterAaiquery(AAIDslParser.AaiqueryContext ctx) { + query += "builder"; + } + + @Override + public void enterDslStatement(AAIDslParser.DslStatementContext ctx) { + // LOGGER.info("Statement Enter"+ctx.getText()); + /* + * This block of code is entered for every query statement + */ + if (isUnionBeg) { + isUnionBeg = false; + isUnionTraversal = true; + + } else if (unionMembers > 0) { + unionMembers--; + query += ",builder.newInstance()"; + isUnionTraversal = true; + } + + } + + @Override + public void exitDslStatement(AAIDslParser.DslStatementContext ctx) { + /* + * Nothing to be done here for now + * LOGGER.info("Statement Exit"+ctx.getText()); + */ + } + + @Override + public void exitAaiquery(AAIDslParser.AaiqueryContext ctx) { + /* + * dedup is by default for all queries If the query has limit in it + * include this as well LOGGER.info("Statement Exit"+ctx.getText()); + */ + + query += ".cap('x').unfold().dedup()" + limitQuery; + } + + /* + * TODO: The contexts are not inherited from a single parent in AAIDslParser + * Need to find a way to do that + */ + @Override + public void enterSingleNodeStep(AAIDslParser.SingleNodeStepContext ctx) { + + prevsNode = currentNode; + currentNode = ctx.NODE().getText(); + + this.generateQuery(); + if (ctx.STORE() != null && ctx.STORE().getText().equals("*")) { + flags.put(currentNode, "store"); + } + + } + + @Override + public void enterSingleQueryStep(AAIDslParser.SingleQueryStepContext ctx) { + + prevsNode = currentNode; + currentNode = ctx.NODE().getText(); + this.generateQuery(); + + if (ctx.STORE() != null && ctx.STORE().getText().equals("*")) { + flags.put(currentNode, "store"); + } + } + + @Override + public void enterMultiQueryStep(AAIDslParser.MultiQueryStepContext ctx) { + + prevsNode = currentNode; + currentNode = ctx.NODE().getText(); + this.generateQuery(); + + if (ctx.STORE() != null && ctx.STORE().getText().equals("*")) { + flags.put(currentNode, "store"); + } + + } + + /* + * Generates the QueryBuilder syntax for the dsl query + */ + private void generateQuery() { + String edgeType = ""; + + if (isUnionTraversal || isTraversal || isWhereTraversal) { + String previousNode = prevsNode; + if (isUnionTraversal) { + previousNode = unionMap.get(unionKey); + isUnionTraversal = false; + } + + EdgeRuleQuery edgeRuleQuery = new EdgeRuleQuery.Builder(previousNode, currentNode).build(); + EdgeRule edgeRule = null; + + try { + edgeRule = edgeRules.getRule(edgeRuleQuery); + } catch (EdgeRuleNotFoundException | AmbiguousRuleChoiceException e) { + } + + if (edgeRule == null) { + edgeType = "EdgeType.COUSIN"; + } else if ("none".equalsIgnoreCase(edgeRule.getContains())){ + edgeType = "EdgeType.COUSIN"; + }else { + edgeType = "EdgeType.TREE"; + } + + query += ".createEdgeTraversal(" + edgeType + ", '" + previousNode + "','" + currentNode + "')"; + + } + + else + query += ".getVerticesByProperty('aai-node-type', '" + currentNode + "')"; + } + + @Override + public void exitSingleNodeStep(AAIDslParser.SingleNodeStepContext ctx) { + + generateExitStep(); + } + + @Override + public void exitSingleQueryStep(AAIDslParser.SingleQueryStepContext ctx) { + generateExitStep(); + } + + @Override + public void exitMultiQueryStep(AAIDslParser.MultiQueryStepContext ctx) { + generateExitStep(); + + } + + private void generateExitStep() { + if (flags.containsKey(currentNode)) { + String storeFlag = flags.get(currentNode); + if (storeFlag != null && storeFlag.equals("store")) + query += ".store('x')"; + flags.remove(currentNode); + } + } + + @Override + public void enterUnionQueryStep(AAIDslParser.UnionQueryStepContext ctx) { + isUnionBeg = true; + + unionKey++; + unionMap.put(unionKey, currentNode); + query += ".union(builder.newInstance()"; + + List<TerminalNode> commaNodes = ctx.COMMA(); + + for (TerminalNode node : commaNodes) { + unionMembers++; + } + } + + @Override + public void exitUnionQueryStep(AAIDslParser.UnionQueryStepContext ctx) { + isUnionBeg = false; + unionMap.remove(unionKey); + + query += ")"; + unionKey--; + + } + + @Override + public void enterFilterTraverseStep(AAIDslParser.FilterTraverseStepContext ctx) { + isWhereTraversal = true; + whereTraversalNode = currentNode; + query += ".where(builder.newInstance()"; + } + + @Override + public void exitFilterTraverseStep(AAIDslParser.FilterTraverseStepContext ctx) { + query += ")"; + isWhereTraversal = false; + currentNode = whereTraversalNode; + } + + @Override + public void enterFilterStep(AAIDslParser.FilterStepContext ctx) { + if (ctx.NOT() != null && ctx.NOT().getText().equals("!")) + isNot = true; + + List<TerminalNode> nodes = ctx.KEY(); + String key = ctx.KEY(0).getText(); + + if (isNot) { + query += ".getVerticesExcludeByProperty("; + isNot = false; + } else + query += ".getVerticesByProperty("; + + if (nodes.size() == 2) { + query += key + "," + ctx.KEY(1).getText(); + query += ")"; + } + + if (nodes.size() > 2) { + + for (TerminalNode node : nodes) { + if (node.getText().equals(key)) + continue; + + query += key + "," + node.getText(); + query += ")"; + } + + } + + } + + @Override + public void exitFilterStep(AAIDslParser.FilterStepContext ctx) { + // For now do nothing + } + + @Override + public void enterTraverseStep(AAIDslParser.TraverseStepContext ctx) { + isTraversal = true; + } + + @Override + public void exitTraverseStep(AAIDslParser.TraverseStepContext ctx) { + isTraversal = false; + } + + @Override + public void enterLimitStep(AAIDslParser.LimitStepContext ctx) { + String value = ctx.NODE().getText(); + limitQuery += ".limit(" + value + ")"; + } +} diff --git a/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java b/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java new file mode 100644 index 0000000..582f0ea --- /dev/null +++ b/src/main/java/org/onap/aai/rest/dsl/DslQueryProcessor.java @@ -0,0 +1,85 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.dsl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.onap.aai.AAIDslLexer; +import org.onap.aai.AAIDslParser; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/** + * The Class DslQueryProcessor. + */ +public class DslQueryProcessor { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DslQueryProcessor.class); + + private DslListener dslListener; + + @Autowired + public DslQueryProcessor(DslListener dslListener){ + this.dslListener = dslListener; + } + + public String parseAaiQuery(String aaiQuery) { + try { + // Create a input stream that reads our string + InputStream stream = new ByteArrayInputStream(aaiQuery.getBytes(StandardCharsets.UTF_8)); + + // Create a lexer from the input CharStream + AAIDslLexer lexer = new AAIDslLexer(CharStreams.fromStream(stream, StandardCharsets.UTF_8)); + + // Get a list of tokens pulled from the lexer + CommonTokenStream tokens = new CommonTokenStream(lexer); + + + // Parser that feeds off of the tokens buffer + AAIDslParser parser = new AAIDslParser(tokens); + + // Specify our entry point + ParseTree ptree = parser.aaiquery(); + LOGGER.info("QUERY-interim" + ptree.toStringTree(parser)); + + // Walk it and attach our listener + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(dslListener, ptree); + LOGGER.info("Final QUERY" + dslListener.query); + + /* + * TODO - Visitor patternQueryDslVisitor visitor = new + * QueryDslVisitor(); String query = visitor.visit(ptree); + * + */ + return dslListener.query; + } catch (Exception e) { + LOGGER.error("Error while processing the query"+e.getMessage()); + } + return ""; + } +} diff --git a/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java b/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java new file mode 100644 index 0000000..2431d11 --- /dev/null +++ b/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java @@ -0,0 +1,226 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.search; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.javatuples.Pair; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.rest.dsl.DslQueryProcessor; +import org.onap.aai.restcore.search.GroovyQueryBuilderSingleton; +import org.onap.aai.restcore.util.URITools; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.queryformats.SubGraphStyle; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import java.io.FileNotFoundException; +import java.net.URI; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class GenericQueryProcessor { + + protected final Optional<URI> uri; + protected final MultivaluedMap<String, String> queryParams; + protected final Optional<Collection<Vertex>> vertices; + protected static Pattern p = Pattern.compile("query/(.*+)"); + protected Optional<String> gremlin; + protected final TransactionalGraphEngine dbEngine; + protected static GroovyQueryBuilderSingleton queryBuilderSingleton = GroovyQueryBuilderSingleton.getInstance(); + protected final boolean isGremlin; + protected Optional<DslQueryProcessor> dslQueryProcessorOptional; + /* dsl parameters to store dsl query and to check + * if this is a DSL request + */ + protected Optional<String> dsl; + protected final boolean isDsl ; + + protected GenericQueryProcessor(Builder builder) { + this.uri = builder.getUri(); + this.dbEngine = builder.getDbEngine(); + this.vertices = builder.getVertices(); + this.gremlin = builder.getGremlin(); + this.isGremlin = builder.isGremlin(); + this.dsl = builder.getDsl(); + this.isDsl = builder.isDsl(); + this.dslQueryProcessorOptional = builder.getDslQueryProcessor(); + + if (uri.isPresent()) { + queryParams = URITools.getQueryMap(uri.get()); + } else { + queryParams = new MultivaluedHashMap<>(); + } + } + + protected abstract GraphTraversal<?,?> runQuery(String query, Map<String, Object> params); + + protected List<Object> processSubGraph(SubGraphStyle style, GraphTraversal<?,?> g) { + final List<Object> resultVertices = new Vector<>(); + g.store("y"); + + if (SubGraphStyle.prune.equals(style) || SubGraphStyle.star.equals(style)) { + g.barrier().bothE(); + if (SubGraphStyle.prune.equals(style)) { + g.where(__.otherV().where(P.within("y"))); + } + g.dedup().subgraph("subGraph").cap("subGraph").map(x -> (Graph)x.get()).next().traversal().V().forEachRemaining(x -> { + resultVertices.add(x); + }); + } else { + resultVertices.addAll(g.toList()); + } + return resultVertices; + } + + public List<Object> execute(SubGraphStyle style) throws FileNotFoundException, AAIException { + final List<Object> resultVertices; + + Pair<String, Map<String, Object>> tuple = this.createQuery(); + String query = tuple.getValue0(); + Map<String, Object> params = tuple.getValue1(); + + if (query.equals("") && (vertices.isPresent() && vertices.get().isEmpty())) { + //nothing to do, just exit + return new ArrayList<>(); + } + GraphTraversal<?,?> g = this.runQuery(query, params); + + resultVertices = this.processSubGraph(style, g); + + return resultVertices; + } + + protected Pair<String, Map<String, Object>> createQuery() throws AAIException { + Map<String, Object> params = new HashMap<>(); + String query = ""; + if (this.isGremlin) { + query = gremlin.get(); + + }else if (this.isDsl) { + String dslUserQuery = dsl.get(); + if(dslQueryProcessorOptional.isPresent()){ + String dslQuery = dslQueryProcessorOptional.get().parseAaiQuery(dslUserQuery); + query = queryBuilderSingleton.executeTraversal(dbEngine, dslQuery, params); + String startPrefix = "g.V()"; + query = startPrefix + query; + } + } + + return new Pair<>(query, params); + } + + public static class Builder { + + private final TransactionalGraphEngine dbEngine; + private Optional<URI> uri = Optional.empty(); + private Optional<String> gremlin = Optional.empty(); + private boolean isGremlin = false; + private Optional<Collection<Vertex>> vertices = Optional.empty(); + private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER; + + private Optional<String> dsl = Optional.empty(); + private boolean isDsl = false; + private DslQueryProcessor dslQueryProcessor; + + public Builder(TransactionalGraphEngine dbEngine) { + this.dbEngine = dbEngine; + } + + public Builder queryFrom(URI uri) { + this.uri = Optional.of(uri); + this.isGremlin = false; + return this; + } + + public Builder startFrom(Collection<Vertex> vertices) { + this.vertices = Optional.of(vertices); + return this; + } + + public Builder queryFrom( String query, String queryType) { + + if(queryType.equals("gremlin")){ + this.gremlin = Optional.of(query); + this.isGremlin = true; + } + if(queryType.equals("dsl")){ + this.dsl = Optional.of(query); + this.isDsl = true; + } + return this; + } + + public Builder processWith(QueryProcessorType type) { + this.processorType = type; + return this; + } + + public Builder queryProcessor(DslQueryProcessor dslQueryProcessor){ + this.dslQueryProcessor = dslQueryProcessor; + return this; + } + + public Optional<DslQueryProcessor> getDslQueryProcessor(){ + return Optional.ofNullable(this.dslQueryProcessor); + } + + public TransactionalGraphEngine getDbEngine() { + return dbEngine; + } + + public Optional<URI> getUri() { + return uri; + } + + public Optional<String> getGremlin() { + return gremlin; + } + + public boolean isGremlin() { + return isGremlin; + } + + public Optional<String> getDsl() { + return dsl; + } + + public boolean isDsl() { + return isDsl; + } + + public Optional<Collection<Vertex>> getVertices() { + return vertices; + } + + public QueryProcessorType getProcessorType() { + return processorType; + } + + public GenericQueryProcessor create() { + return new GroovyShellImpl(this); + } + + } +} diff --git a/src/main/java/org/onap/aai/rest/search/GroovyShellImpl.java b/src/main/java/org/onap/aai/rest/search/GroovyShellImpl.java new file mode 100644 index 0000000..3db4301 --- /dev/null +++ b/src/main/java/org/onap/aai/rest/search/GroovyShellImpl.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.search; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.onap.aai.restcore.search.GremlinGroovyShellSingleton; + +import java.util.Map; + +public class GroovyShellImpl extends GenericQueryProcessor { + + protected GroovyShellImpl(Builder builder) { + super(builder); + } + + @Override + protected GraphTraversal<?,?> runQuery(String query, Map<String, Object> params) { + + params.put("g", this.dbEngine.asAdmin().getTraversalSource()); + + GremlinGroovyShellSingleton shell = GremlinGroovyShellSingleton.getInstance(); + + return shell.executeTraversal(query, params); + } + +} + + diff --git a/src/main/java/org/onap/aai/rest/search/QueryProcessorType.java b/src/main/java/org/onap/aai/rest/search/QueryProcessorType.java new file mode 100644 index 0000000..c8e1d14 --- /dev/null +++ b/src/main/java/org/onap/aai/rest/search/QueryProcessorType.java @@ -0,0 +1,26 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.search; + +public enum QueryProcessorType { + + GREMLIN_SERVER, + LOCAL_GROOVY +} diff --git a/src/main/java/org/onap/aai/rest/util/EchoResponse.java b/src/main/java/org/onap/aai/rest/util/EchoResponse.java new file mode 100644 index 0000000..05ff38e --- /dev/null +++ b/src/main/java/org/onap/aai/rest/util/EchoResponse.java @@ -0,0 +1,122 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.util; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.restcore.RESTAPI; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * The Class EchoResponse. + */ +@Component +@Path("/util") +public class EchoResponse extends RESTAPI { + + protected static String authPolicyFunctionName = "util"; + + public static final String echoPath = "/util/echo"; + + /** + * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients. + * If there is a query string, a transaction gets logged into hbase, proving the application is connected to the data store. + * If there is no query string, no transacction logging is done to hbase. + * + * @param headers the headers + * @param req the req + * @param myAction if exists will cause transaction to be logged to hbase + * @return the response + */ + @GET + @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + @Path("/echo") + public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req, + @QueryParam("action") String myAction) { + Response response = null; + + AAIException ex = null; + String fromAppId = null; + String transId = null; + + try { + fromAppId = getFromAppId(headers ); + transId = getTransId(headers); + } catch (AAIException e) { + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add("PUT uebProvider"); + templateVars.add("addTopic"); + return Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars)) + .build(); + } + + try { + + HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>(); + + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(fromAppId); + templateVars.add(transId); + + exceptionList.put(new AAIException("AAI_0002", "OK"), templateVars); + + response = Response.status(Status.OK) + .entity(ErrorLogHelper.getRESTAPIInfoResponse( + headers.getAcceptableMediaTypes(), exceptionList)) + .build(); + + } catch (Exception e) { + ex = new AAIException("AAI_4000", e); + ArrayList<String> templateVars = new ArrayList<String>(); + templateVars.add(Action.GET.name()); + templateVars.add(fromAppId +" "+transId); + + response = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse( + headers.getAcceptableMediaTypes(), ex, + templateVars)).build(); + + } finally { + if (ex != null) { + ErrorLogHelper.logException(ex); + } + + } + + return response; + } + +} |