From 55e085c6b8fbac94207e0f69ac2b17c561dbc203 Mon Sep 17 00:00:00 2001 From: "Lamont, William (wl2432)" Date: Mon, 15 Jan 2018 13:15:33 -0500 Subject: Add ability to do optional params for custom query Change-Id: I1e61bbee2def41db5e7064ad3a3e916b7afcc299 Issue-ID: AAI-652 Signed-off-by: Lamont, William (wl2432) --- .../main/java/org/onap/aai/rest/QueryConsumer.java | 91 +++++++++++- .../onap/aai/rest/search/CustomQueryConfig.java | 34 +++++ .../aai/rest/search/GenericQueryProcessor.java | 31 ++-- .../onap/aai/rest/search/GetCustomQueryConfig.java | 159 +++++++++++++++++++++ .../aai/rest/search/GremlinServerSingleton.java | 67 +++++---- .../rest/util/ConvertQueryPropertiesToJson.java | 112 +++++++++++++++ 6 files changed, 455 insertions(+), 39 deletions(-) create mode 100644 aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryConfig.java create mode 100644 aai-traversal/src/main/java/org/onap/aai/rest/search/GetCustomQueryConfig.java create mode 100644 aai-traversal/src/main/java/org/onap/aai/rest/util/ConvertQueryPropertiesToJson.java (limited to 'aai-traversal/src/main/java/org') diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java b/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java index b5f2658..3218396 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java @@ -23,6 +23,7 @@ package org.onap.aai.rest; import java.net.URI; import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -49,9 +50,12 @@ import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.ModelType; import org.onap.aai.introspection.Version; +import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.search.CustomQueryConfig; import org.onap.aai.rest.search.GenericQueryProcessor; +import org.onap.aai.rest.search.GremlinServerSingleton; import org.onap.aai.rest.search.QueryProcessorType; import org.onap.aai.restcore.HttpMethod; import org.onap.aai.restcore.RESTAPI; @@ -121,7 +125,6 @@ public class QueryConsumer extends RESTAPI { startURIs.add(new URI(startElement.getAsString())); } } - if (queryElement != null) { queryURI = queryElement.getAsString(); } @@ -129,6 +132,18 @@ public class QueryConsumer extends RESTAPI { gremlin = gremlinElement.getAsString(); } URI queryURIObj = new URI(queryURI); + + CustomQueryConfig customQueryConfig = getCustomQueryConfig(queryURIObj); + if ( customQueryConfig != null ) { + List missingRequiredQueryParameters = checkForMissingQueryParameters( customQueryConfig.getQueryRequiredProperties(), URITools.getQueryMap(queryURIObj)); + if ( !missingRequiredQueryParameters.isEmpty() ) { + return( createMessageMissingQueryRequiredParameters( missingRequiredQueryParameters, headers, info, req)); + } + } else if ( queryElement != null ) { + return( createMessageInvalidQuerySection( queryURI, headers, info, req)); + } + + GenericQueryProcessor processor = null; if (!startURIs.isEmpty()) { @@ -193,5 +208,79 @@ public class QueryConsumer extends RESTAPI { } + + private List checkForMissingQueryParameters( List requiredParameters, MultivaluedMap queryParams ) { + List result = new ArrayList<>(); + Iterator it = requiredParameters.iterator(); + String param; + while(it.hasNext()) { + param = (String)it.next(); + if ( !queryParams.containsKey(param)) { + result.add(param); + } + } + return result; + } + + private CustomQueryConfig getCustomQueryConfig(URI uriObj ) { + + GremlinServerSingleton gremlinServerSingleton; + CustomQueryConfig customQueryConfig; + String path = uriObj.getPath(); + + String[] parts = path.split("/"); + boolean hasQuery = false; + for ( String part:parts ) { + if ( hasQuery) { + gremlinServerSingleton = GremlinServerSingleton.getInstance(); + return gremlinServerSingleton.getCustomQueryConfig(part); + } + if ( "query".equals(part)) { + hasQuery = true; + } + } + + return null; + + } + + private Response createMessageMissingQueryRequiredParameters(List missingRequiredQueryParams, HttpHeaders headers, UriInfo info, HttpServletRequest req) { + AAIException e = new AAIException("AAI_3013"); + + ArrayList templateVars = new ArrayList<>(); + + if (templateVars.isEmpty()) { + templateVars.add(missingRequiredQueryParams.toString()); + } + Status s = e.getErrorObject().getHTTPResponseCode(); + String errorResponse = ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, + templateVars); + Response response = Response.status(s).entity(errorResponse).build(); + /* + Response response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, + templateVars)).build(); + */ + return response; + } + + private Response createMessageInvalidQuerySection(String invalidQuery, HttpHeaders headers, UriInfo info, HttpServletRequest req) { + AAIException e = new AAIException("AAI_3014"); + + ArrayList templateVars = new ArrayList<>(); + + if (templateVars.isEmpty()) { + templateVars.add(invalidQuery); + } + + Response response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, + templateVars)).build(); + + return response; + } + } diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryConfig.java b/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryConfig.java new file mode 100644 index 0000000..ada67c7 --- /dev/null +++ b/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryConfig.java @@ -0,0 +1,34 @@ +package org.onap.aai.rest.search; + +import java.util.List; + +public class CustomQueryConfig { + public CustomQueryConfig() { + // used by GetCustomQueryConfig + } + + + private String query; + private List queryOptionalProperties; + private List queryRequiredProperties; + + public void setQuery(String query) { + this.query = query; + } + public String getQuery() { + return this.query; + } + + public void setQueryOptionalProperties( List queryOptionalProperties) { + this.queryOptionalProperties = queryOptionalProperties; + } + public List getQueryOptionalProperties( ) { + return queryOptionalProperties; + } + public void setQueryRequiredProperties( List queryRequiredProperties) { + this.queryRequiredProperties = queryRequiredProperties; + } + public List getQueryRequiredProperties( ) { + return queryRequiredProperties; + } +} diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java b/aai-traversal/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java index 9f6f3aa..14e218f 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/search/GenericQueryProcessor.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017 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. @@ -25,6 +25,7 @@ import java.io.FileNotFoundException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,6 +43,7 @@ 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.query.builder.MissingOptionalParameter; import org.onap.aai.restcore.util.URITools; import org.onap.aai.serialization.engines.TransactionalGraphEngine; import org.onap.aai.serialization.queryformats.SubGraphStyle; @@ -117,28 +119,41 @@ public abstract class GenericQueryProcessor { if (!this.isGremlin) { Matcher m = p.matcher(uri.get().getPath()); String queryName = ""; + List optionalParameters = Collections.emptyList(); if (m.find()) { queryName = m.group(1); + CustomQueryConfig queryConfig = gremlinServerSingleton.getCustomQueryConfig(queryName); + if ( queryConfig != null ) { + query = queryConfig.getQuery(); + optionalParameters = queryConfig.getQueryOptionalProperties(); + } } - + for (String key : queryParams.keySet()) { params.put(key, queryParams.getFirst(key)); + if ( optionalParameters.contains(key) ){ + optionalParameters.remove(key); + } } - query = gremlinServerSingleton.getStoredQuery(queryName); - if (query == null) { - query = ""; - } else { - query = queryBuilderSingleton.executeTraversal(dbEngine, query, params); + if (!optionalParameters.isEmpty()){ + MissingOptionalParameter missingParameter = MissingOptionalParameter.getInstance(); + for ( String key : optionalParameters ) { + params.put(key, missingParameter); + } } - List ids = new ArrayList<>(); if (vertices.isPresent() && !vertices.get().isEmpty()) { for (Vertex v : vertices.get()) { ids.add(v.id()); } + if (query == null) { + query = ""; + } else { + query = queryBuilderSingleton.executeTraversal(dbEngine, query, params); + } StringBuilder sb = new StringBuilder(); sb.append("["); sb.append(Joiner.on(",").join(ids)); diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/search/GetCustomQueryConfig.java b/aai-traversal/src/main/java/org/onap/aai/rest/search/GetCustomQueryConfig.java new file mode 100644 index 0000000..9ea0b1d --- /dev/null +++ b/aai-traversal/src/main/java/org/onap/aai/rest/search/GetCustomQueryConfig.java @@ -0,0 +1,159 @@ +package org.onap.aai.rest.search; + +/*- + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright (C) 2017 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========================================================= + */ + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.onap.aai.util.AAIConstants; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +public class GetCustomQueryConfig { + + private JsonArray storedQueries = null; + private CustomQueryConfig customQueryConfig; + + + private final static String QUERY_CONFIG = "query"; + private final static String REQUIRED_CONFIG = "required-properties"; + private final static String OPTIONAL_CONFIG = "optional-properties"; + private final static String STORED_QUERIES_CONFIG = "stored-queries"; + private final static String STORED_QUERY_CONFIG = "stored-query"; + + public static final String AAI_HOME_ETC_QUERY_JSON = AAIConstants.AAI_HOME_ETC + "query" + AAIConstants.AAI_FILESEP + "stored-queries.json"; + + public GetCustomQueryConfig(String customQueryJson ) { + init(customQueryJson); + } + + private void init( String customQueryJson) { + JsonParser parser = new JsonParser(); + JsonObject queriesObject = parser.parse(customQueryJson).getAsJsonObject(); + if (queriesObject.has(STORED_QUERIES_CONFIG)) { + + storedQueries = queriesObject.getAsJsonArray(STORED_QUERIES_CONFIG); + } + } + + private List toStringList(JsonArray array) { + Gson converter = new Gson(); + Type listType = new TypeToken>() {}.getType(); + return converter.fromJson(array, listType); + } + + private List getPropertyList(JsonObject configObject, String config ) { + JsonElement subqueryConfig; + JsonArray props; + + if ( configObject.has(config)) { + subqueryConfig = configObject.get(config); + if ( subqueryConfig != null && !subqueryConfig.isJsonNull() ) { + props = subqueryConfig.getAsJsonArray(); + if ( props != null ) { + return toStringList(props); + } + } + } + return toStringList(null); + } + + private String getPropertyString(JsonObject configObject, String config) { + JsonElement subqueryConfig; + + if ( configObject.has(config)) { + subqueryConfig = configObject.get(config); + if ( subqueryConfig != null && !subqueryConfig.isJsonNull() ) { + return subqueryConfig.getAsString(); + } + } + return null; + } + + private void getStoredQueryBlock( JsonObject configObject, String config ) { + if ( !configObject.has(config)) { + return; + } + + JsonElement queryConfig; + JsonObject subObject; + String multipleStartNodes; + List propertyList; + + queryConfig = configObject.get(config); + subObject = queryConfig.getAsJsonObject(); + propertyList = getPropertyList(subObject, REQUIRED_CONFIG); + if ( QUERY_CONFIG.equals(config)) { + customQueryConfig.setQueryRequiredProperties( propertyList ); + } else { + customQueryConfig.setQueryRequiredProperties( null ); + } + + propertyList = getPropertyList(subObject, OPTIONAL_CONFIG); + if ( QUERY_CONFIG.equals(config)) { + customQueryConfig.setQueryOptionalProperties( propertyList ); + } else { + customQueryConfig.setQueryOptionalProperties( null ); + } + + } + + + public CustomQueryConfig getStoredQuery(String queryName ) { + + customQueryConfig = null; + JsonObject configObject; + JsonElement query; + JsonElement queryConfig; + String queryString; + + for (JsonElement storedQuery : storedQueries) { + if (storedQuery.isJsonObject()) { + JsonObject queryObject = storedQuery.getAsJsonObject(); + query = queryObject.get(queryName); + if ( query != null ) { + customQueryConfig = new CustomQueryConfig(); + configObject = query.getAsJsonObject(); + getStoredQueryBlock(configObject, QUERY_CONFIG); + if ( configObject.has(STORED_QUERY_CONFIG)) { + queryConfig = configObject.get(STORED_QUERY_CONFIG); + customQueryConfig.setQuery(queryConfig.getAsString()); + } + break; + } + } + } + + return customQueryConfig; + + } + + + +} diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/search/GremlinServerSingleton.java b/aai-traversal/src/main/java/org/onap/aai/rest/search/GremlinServerSingleton.java index e4ac815..2265680 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/search/GremlinServerSingleton.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/search/GremlinServerSingleton.java @@ -31,6 +31,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Date; import java.util.Properties; import java.util.Timer; @@ -43,7 +46,10 @@ public class GremlinServerSingleton { private Cluster cluster; private boolean timerSet; private Timer timer; - private Properties properties; + + + private GetCustomQueryConfig queryConfig; + private static class Helper { private static final GremlinServerSingleton INSTANCE = new GremlinServerSingleton(); @@ -68,8 +74,6 @@ public class GremlinServerSingleton { */ private void init() { - properties = new Properties(); - try { cluster = Cluster.build(new File(AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "gremlin-server-config.yaml")) .maxContentLength(6537920) @@ -78,27 +82,29 @@ public class GremlinServerSingleton { logger.error("Unable to find the file: " + e); } - File queryFile = new File(AAIConstants.AAI_HOME_ETC_QUERY); + try { + String filepath = GetCustomQueryConfig.AAI_HOME_ETC_QUERY_JSON; + Path path = Paths.get(filepath); + String customQueryConfigJson = new String(Files.readAllBytes(path)); + - try (FileInputStream fis = new FileInputStream(queryFile)){ - properties.load(fis); - } catch (IOException e) { - logger.error("Error occurred during the processing of query file: " + e); - } + queryConfig = new GetCustomQueryConfig(customQueryConfigJson); + } catch (IOException e) { + logger.error("Error occurred during the processing of query json file: " + e); + } - TimerTask task = new FileWatcher(new File(AAIConstants.AAI_HOME_ETC_QUERY)) { + TimerTask task = new FileWatcher(new File(GetCustomQueryConfig.AAI_HOME_ETC_QUERY_JSON)) { @Override protected void onChange(File file) { - File queryFile = new File(AAIConstants.AAI_HOME_ETC_QUERY); - try (FileInputStream fis = new FileInputStream(queryFile)){ - properties.load(fis); - logger.debug("File: " + file + " was changed so the cluster is rebuild for gremlin server"); - } catch (FileNotFoundException e) { - logger.error("Unable to find the file: " + e); - } catch (IOException e) { - logger.error("Error occurred during the processing of query file: " + e); - } + try { + String filepath = GetCustomQueryConfig.AAI_HOME_ETC_QUERY_JSON; + Path path = Paths.get(filepath); + String customQueryConfigJson = new String(Files.readAllBytes(path)); + queryConfig = new GetCustomQueryConfig(customQueryConfigJson); + } catch (IOException e) { + logger.error("Error occurred during the processing of query json file: " + e); + } } }; @@ -113,19 +119,20 @@ public class GremlinServerSingleton { public Cluster getCluster(){ return cluster; } - + /** - * Gets the key if the properties contains that key - * - * Purposely not checking if the property exists due - * to if you check for the property and then get the property - * Then you are going to have to synchronize the method - * - * @param key the query to check if it exists in the file - * @return string if the key exists or null if it doesn't + * Gets the query using CustomQueryConfig + * @param key + * @return */ - public String getStoredQuery(String key){ - return (String) properties.get(key); + public String getStoredQueryFromConfig(String key){ + CustomQueryConfig customQueryConfig = queryConfig.getStoredQuery(key); + return customQueryConfig.getQuery(); } + + public CustomQueryConfig getCustomQueryConfig(String key) { + return queryConfig.getStoredQuery(key); + } + } diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/util/ConvertQueryPropertiesToJson.java b/aai-traversal/src/main/java/org/onap/aai/rest/util/ConvertQueryPropertiesToJson.java new file mode 100644 index 0000000..780a5de --- /dev/null +++ b/aai-traversal/src/main/java/org/onap/aai/rest/util/ConvertQueryPropertiesToJson.java @@ -0,0 +1,112 @@ +package org.onap.aai.rest.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + + +import org.onap.aai.util.AAIConstants; + +public class ConvertQueryPropertiesToJson { + + private final static int maxfilesize = 256000; + + private void addStart( StringBuilder sb ) { + sb.append("{\n \"stored-queries\":[{\n"); + } + + private void addRequiredQueryProperties( StringBuilder sb, List rqd ) { + Iterator it = rqd.iterator(); + sb.append(" \"query\":{\n \"required-properties\":["); + while( it.hasNext()) { + sb.append("\"" + it.next() + "\""); + if ( it.hasNext()) { + sb.append(","); + } + } + sb.append("]\n },\n"); + } + + private void addAnotherQuery( StringBuilder sb, String queryName, String query, List rqd ) { + sb.append(" \"" + queryName + "\":{\n"); + if ( !rqd.isEmpty()) { + addRequiredQueryProperties( sb, rqd); + } + sb.append(" \"stored-query\":\"" + query + "\"\n }\n },{\n"); + } + + private void addLastQuery( StringBuilder sb, String queryName, String query, List rqd ) { + sb.append(" \"" + queryName + "\":{\n"); + if ( !rqd.isEmpty() ) { + addRequiredQueryProperties( sb, rqd); + } + sb.append(" \"stored-query\":\"" + query + "\"\n }\n }]\n}\n"); + } + + private String get2ndParameter( String paramString) { + String endParams = paramString.substring(0, paramString.indexOf(')')); + String result = endParams.substring(endParams.indexOf(',') + 1 ); + String lastParam = result.trim(); + if ( lastParam.startsWith("\\") || lastParam.startsWith("'") || lastParam.startsWith("new ") ){ + return null; + } + + return lastParam; + } + + private List findRqdProperties( String query) { + String[] parts = query.split("getVerticesByProperty"); + List result = new ArrayList(); + if ( parts.length == 1 ) + return result; + int count = 0; + String foundRqdProperty; + while ( count++ < parts.length - 1 ) { + foundRqdProperty = get2ndParameter(parts[count]); + if ( foundRqdProperty != null && !result.contains(foundRqdProperty)) { + result.add(foundRqdProperty); + } + } + return result; + } + + public String convertProperties( Properties props ) { + Enumeration e = props.propertyNames(); + StringBuilder sb = new StringBuilder(maxfilesize); + String queryName; + String query; + addStart( sb ); + List rqd; + while ( e.hasMoreElements()) { + queryName = (String)e.nextElement(); + query = props.getProperty(queryName).trim().replace("\"", "\\\""); + rqd = findRqdProperties( query); + if ( e.hasMoreElements()) { + addAnotherQuery( sb, queryName, query, rqd); + } else { + addLastQuery( sb, queryName, query, rqd); + } + } + + return sb.toString(); + } + public static void main(String[] args) { + File queryFile = new File(AAIConstants.AAI_HOME_ETC_QUERY); + Properties properties = new Properties(); + try (FileInputStream fis = new FileInputStream(queryFile)){ + properties.load(fis); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("Error occurred during the processing of query file: " + e); + } + ConvertQueryPropertiesToJson c = new ConvertQueryPropertiesToJson(); + String json = c.convertProperties(properties); + System.out.println("returned json:\n" + json); + } + +} -- cgit 1.2.3-korg