diff options
author | Venkata Harish K Kajur <vk250x@att.com> | 2017-09-28 13:56:02 -0400 |
---|---|---|
committer | Venkata Harish K Kajur <vk250x@att.com> | 2017-09-28 17:46:16 -0400 |
commit | b33b55b16d1c230fb1bf454d7d517f2c2d57941b (patch) | |
tree | 4802fbdf61c2e8c1cf19c35cd3dc9b0df96d5ed3 /aai-resources/src/main/java/org/onap/aai/rest | |
parent | 9d5eff1a6c19f9af9329f76f3e58d8935eb28dad (diff) |
Change package names org.openecomp to org.onap
Issue-ID: AAI-61 AAI-82
Change-Id: Ib1d937fb31b1e737c4651eac9c0193fd05d97f01
Signed-off-by: Venkata Harish K Kajur <vk250x@att.com>
Diffstat (limited to 'aai-resources/src/main/java/org/onap/aai/rest')
17 files changed, 2672 insertions, 0 deletions
diff --git a/aai-resources/src/main/java/org/onap/aai/rest/BulkAddConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/BulkAddConsumer.java new file mode 100644 index 0000000..b81c42c --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/BulkAddConsumer.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import javax.ws.rs.Path; + +import org.onap.aai.restcore.HttpMethod; + +@Path("{version: v[8-9]|v1[01]}/bulkadd") +public class BulkAddConsumer extends BulkConsumer { + + @Override + protected boolean functionAllowed(HttpMethod method) { + + return method.equals(HttpMethod.PUT); + + } + + @Override + protected boolean enableResourceVersion() { + return true; + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java new file mode 100644 index 0000000..fa74f7b --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java @@ -0,0 +1,485 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.PUT; +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.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.javatuples.Pair; +import org.javatuples.Triplet; + +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.introspection.exceptions.AAIUnmarshallingException; +import org.onap.aai.logging.ErrorObjectNotFoundException; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.rest.db.DBRequest; +import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.util.ValidateEncoding; +import org.onap.aai.restcore.HttpMethod; +import org.onap.aai.restcore.RESTAPI; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +/** + * The Class BulkAddConsumer. + */ +/* + * The purpose of this endpoint is to allow a client to add + * multiple objects with one request. It may take + * one or more transaction objects containing one or more + * objects to add. + * The transactions are independent of each other - + * if one fails, its effects are rolled back, but the others' aren't. + * Within a single transaction, if adding one object fails, all the others' + * changes are rolled back. + */ +public abstract class BulkConsumer extends RESTAPI { + + /** The introspector factory type. */ + private ModelType introspectorFactoryType = ModelType.MOXY; + + /** The query style. */ + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + + /** + * Bulk add. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PUT + @Consumes({ MediaType.APPLICATION_JSON}) + @Produces({ MediaType.APPLICATION_JSON}) + public Response bulkAdd(String content, @PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req){ + + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + Version version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + + Response response = null; + + /* A Response will be generated for each object in each transaction. + * To keep track of what came from where to give organized feedback to the client, + * we keep responses from a given transaction together in one list (hence all being a list of lists) + * and pair each response with its matching URI (which will be null if there wasn't one). + */ + List<List<Pair<URI, Response>>> allResponses = new ArrayList<List<Pair<URI, Response>>>(); + + try { + //TODO add auth check when this endpoint added to that auth properties files + + + JsonArray transactions = getTransactions(content); + + for (int i = 0; i < transactions.size(); i++){ + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + Loader loader = httpEntry.getLoader(); + TransactionalGraphEngine dbEngine = httpEntry.getDbEngine(); + URI thisUri = null; + List<Triplet<URI, Introspector,HttpMethod>> triplet = new ArrayList<Triplet<URI, Introspector,HttpMethod>>(); + HttpMethod method = null; + try { + JsonElement transObj = transactions.get(i); + if (!(transObj instanceof JsonObject)) { + throw new AAIException("AAI_6111", "input payload does not follow bulk add interface"); + } + //JsonObject transaction = transObj.getAsJsonObject(); + + fillObjectTuplesFromTransaction(triplet, transObj.getAsJsonObject(), loader, dbEngine, outputMediaType); + if (triplet.size() == 0) { + //case where user sends a validly formatted transactions object but + //which has no actual things in it for A&AI to do anything with + //assuming we should count this as a user error + throw new AAIException("AAI_6118", "payload had no objects to operate on"); + } + + List<DBRequest> requests = new ArrayList<>(); + for (Triplet<URI, Introspector, HttpMethod> tuple : triplet){ + thisUri = tuple.getValue0(); + method = tuple.getValue2(); + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(thisUri); + DBRequest request = new DBRequest.Builder(method, thisUri, uriQuery, tuple.getValue1(), headers, info, transId).build(); + requests.add(request); + } + + Pair<Boolean, List<Pair<URI, Response>>> results = httpEntry.process(requests, sourceOfTruth, this.enableResourceVersion()); + List<Pair<URI, Response>> responses = results.getValue1(); + allResponses.add(responses); + if (results.getValue0()) { //everything was processed without error + dbEngine.commit(); + } else { //something failed + dbEngine.rollback(); + } + } catch (Exception e) { + /* While httpEntry.process handles its exceptions, exceptions thrown in earlier helpers + * bubbles up to here. As we want to tie error messages to the URI of the object that caused + * them, we catch here, generate a Response, bundle it with that URI, and move on. + */ + method = HttpMethod.PUT; + if (triplet.size() != 0) { //failed somewhere in the middle of tuple-filling + Triplet<URI, Introspector, HttpMethod> lastTuple = triplet.get(triplet.size()-1); //last one in there was the problem + if (lastTuple.getValue1() == null){ + //failed out before thisUri could be set but after tuples started being filled + thisUri = lastTuple.getValue0(); + method = lastTuple.getValue2(); + } + } //else failed out on empty payload so tuples never filled (or failed out even earlier than tuple-filling) + addExceptionCaseFailureResponse(allResponses, e, i, thisUri, headers, info, method); + dbEngine.rollback(); + continue; /* if an exception gets thrown within a transaction we want to keep going to + the next transaction, not break out of the whole request */ + } + } + + String returnPayload = generateResponsePayload(allResponses); + + //unless a top level error gets thrown, we want to 201 bc the client wanted a "fire and forget" kind of setup + response = Response + .status(Status.CREATED) + .entity(returnPayload) + .build(); + } catch (AAIException e) { //these catches needed for handling top level errors in payload parsing where the whole request must fail out + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, e); + } catch(JsonSyntaxException e) { + AAIException ex = new AAIException("AAI_6111"); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, ex); + } catch (Exception e ) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, ex); + } + + return response; + } + + + /** + * Gets the transactions. + * + * @param content - input JSON payload string + * @return JsonArray - the array of transactions + * @throws AAIException the AAI exception + * @throws JsonSyntaxException Parses and breaks the single payload into an array of individual transaction + * bodies to be processed. + */ + private JsonArray getTransactions(String content) throws AAIException, JsonSyntaxException { + JsonParser parser = new JsonParser(); + + JsonObject input = parser.parse(content).getAsJsonObject(); + + if (!(input.has("transactions"))) { + throw new AAIException("AAI_6118", "input payload does not follow bulk add interface - missing \"transactions\""); + } + JsonElement transactionsObj = input.get("transactions"); + + if (!(transactionsObj.isJsonArray())){ + throw new AAIException("AAI_6111", "input payload does not follow bulk add interface"); + } + JsonArray transactions = transactionsObj.getAsJsonArray(); + if (transactions.size() == 0) { + //case where user sends a validly formatted transactions object but + //which has no actual things in it for A&AI to do anything with + //assuming we should count this as a user error + throw new AAIException("AAI_6118", "payload had no objects to operate on"); + } + return transactions; + } + + /** + * Fill object tuples from transaction. + * + * @param tuples the tuples + * @param transaction - JSON body containing the objects to be added + * each object must have a URI and an object body + * @param loader the loader + * @param dbEngine the db engine + * @param inputMediaType the input media type + * @return list of tuples containing each introspector-wrapped object and its given URI + * @throws AAIException the AAI exception + * @throws JsonSyntaxException the json syntax exception + * @throws UnsupportedEncodingException Walks through the given transaction and unmarshals each object in it, then bundles each + * with its URI. + */ + private void fillObjectTuplesFromTransaction(List<Triplet<URI, Introspector, HttpMethod>> triplet, + JsonObject transaction, Loader loader, TransactionalGraphEngine dbEngine, String inputMediaType) + throws AAIException, JsonSyntaxException, UnsupportedEncodingException { + + + if (transaction.has("put") && this.functionAllowed(HttpMethod.PUT)) { + pairUp(triplet, transaction, loader, dbEngine, inputMediaType, HttpMethod.PUT); + } + else if (transaction.has("delete") && this.functionAllowed(HttpMethod.DELETE)) { + pairUp(triplet, transaction, loader, dbEngine, inputMediaType, HttpMethod.DELETE); + } + else if (transaction.has("patch") && this.functionAllowed(HttpMethod.MERGE_PATCH)) { + pairUp(triplet, transaction, loader, dbEngine, inputMediaType, HttpMethod.MERGE_PATCH); + } + + else{ + + throw new AAIException("AAI_6118", "input payload does not follow bulk add interface - missing put delete or patch"); + } + + + + } + + + + private void pairUp(List<Triplet<URI, Introspector, HttpMethod>> triplet, JsonObject item, Loader loader, TransactionalGraphEngine dbEngine, String inputMediaType, HttpMethod method) throws AAIException, JsonSyntaxException, UnsupportedEncodingException{ + + + for (int i=0; i<item.size(); i++) { + Triplet<URI, Introspector, HttpMethod> tuple = Triplet.with(null, null,null); + + tuple = tuple.setAt2(method); + try { + if (!(item.isJsonObject())) { + throw new AAIException("AAI_6111", "input payload does not follow bulk add interface"); + } + + + JsonElement actionElement = null; + + if(item.has("put")){ + actionElement = item.get("put"); + } else if(item.has("patch")){ + actionElement = item.get("patch"); + } else if(item.has("delete")){ + actionElement = item.get("delete"); + } + + if ((actionElement == null) || !actionElement.isJsonArray()) { + throw new AAIException("AAI_6111", "input payload does not follow bulk add interface"); + } + JsonArray httpArray = actionElement.getAsJsonArray(); + for(int j = 0; j < httpArray.size(); ++j){ + JsonObject it = httpArray.get(j).getAsJsonObject(); + JsonElement itemURIfield = it.get("uri"); + if (itemURIfield == null) { + throw new AAIException("AAI_6118", "must include object uri"); + } + String uriStr = itemURIfield.getAsString(); + if (uriStr.endsWith("/relationship-list/relationship")) { + if (method.equals(HttpMethod.PUT)) { + tuple = tuple.setAt2(HttpMethod.PUT_EDGE); + } else if (method.equals(HttpMethod.DELETE)) { + tuple = tuple.setAt2(HttpMethod.DELETE_EDGE); + } + } else { + tuple = tuple.setAt2(method); + } + + URI uri = UriBuilder.fromPath(uriStr).build(); + + /* adding the uri as soon as we have one (valid or not) lets us + * keep any errors with their corresponding uris for client feedback + */ + tuple = tuple.setAt0(uri); + + if (!ValidateEncoding.getInstance().validate(uri)) { + throw new AAIException("AAI_3008", "uri=" + uri.getPath()); + } + + if (!(it.has("body"))){ + throw new AAIException("AAI_6118", "input payload does not follow bulk add interface - missing \"body\""); + } + JsonElement bodyObj = it.get("body"); + if (!(bodyObj.isJsonObject())) { + throw new AAIException("AAI_6111", "input payload does not follow bulk add interface"); + } + + Gson gson = new Gson(); + + String bodyStr = gson.toJson(bodyObj); + + if (tuple.getValue2().equals(HttpMethod.PUT_EDGE)) { + Introspector obj; + try { + obj = loader.unmarshal("relationship", bodyStr, org.onap.aai.restcore.MediaType.getEnum(inputMediaType)); + } catch (AAIUnmarshallingException e) { + throw new AAIException("AAI_3000", "object could not be unmarshalled:" + bodyStr); + + } + + + tuple = tuple.setAt1(obj); + + } else { + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uri); + String objName = uriQuery.getResultType(); + + Introspector obj; + try { + obj = loader.unmarshal(objName, bodyStr, org.onap.aai.restcore.MediaType.getEnum(inputMediaType)); + } catch (AAIUnmarshallingException e) { + throw new AAIException("AAI_3000", "object could not be unmarshalled:" + bodyStr); + + } + + this.validateIntrospector(obj, loader, uri, tuple.getValue2()); + tuple = tuple.setAt1(obj); + } + triplet.add(tuple); + } +// JsonElement itemURIfield = item.get("uri"); + + } catch (AAIException e) { + // even if tuple doesn't have a uri or body, this way we keep all information associated with this error together + // even if both are null, that indicates how the input was messed up, so still useful to carry around like this + triplet.add(tuple); + throw e; //rethrow so the right response is generated on the level above + } + } + } + + + + /** + * Generate response payload. + * + * @param allResponses - the list of the lists of responses from every action in every transaction requested + * @return A json string of similar format to the bulk add interface which for each response includes + * the original URI and a body with the status code of the response and the error message. + * + * Creates the payload for a single unified response from all responses generated + */ + private String generateResponsePayload(List<List<Pair<URI,Response>>> allResponses){ + JsonObject ret = new JsonObject(); + JsonArray retArr = new JsonArray(); + + for(List<Pair<URI,Response>> responses : allResponses){ + JsonObject tResp = new JsonObject(); + JsonArray tArrResp = new JsonArray(); + + for (Pair<URI,Response> r : responses) { + JsonObject indPayload = new JsonObject(); + + URI origURI = r.getValue0(); + if (origURI != null) { + indPayload.addProperty("uri", origURI.getPath()); + } else { + indPayload.addProperty("uri", (String)null); + } + + JsonObject body = new JsonObject(); + + int rStatus = r.getValue1().getStatus(); + String rContents = null; + + rContents = (String)r.getValue1().getEntity(); + + body.addProperty(new Integer(rStatus).toString(), rContents); + indPayload.add("body", body); + + tArrResp.add(indPayload); + } + + tResp.add("put", tArrResp); + retArr.add(tResp); + } + ret.add("transaction", retArr); + Gson gson = new GsonBuilder().serializeNulls().create(); + String jsonStr = gson.toJson(ret); + return jsonStr; + } + + /** + * Adds the exception case failure response. + * + * @param allResponses the all responses + * @param e the e + * @param index - index of which transaction was being processed when the exception was thrown + * @param thisUri the this uri + * @param headers the headers + * @param info the info + * @param templateAction the template action + * @param logline Generates a Response based on the given exception and adds it to the collection of responses for this request. + * @throws ErrorObjectNotFoundException + */ + private void addExceptionCaseFailureResponse(List<List<Pair<URI, Response>>> allResponses, Exception e, int index, URI thisUri, HttpHeaders headers, UriInfo info, HttpMethod templateAction) { + AAIException ex = null; + + if (!(e instanceof AAIException)){ + ex = new AAIException("AAI_4000", e); + } else { + ex = (AAIException)e; + } + + if (allResponses.size() != (index+1)) { + //index+1 bc if all transactions thus far have had a response list added + //the size will be one more than the current index (since those are offset by 1) + + //this transaction doesn't have a response list yet, so create one + Response failResp = consumerExceptionResponseGenerator(headers, info, templateAction, ex); + Pair<URI, Response> uriResp = Pair.with(thisUri, failResp); + List<Pair<URI, Response>> transRespList = new ArrayList<Pair<URI,Response>>(); + transRespList.add(uriResp); + allResponses.add(transRespList); + } else { + //this transaction already has a response list, so add this failure response to it + Response failResp = consumerExceptionResponseGenerator(headers, info, templateAction, ex); + Pair<URI, Response> uriResp = Pair.with(thisUri, failResp); + List<Pair<URI, Response>> tResps = allResponses.get(index); + tResps.add(uriResp); + } + } + + protected abstract boolean functionAllowed(HttpMethod method); + + protected abstract boolean enableResourceVersion(); + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/BulkProcessConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/BulkProcessConsumer.java new file mode 100644 index 0000000..d22fe21 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/BulkProcessConsumer.java @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import javax.ws.rs.Path; + +import org.onap.aai.restcore.HttpMethod; + +@Path("{version: v[2789]|v1[01]}/bulkprocess") +public class BulkProcessConsumer extends BulkConsumer { + + @Override + protected boolean functionAllowed(HttpMethod method) { + + return method.equals(HttpMethod.PUT) || method.equals(HttpMethod.DELETE) || method.equals(HttpMethod.MERGE_PATCH); + } + + @Override + protected boolean enableResourceVersion() { + // TODO Auto-generated method stub + return true; + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/ExampleConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/ExampleConsumer.java new file mode 100644 index 0000000..c942258 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/ExampleConsumer.java @@ -0,0 +1,105 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import javax.servlet.http.HttpServletRequest; +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.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.MarshallerProperties; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.introspection.generator.CreateExample; +import org.onap.aai.restcore.HttpMethod; +import org.onap.aai.restcore.RESTAPI; + +/** + * The Class ExampleConsumer. + */ +@Path("/{version: v[2789]|v1[01]}/examples") +public class ExampleConsumer extends RESTAPI { + + + /** + * Gets the example. + * + * @param versionParam the version param + * @param type the type + * @param headers the headers + * @param info the info + * @param req the req + * @return the example + */ + @GET + @Path("/{objectType: [^\\/]+}") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getExample(@PathParam("version")String versionParam, @PathParam("objectType")String type, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + Status status = Status.INTERNAL_SERVER_ERROR; + Response response = null; + + try { + String mediaType = getMediaType(headers.getAcceptableMediaTypes()); + org.onap.aai.restcore.MediaType outputMediaType = org.onap.aai.restcore.MediaType.getEnum(mediaType); + + Version version = Version.valueOf(versionParam); + Loader loader = LoaderFactory.createLoaderForVersion(ModelType.MOXY, version); + + CreateExample example = new CreateExample(loader, type); + + Introspector obj = example.getExampleObject(); + String result = ""; + if (obj != null) { + status = Status.OK; + MarshallerProperties properties = + new MarshallerProperties.Builder(outputMediaType).build(); + result = obj.marshal(properties); + } else { + + } + response = Response + .ok(obj) + .entity(result) + .status(status) + .type(outputMediaType.toString()).build(); + } catch (AAIException e) { + //TODO check that the details here are sensible + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, e); + } catch (Exception e) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.GET, ex); + } + return response; + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/ExceptionHandler.java b/aai-resources/src/main/java/org/onap/aai/rest/ExceptionHandler.java new file mode 100644 index 0000000..c20a370 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/ExceptionHandler.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import java.util.ArrayList; +import java.util.List; + +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 org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.sun.istack.SAXParseException2; + +/** + * 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/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java new file mode 100644 index 0000000..a68d8f2 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/LegacyMoxyConsumer.java @@ -0,0 +1,597 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Encoded; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +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.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.apache.cxf.jaxrs.ext.PATCH; +import org.javatuples.Pair; +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.rest.db.DBRequest; +import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.exceptions.AAIInvalidXMLNamespace; +import org.onap.aai.rest.util.ValidateEncoding; +import org.onap.aai.restcore.HttpMethod; +import org.onap.aai.restcore.RESTAPI; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.workarounds.RemoveDME2QueryParams; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.base.Joiner; + + +/** + * The Class LegacyMoxyConsumer. + */ +@Path("{version: v[2789]|v1[01]}") +public class LegacyMoxyConsumer extends RESTAPI { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(LegacyMoxyConsumer.class.getName()); + protected static String authPolicyFunctionName = "REST"; + private ModelType introspectorFactoryType = ModelType.MOXY; + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + + /** + * Update. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PUT + @Path("/{uri: .+}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response update (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + MediaType mediaType = headers.getMediaType(); + + return this.handleWrites(mediaType, HttpMethod.PUT, content, versionParam, uri, headers, info); + } + + /** + * Update relationship. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PUT + @Path("/{uri: .+}/relationship-list/relationship") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response updateRelationship (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + MediaType inputMediaType = headers.getMediaType(); + Response response = null; + Loader loader = null; + TransactionalGraphEngine dbEngine = null; + boolean success = true; + + try { + validateRequest(info); + Version version = Version.valueOf(versionParam); + version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + loader = httpEntry.getLoader(); + dbEngine = httpEntry.getDbEngine(); + + URI uriObject = UriBuilder.fromPath(uri).build(); + this.validateURI(uriObject); + + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject); + + Introspector wrappedEntity = loader.unmarshal("relationship", content, org.onap.aai.restcore.MediaType.getEnum(this.getInputMediaType(inputMediaType))); + + DBRequest request = new DBRequest.Builder(HttpMethod.PUT_EDGE, uriObject, uriQuery, wrappedEntity, headers, info, transId).build(); + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + + response = responsesTuple.getValue1().get(0).getValue1(); + success = responsesTuple.getValue0(); + + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, e); + success = false; + } catch (Exception e) { + AAIException aaiException = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.PUT, aaiException); + success = false; + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + LOGGER.warn("Rolling back Titan transaction"); + dbEngine.rollback(); + } + } + + } + + return response; + } + + /** + * Patch. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PATCH + @Path("/{uri: .+}") + @Consumes({ "application/merge-patch+json" }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response patch (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + MediaType mediaType = MediaType.APPLICATION_JSON_TYPE; + + return this.handleWrites(mediaType, HttpMethod.MERGE_PATCH, content, versionParam, uri, headers, info); + + } + + /** + * Gets the legacy. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param depthParam the depth param + * @param cleanUp the clean up + * @param headers the headers + * @param info the info + * @param req the req + * @return the legacy + */ + @GET + @Path("/{uri: .+}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getLegacy (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @DefaultValue("all") @QueryParam("depth") String depthParam, @DefaultValue("false") @QueryParam("cleanup") String cleanUp, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + return this.getLegacy(content, versionParam, uri, depthParam, cleanUp, headers, info, req, new HashSet<String>()); + } + + /** + * This method exists as a workaround for filtering out undesired query params while routing between REST consumers + * + * @param content + * @param versionParam + * @param uri + * @param depthParam + * @param cleanUp + * @param headers + * @param info + * @param req + * @param removeQueryParams + * @return + */ + public Response getLegacy(String content, String versionParam, String uri, String depthParam, String cleanUp, HttpHeaders headers, UriInfo info, HttpServletRequest req, Set<String> removeQueryParams) { + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + Response response = null; + TransactionalGraphEngine dbEngine = null; + Loader loader = null; + + try { + validateRequest(info); + Version version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + final HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + dbEngine = httpEntry.getDbEngine(); + loader = httpEntry.getLoader(); + MultivaluedMap<String, String> params = info.getQueryParameters(); + + RemoveDME2QueryParams dme2Workaround = new RemoveDME2QueryParams(); + //clear out all params not used for filtering + params.remove("depth"); + params.remove("cleanup"); + params.remove("nodes-only"); + for (String queryParam : removeQueryParams) { + params.remove(queryParam); + } + if (dme2Workaround.shouldRemoveQueryParams(params)) { + dme2Workaround.removeQueryParams(params); + } + + uri = uri.split("\\?")[0]; + + URI uriObject = UriBuilder.fromPath(uri).build(); + + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject, params); + + String objType = ""; + if (!uriQuery.getContainerType().equals("")) { + objType = uriQuery.getContainerType(); + } else { + objType = uriQuery.getResultType(); + } + Introspector obj = loader.introspectorFromName(objType); + DBRequest request = + new DBRequest.Builder(HttpMethod.GET, uriObject, uriQuery, obj, headers, info, transId).build(); + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + + response = responsesTuple.getValue1().get(0).getValue1(); + + } 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) { + if (cleanUp.equals("true")) { + dbEngine.commit(); + } else { + dbEngine.rollback(); + } + } + } + + return response; + } + /** + * Delete. + * + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param resourceVersion the resource version + * @param req the req + * @return the response + */ + @DELETE + @Path("/{uri: .+}") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response delete (@PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @QueryParam("resource-version")String resourceVersion, @Context HttpServletRequest req) { + + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + + TransactionalGraphEngine dbEngine = null; + Response response = Response.status(404) + .type(outputMediaType).build(); + + boolean success = true; + + try { + + validateRequest(info); + Version version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + + dbEngine = httpEntry.getDbEngine(); + Loader loader = httpEntry.getLoader(); + + URI uriObject = UriBuilder.fromPath(uri).build(); + + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject); + String objType = uriQuery.getResultType(); + Introspector obj = loader.introspectorFromName(objType); + + DBRequest request = new DBRequest.Builder(HttpMethod.DELETE, uriObject, uriQuery, obj, headers, info, transId).build(); + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + + response = responsesTuple.getValue1().get(0).getValue1(); + success = responsesTuple.getValue0(); + + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.DELETE, e); + success = false; + } catch (Exception e) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.DELETE, ex); + success = false; + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + LOGGER.warn("Rolling back Titan transaction"); + dbEngine.rollback(); + } + } + } + + return response; + } + + /** + * This whole method does nothing because the body is being dropped while fielding the request. + * + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @DELETE + @Path("/{uri: .+}/relationship-list/relationship") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response deleteRelationship (String content, @PathParam("version")String versionParam, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + MediaType inputMediaType = headers.getMediaType(); + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + + Loader loader = null; + TransactionalGraphEngine dbEngine = null; + Response response = Response.status(404) + .type(outputMediaType).build(); + + boolean success = true; + + try { + this.validateRequest(info); + Version version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + loader = httpEntry.getLoader(); + dbEngine = httpEntry.getDbEngine(); + + if (content.equals("")) { + throw new AAIException("AAI_3102", "You must supply a relationship"); + } + URI uriObject = UriBuilder.fromPath(uri).build(); + this.validateURI(uriObject); + + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject); + + Introspector wrappedEntity = loader.unmarshal("relationship", content, org.onap.aai.restcore.MediaType.getEnum(this.getInputMediaType(inputMediaType))); + + DBRequest request = new DBRequest.Builder(HttpMethod.DELETE_EDGE, uriObject, uriQuery, wrappedEntity, headers, info, transId).build(); + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + + response = responsesTuple.getValue1().get(0).getValue1(); + success = responsesTuple.getValue0(); + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.DELETE, e); + success = false; + } catch (Exception e) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, HttpMethod.DELETE, ex); + success = false; + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + LOGGER.warn("Rolling back Titan transaction"); + dbEngine.rollback(); + } + } + } + + return response; + } + + /** + * Validate request. + * + * @param uri the uri + * @param headers the headers + * @param req the req + * @param action the action + * @param info the info + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private void validateRequest(UriInfo info) throws AAIException, UnsupportedEncodingException { + + if (!ValidateEncoding.getInstance().validate(info)) { + throw new AAIException("AAI_3008", "uri=" + getPath(info)); + } + } + + /** + * Gets the path. + * + * @param info the info + * @return the path + */ + private String getPath(UriInfo info) { + String path = info.getPath(false); + MultivaluedMap<String, String> map = info.getQueryParameters(false); + String params = "?"; + List<String> parmList = new ArrayList<>(); + for (String key : map.keySet()) { + for (String value : map.get(key)) { + parmList.add(key + "=" + value); + } + } + String queryParams = Joiner.on("&").join(parmList); + if (map.keySet().size() > 0) { + path += params + queryParams; + } + + return path; + + } + + /** + * Handle writes. + * + * @param aaiAction the aai action + * @param mediaType the media type + * @param method the method + * @param content the content + * @param versionParam the version param + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + private Response handleWrites(MediaType mediaType, HttpMethod method, String content, String versionParam, String uri, HttpHeaders headers, UriInfo info) { + + Response response = null; + TransactionalGraphEngine dbEngine = null; + Loader loader = null; + Version version = null; + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + Boolean success = true; + + try { + validateRequest(info); + + version = Version.valueOf(versionParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + loader = httpEntry.getLoader(); + dbEngine = httpEntry.getDbEngine(); + URI uriObject = UriBuilder.fromPath(uri).build(); + this.validateURI(uriObject); + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject); + String objName = uriQuery.getResultType(); + if (content.length() == 0) { + if (mediaType.toString().contains(MediaType.APPLICATION_JSON)) { + content = "{}"; + } else { + content = "<empty/>"; + } + } + Introspector obj = loader.unmarshal(objName, content, org.onap.aai.restcore.MediaType.getEnum(this.getInputMediaType(mediaType))); + if (obj == null) { + throw new AAIException("AAI_3000", "object could not be unmarshalled:" + content); + } + + if (mediaType.toString().contains(MediaType.APPLICATION_XML) && !content.equals("<empty/>") && isEmptyObject(obj)) { + throw new AAIInvalidXMLNamespace(content); + } + + this.validateIntrospector(obj, loader, uriObject, method); + + DBRequest request = + new DBRequest.Builder(method, uriObject, uriQuery, obj, headers, info, transId) + .rawRequestContent(content).build(); + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + + response = responsesTuple.getValue1().get(0).getValue1(); + success = responsesTuple.getValue0(); + } catch (AAIException e) { + response = consumerExceptionResponseGenerator(headers, info, method, e); + success = false; + } catch (Exception e ) { + AAIException ex = new AAIException("AAI_4000", e); + response = consumerExceptionResponseGenerator(headers, info, method, ex); + success = false; + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + LOGGER.warn("Rolling back Titan transaction"); + dbEngine.rollback(); + } + } + } + + return response; + } + + private void validateURI(URI uri) throws AAIException { + if (hasRelatedTo(uri)) { + throw new AAIException("AAI_3010"); + } + } + private boolean hasRelatedTo(URI uri) { + + return uri.toString().contains("/" + RestTokens.COUSIN + "/"); + } + + protected boolean isEmptyObject(Introspector obj) { + return "{}".equals(obj.marshal(false)); + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/URLFromVertexIdConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/URLFromVertexIdConsumer.java new file mode 100644 index 0000000..aa67557 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/URLFromVertexIdConsumer.java @@ -0,0 +1,121 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import java.net.URI; +import java.net.URL; +import java.util.Iterator; + +import javax.servlet.http.HttpServletRequest; +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.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import org.apache.tinkerpop.gremlin.structure.Vertex; + +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.rest.db.HttpEntry; +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.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.util.AAIConfig; +import org.onap.aai.workarounds.LegacyURITransformer; + +/** + * The Class URLFromVertexIdConsumer. + */ +@Path("{version: v[2789]|v1[01]}/generateurl") +public class URLFromVertexIdConsumer extends RESTAPI { + private ModelType introspectorFactoryType = ModelType.MOXY; + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + + private final String ID_ENDPOINT = "/id/{vertexid: \\d+}"; + + /** + * Generate url from vertex id. + * + * @param content the content + * @param versionParam the version param + * @param vertexid the vertexid + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @Path(ID_ENDPOINT) + @Produces({ MediaType.TEXT_PLAIN }) + public Response generateUrlFromVertexId(String content, @PathParam("version")String versionParam, @PathParam("vertexid")long vertexid, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + + Version version = Version.valueOf(versionParam); + StringBuilder result = new StringBuilder(); + Response response = null; + TransactionalGraphEngine dbEngine = null; + try { + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + dbEngine = httpEntry.getDbEngine(); + + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + + Iterator<Vertex> thisVertex = dbEngine.asAdmin().getTraversalSource().V(vertexid); + + if (!thisVertex.hasNext()) { + throw new AAIException("AAI_6114", "no node at that vertex id"); + } + URI uri = serializer.getURIForVertex(thisVertex.next()); + + result.append(uri.getRawPath()); + result.insert(0, version); + result.insert(0, AAIConfig.get("aai.server.url.base")); + LegacyURITransformer urlTransformer = LegacyURITransformer.getInstance(); + URI output = new URI(result.toString()); + + response = Response.ok().entity(result.toString()).status(Status.OK).type(MediaType.TEXT_PLAIN).build(); + } catch (AAIException e) { + //TODO check that the details here are sensible + 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 { //to close the titan transaction (I think) + if (dbEngine != null) { + dbEngine.rollback(); + } + } + return response; + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/VertexIdConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/VertexIdConsumer.java new file mode 100644 index 0000000..68a72e6 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/VertexIdConsumer.java @@ -0,0 +1,146 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +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 javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.javatuples.Pair; + +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.MarshallerProperties; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.rest.db.DBRequest; +import org.onap.aai.rest.db.HttpEntry; +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.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; + +/** + * The Class VertexIdConsumer. + */ +@Path("{version: v[2789]|v1[01]}/resources") +public class VertexIdConsumer extends RESTAPI { + + private ModelType introspectorFactoryType = ModelType.MOXY; + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + + private final String ID_ENDPOINT = "/id/{vertexid: \\d+}"; + + /** + * Gets the by vertex id. + * + * @param content the content + * @param versionParam the version param + * @param vertexid the vertexid + * @param depthParam the depth param + * @param headers the headers + * @param info the info + * @param req the req + * @return the by vertex id + */ + @GET + @Path(ID_ENDPOINT) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getByVertexId(String content, @PathParam("version")String versionParam, @PathParam("vertexid")long vertexid, @DefaultValue("all") @QueryParam("depth") String depthParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + Version version = Version.valueOf(versionParam); + Status status = Status.NOT_FOUND; + String result = ""; + Response response = null; + TransactionalGraphEngine dbEngine = null; + try { + int depth = setDepth(depthParam); + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + dbEngine = httpEntry.getDbEngine(); + Loader loader = httpEntry.getLoader(); + + DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); + + //get type of the object represented by the given id + Vertex thisVertex = null; + Iterator<Vertex> itr = dbEngine.asAdmin().getTraversalSource().V(vertexid); + + if (!itr.hasNext()) { + throw new AAIException("AAI_6114", "no node at that vertex id"); + } + thisVertex = itr.next(); + String objName = thisVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + + QueryParser query = dbEngine.getQueryBuilder(thisVertex).createQueryFromObjectName(objName); + + Introspector obj = loader.introspectorFromName(query.getResultType()); + + URI uriObject = UriBuilder.fromPath(info.getPath()).build(); + + DBRequest request = + new DBRequest.Builder(HttpMethod.GET, uriObject, query, obj, headers, info, transId) + .customMarshaller(new MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).includeRoot(true).build()).build(); + + List<DBRequest> requests = new ArrayList<>(); + requests.add(request); + Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(requests, sourceOfTruth); + response = responsesTuple.getValue1().get(0).getValue1(); + } 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 { //to close the titan transaction (I think) + if (dbEngine != null) { + dbEngine.rollback(); + } + } + return response; + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/exceptions/AAIInvalidXMLNamespace.java b/aai-resources/src/main/java/org/onap/aai/rest/exceptions/AAIInvalidXMLNamespace.java new file mode 100644 index 0000000..b9a8176 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/exceptions/AAIInvalidXMLNamespace.java @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.exceptions; + +import org.onap.aai.exceptions.AAIException; + +public class AAIInvalidXMLNamespace extends AAIException { + + private static final long serialVersionUID = 7487333042291858169L; + + public AAIInvalidXMLNamespace(String message) { + super("AAI_3011", message); + } + + public AAIInvalidXMLNamespace(Throwable cause) { + super("AAI_3011",cause); + } + + public AAIInvalidXMLNamespace(String message, Throwable cause) { + super("AAI_3011", cause, message); + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java new file mode 100644 index 0000000..0188142 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/retired/RetiredConsumer.java @@ -0,0 +1,144 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.retired; + +import java.util.ArrayList; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.cxf.jaxrs.ext.PATCH; + +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.restcore.RESTAPI; +import org.onap.aai.util.AAIConfig; + +/** + * The Class RetiredConsumer. + */ +public abstract class RetiredConsumer extends RESTAPI { + + /** + * Creates the message get. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @GET + @Path("/{uri:.*}") + public Response createMessageGet(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + /** + * Creates the message delete. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @DELETE + @Path("/{uri:.*}") + public Response createMessageDelete(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + /** + * Creates the message post. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @POST + @Path("/{uri:.*}") + public Response createMessagePost(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + @PATCH + @Path("/{uri:.*}") + public Response createMessagePatch(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + /** + * Creates the message put. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + @PUT + @Path("/{uri:.*}") + public Response createMessagePut(@PathParam("version")String versionParam, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) { + return createMessage(versionParam, headers, info, req); + } + + + /** + * Creates the message. + * + * @param versionParam the version param + * @param headers the headers + * @param info the info + * @param req the req + * @return the response + */ + private Response createMessage(String versionParam, HttpHeaders headers, UriInfo info, HttpServletRequest req) { + AAIException e = new AAIException("AAI_3007"); + + ArrayList<String> templateVars = new ArrayList<String>(); + + if (templateVars.size() == 0) { + templateVars.add("PUT"); + templateVars.add(info.getPath().toString()); + templateVars.add(versionParam); + templateVars.add(AAIConfig.get("aai.default.api.version", "")); + } + + Response response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, + templateVars)).build(); + + return response; + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/retired/V3ThroughV7Consumer.java b/aai-resources/src/main/java/org/onap/aai/rest/retired/V3ThroughV7Consumer.java new file mode 100644 index 0000000..a43e5c2 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/retired/V3ThroughV7Consumer.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.retired; + +import javax.ws.rs.Path; + +@Path("{version: v[3-6]}") //TODO re-add v7 when we fix our env issues AAI-8567 +public class V3ThroughV7Consumer extends RetiredConsumer { + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8Models.java b/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8Models.java new file mode 100644 index 0000000..26dea02 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8Models.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.retired; + +import javax.ws.rs.Path; + +@Path("{version: v[78]}/service-design-and-creation/models") +public class V7V8Models extends RetiredConsumer { + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8NamedQueries.java b/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8NamedQueries.java new file mode 100644 index 0000000..98be455 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/retired/V7V8NamedQueries.java @@ -0,0 +1,29 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.retired; + +import javax.ws.rs.Path; + +@Path("{version: v[78]}/service-design-and-creation/named-queries") +public class V7V8NamedQueries extends RetiredConsumer { + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/tools/ModelVersionTransformer.java b/aai-resources/src/main/java/org/onap/aai/rest/tools/ModelVersionTransformer.java new file mode 100644 index 0000000..eeaaf6a --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/tools/ModelVersionTransformer.java @@ -0,0 +1,410 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.tools; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.Encoded; +import javax.ws.rs.POST; +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.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dbmap.DBConnectionType; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.MarshallerProperties; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.introspection.Version; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.exceptions.AAIInvalidXMLNamespace; +import org.onap.aai.rest.util.ValidateEncoding; +import org.onap.aai.restcore.RESTAPI; +import org.onap.aai.serialization.db.EdgeType; +import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.google.common.base.Joiner; + + +/** + * The Class ModelVersionTransformer. + */ +@Path("tools") +public class ModelVersionTransformer extends RESTAPI { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ModelVersionTransformer.class.getName()); + protected static String authPolicyFunctionName = "REST"; + private ModelType introspectorFactoryType = ModelType.MOXY; + private QueryStyle queryStyle = QueryStyle.TRAVERSAL; + + + /** + * POST for model transformation. + * + * @param content the content + * @param uri the uri + * @param headers the headers + * @param info the info + * @param req the req + * @return the transformed model + * @Path("/{uri: modeltransform}") + * @throws UnsupportedEncodingException + */ + @POST + @Path("/{uri: modeltransform}") + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response modelTransform (String content, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo info, @Context HttpServletRequest req) throws UnsupportedEncodingException { + Response response = null; + TransactionalGraphEngine dbEngine = null; + Loader loader = null; + MediaType mediaType = headers.getMediaType(); + String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId"); + String transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + String realTime = headers.getRequestHeaders().getFirst("Real-Time"); + Boolean success = true; + AAIException ex; + + try { + validateRequest(info); + + DBConnectionType type = this.determineConnectionType(sourceOfTruth, realTime); + HttpEntry httpEntry = new HttpEntry(Version.v8, introspectorFactoryType, queryStyle, type); + loader = httpEntry.getLoader(); + dbEngine = httpEntry.getDbEngine(); + if (content.length() == 0) { + if (mediaType.toString().contains(MediaType.APPLICATION_JSON)) { + content = "{}"; + } else { + content = "<empty/>"; + } + } + + //Unmarshall the received model and store properties and values in a map. + Introspector obj = loader.unmarshal("Model", content, org.onap.aai.restcore.MediaType.getEnum(this.getInputMediaType(mediaType))); + if (obj == null) { + throw new AAIException("AAI_3000", "object could not be unmarshalled:" + content); + } + + if (mediaType.toString().contains(MediaType.APPLICATION_XML) && !content.equals("<empty/>") && isEmptyObject(obj)) { + throw new AAIInvalidXMLNamespace(content); + } + + Set<String> properties = obj.getProperties(); + java.util.Iterator<String> propItr = properties.iterator(); + + Map<String, Object> v8PropMap = new HashMap<String, Object>(); + while (propItr.hasNext()){ + String property = propItr.next(); + Object propertyValue = obj.getValue(property); + if (propertyValue != null) { + v8PropMap.put(propItr.next(), propertyValue); + } + } + // Get the current models and create a map of model-ver to model keys, this allows us + // to easily figure out and construct the relationships on the supplied v8 model + Map<String,String> modelVersionIdToModelInvariantIdMap = getCurrentModelsFromGraph(headers, transId, info); + + // Build the v10 - TODO + HttpEntry newHttpEntry = new HttpEntry(Version.v10, introspectorFactoryType, queryStyle, type); + Loader newLoader = newHttpEntry.getLoader(); + Introspector newModelObj = newLoader.introspectorFromName("Model"); + + // pull the attributes we need to apply to the model + model-ver objects + // model specific attrs + String oldModelInvariantId = obj.getValue("model-id"); + String oldModelType = obj.getValue("model-type"); + // model-ver specific + String oldModelName = obj.getValue("model-name"); + String oldModelVersionId = obj.getValue("model-name-version-id"); + String oldModelVersion = obj.getValue("model-version"); + + // copy attributes from the v8 model object to the v10 model object + newModelObj.setValue("model-invariant-id", oldModelInvariantId); + newModelObj.setValue("model-type", oldModelType); + + Introspector modelVersObj = newModelObj.newIntrospectorInstanceOfProperty("model-vers"); + + newModelObj.setValue("model-vers", modelVersObj.getUnderlyingObject()); + + List<Object> modelVerList = (List<Object>)modelVersObj.getValue("model-ver"); + + //create a model-ver object + Introspector modelVerObj = newLoader.introspectorFromName("ModelVer"); + // load attributes from the v8 model object into the v10 model-ver object + modelVerObj.setValue("model-version-id", oldModelVersionId); + modelVerObj.setValue("model-name", oldModelName); + modelVerObj.setValue("model-version", oldModelVersion); + + + if (obj.hasProperty("model-elements")) { + Introspector oldModelElements = obj.getWrappedValue("model-elements"); + if (oldModelElements != null) { + Introspector newModelElements = modelVerObj.newIntrospectorInstanceOfProperty("model-elements"); + modelVerObj.setValue("model-elements", newModelElements.getUnderlyingObject()); + repackModelElements(oldModelElements, newModelElements, modelVersionIdToModelInvariantIdMap); + } + } + + modelVerList.add(modelVerObj.getUnderlyingObject()); + + String outputMediaType = getMediaType(headers.getAcceptableMediaTypes()); + MarshallerProperties marshallerProperties = + new MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build(); + + String result = newModelObj.marshal(marshallerProperties); + response = Response.ok(result).build(); + + } catch (AAIException e) { + + ArrayList<String> templateVars = new ArrayList<String>(2); + templateVars.add("POST modeltransform"); + templateVars.add("model-ver.model-version-id"); + response = Response + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse( + headers.getAcceptableMediaTypes(), e, + templateVars)).build(); + success = false; + } catch (Exception e) { + ArrayList<String> templateVars = new ArrayList<String>(2); + templateVars.add("POST modeltransform"); + templateVars.add("model-ver.model-version-id"); + ex = new AAIException("AAI_4000", e); + response = Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars)) + .build(); + e.printStackTrace(); + success = false; + } finally { + if (dbEngine != null) { + if (success) { + dbEngine.commit(); + } else { + dbEngine.rollback(); + } + } + } + return response; + } + + + private void repackModelElements(Introspector oldModelElements, Introspector newModelElements, Map<String, String> modelVersionIdToModelInvariantIdMap) + throws AAIUnknownObjectException, AAIException { + + List<Introspector> oldModelElementList = oldModelElements.getWrappedListValue("model-element"); + List<Object> newModelElementList = (List<Object>)newModelElements.getValue("model-element"); + + for (Introspector oldModelElement : oldModelElementList) { + Introspector newModelElement = newModelElements.getLoader().introspectorFromName("model-element"); + + ArrayList<String> attrs = new ArrayList<String>(); + + attrs.add("model-element-uuid"); + attrs.add("new-data-del-flag"); + attrs.add("cardinality"); + attrs.add("linkage-points"); + + for (String attr : attrs) { + if (oldModelElement.hasProperty(attr)) { + newModelElement.setValue(attr, oldModelElement.getValue(attr)); + } + } + + if (oldModelElement.hasProperty("relationship-list")) { + + Introspector oldRelationshipList = oldModelElement.getWrappedValue("relationship-list"); + Introspector newRelationshipList = newModelElements.getLoader().introspectorFromName("relationship-list"); + newModelElement.setValue("relationship-list", newRelationshipList.getUnderlyingObject()); + + List<Introspector> oldRelationshipListList = oldRelationshipList.getWrappedListValue("relationship"); + List<Object> newRelationshipListList = (List<Object>)newRelationshipList.getValue("relationship"); + + for (Introspector oldRelationship : oldRelationshipListList) { + + Introspector newRelationship = newModelElements.getLoader().introspectorFromName("relationship"); + newRelationshipListList.add(newRelationship.getUnderlyingObject()); + + List<Introspector> oldRelationshipData = oldRelationship.getWrappedListValue("relationship-data"); + List<Object> newRelationshipData = (List<Object>)newRelationship.getValue("relationship-data"); + + newRelationship.setValue("related-to", "model-ver"); + + for (Introspector oldRelationshipDatum : oldRelationshipData) { + + String oldProp = null; + String oldVal = null; + + if (oldRelationshipDatum.hasProperty("relationship-key")) { + oldProp = oldRelationshipDatum.getValue("relationship-key"); + } + if (oldRelationshipDatum.hasProperty("relationship-value")) { + oldVal = oldRelationshipDatum.getValue("relationship-value"); + } + + if ("model.model-name-version-id".equals(oldProp)) { + // make two new relationshipDatum for use w/ the new style model + + // you should have the model in the list of models we collected earlier + if (modelVersionIdToModelInvariantIdMap.containsKey(oldVal)) { + Introspector newRelationshipDatum1 = newModelElements.getLoader().introspectorFromName("relationship-data"); + Introspector newRelationshipDatum2 = newModelElements.getLoader().introspectorFromName("relationship-data"); + + String modelId = modelVersionIdToModelInvariantIdMap.get(oldVal); + + // the first one points at the model-invariant-id of found model + newRelationshipDatum1.setValue("relationship-key", "model.model-invariant-id"); + newRelationshipDatum1.setValue("relationship-value", modelId); + + // the second one points at the model-version-id which corresponds to the old model-name-version-id + newRelationshipDatum2.setValue("relationship-key", "model-ver.model-version-id"); + newRelationshipDatum2.setValue("relationship-value", oldVal); + + newRelationshipData.add(newRelationshipDatum1.getUnderlyingObject()); + newRelationshipData.add(newRelationshipDatum2.getUnderlyingObject()); + } else { + throw new AAIException("AAI_6114", "No model-ver found using model-ver.model-version-id=" + oldVal); + } + } + } + + } + } + + if (oldModelElement.hasProperty("model-elements")) { + Introspector nextOldModelElements = oldModelElement.getWrappedValue("model-elements"); + if (nextOldModelElements != null) { + Introspector nextNewModelElements = newModelElement.newIntrospectorInstanceOfProperty("model-elements"); + newModelElement.setValue("model-elements", nextNewModelElements.getUnderlyingObject()); + repackModelElements(nextOldModelElements, nextNewModelElements, modelVersionIdToModelInvariantIdMap); + } + } + newModelElementList.add(newModelElement.getUnderlyingObject()); + } + return; + + } + + private Map<String, String> getCurrentModelsFromGraph(HttpHeaders headers, String transactionId, UriInfo info) throws NoEdgeRuleFoundException, AAIException { + + TransactionalGraphEngine dbEngine = null; + Map<String, String> modelVerModelMap = new HashMap<String,String>() ; + try { + + Version version = AAIProperties.LATEST; + DBConnectionType type = DBConnectionType.REALTIME; + + final HttpEntry httpEntry = new HttpEntry(version, introspectorFactoryType, queryStyle, type); + dbEngine = httpEntry.getDbEngine(); + + List<Vertex> modelVertices = dbEngine.asAdmin().getTraversalSource().V().has(AAIProperties.NODE_TYPE,"model").toList(); + for (Vertex modelVtx : modelVertices) { + + List<Vertex> modelVerVerts = dbEngine.getQueryBuilder(modelVtx).createEdgeTraversal(EdgeType.TREE, "model", "model-ver").toList(); + for (Vertex v : modelVerVerts) { + modelVerModelMap.put(v.value("model-version-id"), modelVtx.value("model-invariant-id")); + } + } + } catch (NoSuchElementException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (Exception e1) { + e1.printStackTrace(); + } + return modelVerModelMap; + + } + + /** + * Validate request. + * + * @param uri the uri + * @param headers the headers + * @param req the req + * @param action the action + * @param info the info + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private void validateRequest(UriInfo info) throws AAIException, UnsupportedEncodingException { + + if (!ValidateEncoding.getInstance().validate(info)) { + throw new AAIException("AAI_3008", "uri=" + getPath(info)); + } + } + + /** + * Gets the path. + * + * @param info the info + * @return the path + */ + private String getPath(UriInfo info) { + String path = info.getPath(false); + MultivaluedMap<String, String> map = info.getQueryParameters(false); + String params = "?"; + List<String> parmList = new ArrayList<>(); + for (String key : map.keySet()) { + for (String value : map.get(key)) { + parmList.add(key + "=" + value); + } + } + String queryParams = Joiner.on("&").join(parmList); + if (map.keySet().size() > 0) { + path += params + queryParams; + } + + return path; + + } + + protected boolean isEmptyObject(Introspector obj) { + return "{}".equals(obj.marshal(false)); + } + + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java new file mode 100644 index 0000000..55a07e4 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java @@ -0,0 +1,122 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.util; + +import java.util.ArrayList; +import java.util.HashMap; + +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 org.onap.aai.exceptions.AAIException; +import org.onap.aai.logging.ErrorLogHelper; +import org.onap.aai.restcore.RESTAPI; + +/** + * The Class EchoResponse. + */ +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(echoPath) + 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; + } + +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/util/LogFormatTools.java b/aai-resources/src/main/java/org/onap/aai/rest/util/LogFormatTools.java new file mode 100644 index 0000000..cfda0c3 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/util/LogFormatTools.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.util; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class LogFormatTools { + + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern(DATE_FORMAT) + .withZone(ZoneOffset.UTC); + + public static String getCurrentDateTime() { + return DTF.format(ZonedDateTime.now()); + } +} diff --git a/aai-resources/src/main/java/org/onap/aai/rest/util/ValidateEncoding.java b/aai-resources/src/main/java/org/onap/aai/rest/util/ValidateEncoding.java new file mode 100644 index 0000000..7d4b314 --- /dev/null +++ b/aai-resources/src/main/java/org/onap/aai/rest/util/ValidateEncoding.java @@ -0,0 +1,161 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.rest.util; + +import java.io.UnsupportedEncodingException; +import java.net.URI; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; + +import org.springframework.web.util.UriUtils; + +/** + * The Class ValidateEncoding. + */ +public class ValidateEncoding { + + private final String encoding = "UTF-8"; + + /** + * Instantiates a new validate encoding. + */ + private ValidateEncoding() { + + } + + /** + * The Class Helper. + */ + private static class Helper { + + /** The Constant INSTANCE. */ + private static final ValidateEncoding INSTANCE = new ValidateEncoding(); + } + + /** + * Gets the single instance of ValidateEncoding. + * + * @return single instance of ValidateEncoding + */ + public static ValidateEncoding getInstance() { + return Helper.INSTANCE; + } + + /** + * Validate. + * + * @param uri the uri + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public boolean validate(URI uri) throws UnsupportedEncodingException { + boolean result = true; + if (!validatePath(uri.getRawPath())) { + result = false; + } + /*if (!validateQueryParams(uri.getRawQuery())) { + result = false; + } //TODO + */ + + return result; + } + + /** + * Validate. + * + * @param info the info + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + public boolean validate(UriInfo info) throws UnsupportedEncodingException { + boolean result = true; + if (!validatePath(info.getPath(false))) { + result = false; + } + if (!validateQueryParams(info.getQueryParameters(false))) { + result = false; + } + + return result; + } + + /** + * Validate path. + * + * @param path the path + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean validatePath(String path) throws UnsupportedEncodingException { + String[] segments = path.split("/"); + boolean valid = true; + for (String segment : segments) { + if (!this.checkEncoding(segment)) { + valid = false; + } + } + + return valid; + + } + + /** + * Validate query params. + * + * @param params the params + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean validateQueryParams(MultivaluedMap<String, String> params) throws UnsupportedEncodingException { + boolean valid = true; + + for (String key : params.keySet()) { + if (!this.checkEncoding(key)) { + valid = false; + } + for (String item : params.get(key)) { + if (!this.checkEncoding(item)) { + valid = false; + } + } + } + return valid; + } + + /** + * Check encoding. + * + * @param segment the segment + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private boolean checkEncoding(String segment) throws UnsupportedEncodingException { + boolean result = false; + String decode = UriUtils.decode(segment, encoding); + String encode = UriUtils.encode(decode, encoding); + result = segment.equals(encode); + + return result; + } +} |