/** * ============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 java.util.List; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import org.onap.aai.concurrent.AaiCallable; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.ModelType; import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; import org.onap.aai.rest.db.HttpEntry; 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; 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.transforms.XmlFormatTransformer; import org.onap.aai.util.AAIConstants; import org.onap.aai.util.TraversalConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import io.micrometer.core.annotation.Timed; @Path("/recents/{version: v[1-9][0-9]*|latest}") @Timed public class RecentAPIConsumer extends RESTAPI { private static final String AAI_3021 = "AAI_3021"; /** The introspector factory type. */ private ModelType introspectorFactoryType = ModelType.MOXY; private QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY; /** The query style. */ private static final Logger LOGGER = LoggerFactory.getLogger(RecentAPIConsumer.class); private HttpEntry traversalUriHttpEntry; private SchemaVersions schemaVersions; private String basePath; private GremlinServerSingleton gremlinServerSingleton; private XmlFormatTransformer xmlFormatTransformer; @Autowired public RecentAPIConsumer(HttpEntry traversalUriHttpEntry, SchemaVersions schemaVersions, GremlinServerSingleton gremlinServerSingleton, XmlFormatTransformer xmlFormatTransformer, @Value("${schema.uri.base.path}") String basePath) { this.traversalUriHttpEntry = traversalUriHttpEntry; this.schemaVersions = schemaVersions; this.gremlinServerSingleton = gremlinServerSingleton; this.xmlFormatTransformer = xmlFormatTransformer; this.basePath = basePath; } @GET @Path("/{nodeType: .+}") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response getRecentData(String content, @PathParam("version") String versionParam, @PathParam("nodeType") String nodeType, @Context HttpHeaders headers, @Context HttpServletRequest req, @Context UriInfo info) { return runner(TraversalConstants.AAI_TRAVERSAL_TIMEOUT_ENABLED, TraversalConstants.AAI_TRAVERSAL_TIMEOUT_APP, TraversalConstants.AAI_TRAVERSAL_TIMEOUT_LIMIT, headers, info, HttpMethod.GET, new AaiCallable() { @Override public Response process() { return processRecentData(content, req, versionParam, nodeType, info, headers); } }); } public Response processRecentData(String content, HttpServletRequest req, @PathParam("version") String versionParam, @PathParam("nodeType") String nodeType, @Context UriInfo info, @Context HttpHeaders headers) { String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor"); QueryProcessorType processorType = this.processorType; Response response; TransactionalGraphEngine dbEngine = null; try { if (queryProcessor != null) { processorType = QueryProcessorType.valueOf(queryProcessor); } SchemaVersion version = new SchemaVersion(versionParam); this.checkVersion(version); String serverBase = req.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/"); traversalUriHttpEntry.setHttpEntryProperties(version, serverBase); dbEngine = traversalUriHttpEntry.getDbEngine(); /* * Check for mandatory parameters here */ this.checkNodeType(nodeType); this.checkQueryParams(info.getQueryParameters()); GenericQueryProcessor processor = null; processor = new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton) .queryFrom(nodeType, "nodeQuery").uriParams(info.getQueryParameters()) .processWith(processorType).create(); String result = ""; SubGraphStyle subGraphStyle = null; List vertices = processor.execute(subGraphStyle); DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); FormatFactory ff = new FormatFactory(traversalUriHttpEntry.getLoader(), serializer, schemaVersions, this.basePath, serverBase); Format format = Format.pathed_resourceversion; Formatter formater = ff.get(format, info.getQueryParameters()); result = formater.output(vertices).toString(); // LOGGER.info("Completed"); String acceptType = headers.getHeaderString("Accept"); if (acceptType == null) { acceptType = MediaType.APPLICATION_JSON; } if (MediaType.APPLICATION_XML_TYPE.isCompatible(MediaType.valueOf(acceptType))) { result = xmlFormatTransformer.transform(result); } response = Response.status(Status.OK).type(acceptType).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 { if (dbEngine != null) { dbEngine.rollback(); } } return response; } private void checkVersion(SchemaVersion version) throws AAIException { if (!schemaVersions.getVersions().contains(version)) { throw new AAIException(AAI_3021, "Schema Version is not valid"); } } public void checkNodeType(String nodeType) throws AAIException { try { traversalUriHttpEntry.getLoader().introspectorFromName(nodeType); } catch (AAIUnknownObjectException e) { throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to recents query."); } } public void checkQueryParams(MultivaluedMap params) throws AAIException { boolean isHoursParameter = false; boolean isDateTimeParameter = false; if (params != null && params.containsKey("hours") && params.getFirst("hours").matches("-?\\d+")) { isHoursParameter = true; long hours; try { hours = Long.parseLong(params.getFirst("hours")); } catch (NumberFormatException ex) { throw new AAIException(AAI_3021, " Invalid Hours. Valid values for hours are 1 to " + AAIConstants.HISTORY_MAX_HOURS); } if (hours < 1 || hours > AAIConstants.HISTORY_MAX_HOURS) { throw new AAIException(AAI_3021, " Valid values for hours are 1 to " + AAIConstants.HISTORY_MAX_HOURS); } } if (params != null && params.containsKey("date-time") && params.getFirst("date-time").matches("-?\\d+")) { isDateTimeParameter = true; Long minStartTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(AAIConstants.HISTORY_MAX_HOURS); Long startTime; try { startTime = Long.parseLong(params.getFirst("date-time")); } catch (NumberFormatException ex) { throw new AAIException(AAI_3021, " Invalid Data-time. Valid values for date-time are " + minStartTime + " to " + System.currentTimeMillis()); } if (startTime < minStartTime) { throw new AAIException(AAI_3021, " Valid values for date-time are " + minStartTime + " to " + System.currentTimeMillis()); } } if (!isHoursParameter && !isDateTimeParameter) { throw new AAIException(AAI_3021, "Send valid hours or date-time to specify the timebounds"); } if (isHoursParameter && isDateTimeParameter) { throw new AAIException(AAI_3021, "Send either hours or date-time and not both to specify the timebounds"); } } }