Fix sonar issues in /crud/service/CrudRestService
[aai/gizmo.git] / src / main / java / org / onap / crud / service / CrudRestService.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 package org.onap.crud.service;
22
23 import java.security.cert.X509Certificate;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import javax.security.auth.x500.X500Principal;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.ws.rs.Consumes;
33 import javax.ws.rs.DELETE;
34 import javax.ws.rs.Encoded;
35 import javax.ws.rs.GET;
36 import javax.ws.rs.POST;
37 import javax.ws.rs.PUT;
38 import javax.ws.rs.Path;
39 import javax.ws.rs.PathParam;
40 import javax.ws.rs.Produces;
41 import javax.ws.rs.core.Context;
42 import javax.ws.rs.core.EntityTag;
43 import javax.ws.rs.core.HttpHeaders;
44 import javax.ws.rs.core.MediaType;
45 import javax.ws.rs.core.Response;
46 import javax.ws.rs.core.Response.Status;
47 import javax.ws.rs.core.UriInfo;
48 import org.apache.commons.lang3.tuple.ImmutablePair;
49 import org.apache.cxf.jaxrs.ext.PATCH;
50 import org.onap.aai.cl.api.Logger;
51 import org.onap.aai.cl.eelf.LoggerFactory;
52 import org.onap.aaiauth.auth.Auth;
53 import org.onap.crud.exception.CrudException;
54 import org.onap.crud.logging.CrudServiceMsgs;
55 import org.onap.crud.logging.LoggingUtil;
56 import org.onap.crud.util.CrudProperties;
57 import org.onap.crud.util.CrudServiceConstants;
58 import org.onap.crud.util.CrudServiceUtil;
59 import org.slf4j.MDC;
60 import com.google.gson.JsonElement;
61
62 @Path("/services/inventory")
63 public class CrudRestService {
64
65   private AbstractGraphDataService graphDataService;
66   Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName());
67   Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName());
68   private Auth auth;
69
70   private String mediaType = MediaType.APPLICATION_JSON;
71   public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override";
72
73   public CrudRestService(AbstractGraphDataService graphDataService) throws Exception {
74     this.graphDataService = graphDataService;
75     this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE);
76   }
77
78   // For unit testing
79   public CrudRestService(AbstractGraphDataService graphDataService, Auth auth) throws Exception {
80     this.graphDataService = graphDataService;
81     this.auth = auth;
82   }
83
84   public enum Action {
85     POST, GET, PUT, DELETE, PATCH
86   }
87
88   public void startup() {
89
90   }
91
92   @GET
93   @Path("/{version}/{type}/{id}")
94   @Consumes({MediaType.APPLICATION_JSON})
95   @Produces({MediaType.APPLICATION_JSON})
96   public Response getVertex(String content, @PathParam("version") String version, @PathParam("type") String type,
97                             @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
98                             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
99     LoggingUtil.initMdcContext(req, headers);
100
101     logger.debug("Incoming request..." + content);
102     Response response = null;
103
104     Map<String, String> params = addParams ( uriInfo, false, type, version );
105
106     try {
107       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
108           ImmutablePair<EntityTag, String> result = graphDataService.getVertex(version, id, type, params);
109         response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
110       } else {
111         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
112       }
113     } catch (CrudException ce) {
114       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
115     } catch (Exception e) {
116       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
117
118     }
119
120     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
121     return response;
122   }
123
124   @GET
125   @Path("/{version}/{type}/")
126   @Consumes({MediaType.APPLICATION_JSON})
127   @Produces({MediaType.APPLICATION_JSON})
128   public Response getVertices(String content, @PathParam("version") String version, @PathParam("type") String type,
129                               @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
130                               @Context HttpServletRequest req) {
131
132     LoggingUtil.initMdcContext(req, headers);
133
134     logger.debug("Incoming request..." + content);
135     Response response = null;
136     try {
137       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
138         String propertiesKey = CrudProperties.get(CrudServiceConstants.CRD_COLLECTION_PROPERTIES_KEY);
139         Map<String, String> filter = addParams ( uriInfo, true, type, version );
140
141         HashSet<String> properties;
142         if (uriInfo.getQueryParameters().containsKey(propertiesKey)) {
143           properties = new HashSet<>(uriInfo.getQueryParameters().get(propertiesKey));
144         } else {
145           properties = new HashSet<>();
146         }
147
148         ImmutablePair<EntityTag, String> result = graphDataService.getVertices(version, type, filter, properties);
149         response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
150       } else {
151         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
152       }
153     } catch (CrudException ce) {
154       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
155     } catch (Exception e) {
156       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
157
158     }
159
160     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
161     return response;
162   }
163
164   @GET
165   @Path("/relationships/{version}/{type}/{id}")
166   @Consumes({MediaType.APPLICATION_JSON})
167   @Produces({MediaType.APPLICATION_JSON})
168   public Response getEdge(String content, @PathParam("version") String version, @PathParam("type") String type,
169                           @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
170                           @Context UriInfo uriInfo, @Context HttpServletRequest req) {
171     LoggingUtil.initMdcContext(req, headers);
172
173     logger.debug("Incoming request..." + content);
174     Response response = null;
175
176     Map<String, String> params = addParams ( uriInfo, false, type, version );
177
178     try {
179       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
180
181         ImmutablePair<EntityTag, String> result = graphDataService.getEdge(version, id, type, params);
182         response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
183       } else {
184         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
185       }
186     } catch (CrudException ce) {
187       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
188     } catch (Exception e) {
189       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
190     }
191
192     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
193     return response;
194   }
195
196   @GET
197   @Path("/relationships/{version}/{type}/")
198   @Consumes({MediaType.APPLICATION_JSON})
199   @Produces({MediaType.APPLICATION_JSON})
200   public Response getEdges(String content, @PathParam("version") String version, @PathParam("type") String type,
201                            @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
202                            @Context HttpServletRequest req) {
203
204     LoggingUtil.initMdcContext(req, headers);
205
206     logger.debug("Incoming request..." + content);
207     Response response = null;
208     Map<String, String> filter = addParams ( uriInfo, true, type, version );
209
210     try {
211       if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
212           ImmutablePair<EntityTag, String> result = graphDataService.getEdges(version, type, filter);
213           response = Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
214       } else {
215         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
216       }
217     } catch (CrudException ce) {
218       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
219     } catch (Exception e) {
220       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
221     }
222
223     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
224     return response;
225   }
226
227   @PUT
228   @Path("/relationships/{version}/{type}/{id}")
229   @Consumes({MediaType.APPLICATION_JSON})
230   @Produces({MediaType.APPLICATION_JSON})
231   public Response updateEdge(String content, @PathParam("version") String version, @PathParam("type") String type,
232                              @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
233                              @Context UriInfo uriInfo, @Context HttpServletRequest req) {
234
235     LoggingUtil.initMdcContext(req, headers);
236
237     logger.debug("Incoming request..." + content);
238     Response response = null;
239
240
241     try {
242       if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
243         EdgePayload payload = EdgePayload.fromJson(content);
244         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
245           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
246         }
247         if (payload.getId() != null && !payload.getId().equals(id)) {
248           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
249         }
250         ImmutablePair<EntityTag, String> result;
251         if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
252             && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
253           result = graphDataService.patchEdge(version, id, type, payload);
254           response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
255         } else {
256           result = graphDataService.updateEdge(version, id, type, payload);
257           response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
258         }
259         
260       } else {
261         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
262       }
263     } catch (CrudException ce) {
264       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
265     } catch (Exception e) {
266       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
267     }
268
269     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
270     return response;
271   }
272
273   @PATCH
274   @Path("/relationships/{version}/{type}/{id}")
275   @Consumes({"application/merge-patch+json"})
276   @Produces({MediaType.APPLICATION_JSON})
277   public Response patchEdge(String content, @PathParam("version") String version, @PathParam("type") String type,
278                             @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
279                             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
280
281     LoggingUtil.initMdcContext(req, headers);
282
283     logger.debug("Incoming request..." + content);
284     Response response = null;
285
286     try {
287       if (validateRequest(req, uri, content, Action.PATCH, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
288         EdgePayload payload = EdgePayload.fromJson(content);
289         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
290           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
291         }
292         if (payload.getId() != null && !payload.getId().equals(id)) {
293           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
294         }
295
296         ImmutablePair<EntityTag, String> result = graphDataService.patchEdge(version, id, type, payload);
297         response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
298       } else {
299         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
300       }
301     } catch (CrudException ce) {
302       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
303     } catch (Exception e) {
304       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
305     }
306
307     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
308     return response;
309   }
310
311   @PUT
312   @Path("/{version}/{type}/{id}")
313   @Consumes({MediaType.APPLICATION_JSON})
314   @Produces({MediaType.APPLICATION_JSON})
315   public Response updateVertex(String content, @PathParam("version") String version, @PathParam("type") String type,
316                                @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
317                                @Context UriInfo uriInfo, @Context HttpServletRequest req) {
318
319     LoggingUtil.initMdcContext(req, headers);
320
321     logger.debug("Incoming request..." + content);
322     Response response = null;
323
324     try {
325       if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
326         VertexPayload payload = VertexPayload.fromJson(content);
327         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
328           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
329         }
330         if (payload.getId() != null && !payload.getId().equals(id)) {
331           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
332         }
333
334         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
335
336         ImmutablePair<EntityTag, String> result;
337         if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
338             && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
339           result = graphDataService.patchVertex(version, id, type, payload);
340           response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
341         } else {
342           result = graphDataService.updateVertex(version, id, type, payload);
343           response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
344         }
345         
346       } else {
347         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
348       }
349     } catch (CrudException ce) {
350       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
351     } catch (Exception e) {
352       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
353     }
354
355     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
356     return response;
357   }
358
359   @PATCH
360   @Path("/{version}/{type}/{id}")
361   @Consumes({"application/merge-patch+json"})
362   @Produces({MediaType.APPLICATION_JSON})
363   public Response patchVertex(String content, @PathParam("version") String version, @PathParam("type") String type,
364                               @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
365                               @Context UriInfo uriInfo, @Context HttpServletRequest req) {
366
367     LoggingUtil.initMdcContext(req, headers);
368
369     logger.debug("Incoming request..." + content);
370     Response response = null;
371
372     try {
373       if (validateRequest(req, uri, content, Action.PATCH, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
374         VertexPayload payload = VertexPayload.fromJson(content);
375         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
376           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
377         }
378         if (payload.getId() != null && !payload.getId().equals(id)) {
379           throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
380         }
381
382         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
383
384         ImmutablePair<EntityTag, String> result = graphDataService.patchVertex(version, id, type, payload);
385         response = Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey()).build();
386       } else {
387         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
388       }
389     } catch (CrudException ce) {
390       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
391     } catch (Exception e) {
392       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
393     }
394
395     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
396     return response;
397   }
398
399   @POST
400   @Path("/{version}/{type}/")
401   @Consumes({MediaType.APPLICATION_JSON})
402   @Produces({MediaType.APPLICATION_JSON})
403   public Response addVertex(String content, @PathParam("version") String version, @PathParam("type") String type,
404                             @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
405                             @Context HttpServletRequest req) {
406
407     LoggingUtil.initMdcContext(req, headers);
408
409     logger.debug("Incoming request..." + content);
410     Response response = null;
411
412
413     try {
414       if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
415         VertexPayload payload = VertexPayload.fromJson(content);
416         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
417           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
418         }
419         if (payload.getId() != null) {
420           throw new CrudException("ID specified , use Http PUT to update Vertex", Status.BAD_REQUEST);
421         }
422
423         if (payload.getType() != null && !payload.getType().equals(type)) {
424           throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST);
425         }
426
427         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
428
429         ImmutablePair<EntityTag, String> result = graphDataService.addVertex(version, type, payload);
430         response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
431       } else {
432         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
433       }
434     } catch (CrudException ce) {
435       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
436     } catch (Exception e) {
437       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
438     }
439
440     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
441     return response;
442   }
443
444   private void validateBulkPayload(BulkPayload payload) throws CrudException {
445     List<String> vertices = new ArrayList<String>();
446     List<String> edges = new ArrayList<String>();
447
448     for (JsonElement v : payload.getObjects()) {
449       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
450           v.getAsJsonObject().entrySet());
451
452       if (entries.size() != 2) {
453         throw new CrudException("", Status.BAD_REQUEST);
454       }
455       Map.Entry<String, JsonElement> opr = entries.get(0);
456       Map.Entry<String, JsonElement> item = entries.get(1);
457
458       if (vertices.contains(item.getKey())) {
459         throw new CrudException("duplicate vertex in payload: " + item.getKey(), Status.BAD_REQUEST);
460       }
461       VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString());
462       if (vertexPayload.getType() == null) {
463         throw new CrudException("Vertex Type cannot be null for: " + item.getKey(), Status.BAD_REQUEST);
464       }
465
466       if (!opr.getKey().equalsIgnoreCase("operation")) {
467         throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST);
468       }
469
470       if (!opr.getValue().getAsString().equalsIgnoreCase("add")
471           && !opr.getValue().getAsString().equalsIgnoreCase("modify")
472           && !opr.getValue().getAsString().equalsIgnoreCase("patch")
473           && !opr.getValue().getAsString().equalsIgnoreCase("delete")) {
474         throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST);
475       }
476       // check if ID is populate for modify/patch/delete operation
477       if ((opr.getValue().getAsString().equalsIgnoreCase("modify")
478           || opr.getValue().getAsString().equalsIgnoreCase("patch")
479           || opr.getValue().getAsString().equalsIgnoreCase("delete")) && (vertexPayload.getId() == null)) {
480
481         throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST);
482
483       }
484
485       vertices.add(item.getKey());
486     }
487
488     for (JsonElement v : payload.getRelationships()) {
489       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
490           v.getAsJsonObject().entrySet());
491
492       if (entries.size() != 2) {
493         throw new CrudException("", Status.BAD_REQUEST);
494       }
495       Map.Entry<String, JsonElement> opr = entries.get(0);
496       Map.Entry<String, JsonElement> item = entries.get(1);
497
498       if (edges.contains(item.getKey())) {
499         throw new CrudException("duplicate Edge in payload: " + item.getKey(), Status.BAD_REQUEST);
500       }
501
502       EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString());
503
504       if (edgePayload.getType() == null) {
505         throw new CrudException("Edge Type cannot be null for: " + item.getKey(), Status.BAD_REQUEST);
506       }
507
508       if (!opr.getKey().equalsIgnoreCase("operation")) {
509         throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST);
510       }
511
512       if (!opr.getValue().getAsString().equalsIgnoreCase("add")
513           && !opr.getValue().getAsString().equalsIgnoreCase("modify")
514           && !opr.getValue().getAsString().equalsIgnoreCase("patch")
515           && !opr.getValue().getAsString().equalsIgnoreCase("delete")) {
516         throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST);
517       }
518       // check if ID is populate for modify/patch/delete operation
519       if ((edgePayload.getId() == null) && (opr.getValue().getAsString().equalsIgnoreCase("modify")
520           || opr.getValue().getAsString().equalsIgnoreCase("patch")
521           || opr.getValue().getAsString().equalsIgnoreCase("delete"))) {
522
523         throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST);
524
525       }
526       if (opr.getValue().getAsString().equalsIgnoreCase("add")) {
527         if (edgePayload.getSource() == null || edgePayload.getTarget() == null) {
528           throw new CrudException("Source/Target cannot be null for edge: " + item.getKey(), Status.BAD_REQUEST);
529         }
530         if (edgePayload.getSource().startsWith("$") && !vertices.contains(edgePayload.getSource().substring(1))) {
531           throw new CrudException(
532               "Source Vertex " + edgePayload.getSource().substring(1) + " not found for Edge: " + item.getKey(),
533               Status.BAD_REQUEST);
534         }
535
536         if (edgePayload.getTarget().startsWith("$") && !vertices.contains(edgePayload.getTarget().substring(1))) {
537           throw new CrudException(
538               "Target Vertex " + edgePayload.getSource().substring(1) + " not found for Edge: " + item.getKey(),
539               Status.BAD_REQUEST);
540         }
541       }
542       edges.add(item.getKey());
543
544     }
545
546   }
547
548   @POST
549   @Path("/{version}/bulk/")
550   @Consumes({MediaType.APPLICATION_JSON})
551   @Produces({MediaType.APPLICATION_JSON})
552   public Response addBulk(String content, @PathParam("version") String version, @PathParam("type") String type,
553                           @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
554                           @Context HttpServletRequest req) {
555
556     LoggingUtil.initMdcContext(req, headers);
557
558     logger.debug("Incoming request..." + content);
559     Response response = null;
560
561
562     try {
563       if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
564         BulkPayload payload = BulkPayload.fromJson(content);
565         if ((payload.getObjects() == null && payload.getRelationships() == null)
566             || (payload.getObjects() != null && payload.getObjects().isEmpty() && payload.getRelationships() != null
567             && payload.getRelationships().isEmpty())) {
568           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
569         }
570
571         validateBulkPayload(payload);
572         String result = graphDataService.addBulk(version, payload, headers);
573         response = Response.status(Status.OK).entity(result).type(mediaType).build();
574       } else {
575         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
576       }
577     } catch (CrudException ce) {
578       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
579     } catch (Exception e) {
580       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
581     }
582
583     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
584     return response;
585   }
586
587   @POST
588   @Path("/{version}/")
589   @Consumes({MediaType.APPLICATION_JSON})
590   @Produces({MediaType.APPLICATION_JSON})
591   public Response addVertex(String content, @PathParam("version") String version, @PathParam("uri") @Encoded String uri,
592                             @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
593
594     LoggingUtil.initMdcContext(req, headers);
595
596     logger.debug("Incoming request..." + content);
597     Response response = null;
598
599     try {
600
601       if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
602         VertexPayload payload = VertexPayload.fromJson(content);
603         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
604           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
605         }
606         if (payload.getId() != null) {
607           throw new CrudException("ID specified , use Http PUT to update Vertex", Status.BAD_REQUEST);
608         }
609
610         if (payload.getType() == null || payload.getType().isEmpty()) {
611           throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST);
612         }
613
614         payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
615
616         ImmutablePair<EntityTag, String> result = graphDataService.addVertex(version, payload.getType(), payload);
617         response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
618       } else {
619         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
620       }
621     } catch (CrudException ce) {
622       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
623     } catch (Exception e) {
624       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
625     }
626
627     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
628     return response;
629   }
630
631   @POST
632   @Path("/relationships/{version}/{type}/")
633   @Consumes({MediaType.APPLICATION_JSON})
634   @Produces({MediaType.APPLICATION_JSON})
635   public Response addEdge(String content, @PathParam("version") String version, @PathParam("type") String type,
636                           @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
637                           @Context HttpServletRequest req) {
638
639     LoggingUtil.initMdcContext(req, headers);
640
641     logger.debug("Incoming request..." + content);
642     Response response = null;
643
644
645     try {
646       if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
647         EdgePayload payload = EdgePayload.fromJson(content);
648         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
649           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
650         }
651         if (payload.getId() != null) {
652           throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
653         }
654
655         if (payload.getType() != null && !payload.getType().equals(type)) {
656           throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);
657         }
658         ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, type, payload);
659         response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
660       } else {
661         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
662       }
663     } catch (CrudException ce) {
664       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
665     } catch (Exception e) {
666       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
667     }
668
669     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
670     return response;
671   }
672
673   @POST
674   @Path("/relationships/{version}/")
675   @Consumes({MediaType.APPLICATION_JSON})
676   @Produces({MediaType.APPLICATION_JSON})
677   public Response addEdge(String content, @PathParam("version") String version, @PathParam("uri") @Encoded String uri,
678                           @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
679
680     LoggingUtil.initMdcContext(req, headers);
681
682     logger.debug("Incoming request..." + content);
683     Response response = null;
684
685
686     try {
687       if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
688         EdgePayload payload = EdgePayload.fromJson(content);
689         if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
690           throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
691         }
692         if (payload.getId() != null) {
693           throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
694         }
695
696         if (payload.getType() == null || payload.getType().isEmpty()) {
697           throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST);
698         }
699         ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, payload.getType(), payload);
700         response = Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType).build();
701       } else {
702         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
703       }
704     } catch (CrudException ce) {
705       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
706     } catch (Exception e) {
707       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
708     }
709
710     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
711     return response;
712   }
713
714   @DELETE
715   @Path("/{version}/{type}/{id}")
716   @Consumes({MediaType.APPLICATION_JSON})
717   @Produces({MediaType.APPLICATION_JSON})
718   public Response deleteVertex(String content, @PathParam("version") String version, @PathParam("type") String type,
719                                @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
720                                @Context UriInfo uriInfo, @Context HttpServletRequest req) {
721
722     LoggingUtil.initMdcContext(req, headers);
723
724     logger.debug("Incoming request..." + content);
725     Response response = null;
726
727
728     try {
729       if (validateRequest(req, uri, content, Action.DELETE, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
730         String result = graphDataService.deleteVertex(version, id, type);
731         response = Response.status(Status.OK).entity(result).type(mediaType).build();
732       } else {
733         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
734       }
735     } catch (CrudException ce) {
736       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
737     } catch (Exception e) {
738       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
739     }
740
741     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
742     return response;
743   }
744
745   @DELETE
746   @Path("/relationships/{version}/{type}/{id}")
747   @Consumes({MediaType.APPLICATION_JSON})
748   @Produces({MediaType.APPLICATION_JSON})
749   public Response deleteEdge(String content, @PathParam("version") String version, @PathParam("type") String type,
750                              @PathParam("id") String id, @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers,
751                              @Context UriInfo uriInfo, @Context HttpServletRequest req) {
752
753     LoggingUtil.initMdcContext(req, headers);
754
755     logger.debug("Incoming request..." + content);
756     Response response = null;
757
758     try {
759       if (validateRequest(req, uri, content, Action.DELETE, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
760         String result = graphDataService.deleteEdge(version, id, type);
761         response = Response.status(Status.OK).entity(result).type(mediaType).build();
762       } else {
763         response = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON).build();
764       }
765     } catch (CrudException ce) {
766       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
767     } catch (Exception e) {
768       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
769     }
770
771     LoggingUtil.logRestRequest(logger, auditLogger, req, response);
772     return response;
773   }
774
775   protected boolean validateRequest(HttpServletRequest req, String uri, String content, Action action,
776                                     String authPolicyFunctionName, HttpHeaders headers) throws CrudException {
777     boolean isValid = false;
778     try {
779       String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite");
780       String authUser = null;
781       if (cipherSuite != null) {
782         X509Certificate[] certChain = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
783         X509Certificate clientCert = certChain[0];
784         X500Principal subjectDn = clientCert.getSubjectX500Principal();
785         authUser = subjectDn.toString();
786       }
787       if(null != authUser) {
788         isValid = this.auth.validateRequest(authUser.toLowerCase(), action.toString() + ":" + authPolicyFunctionName);
789       }
790     } catch (Exception e) {
791       logResult(action, uri, e);
792       return false;
793     }
794
795     validateRequestHeader(headers);
796     
797     return isValid;
798   }
799   
800   public void validateRequestHeader(HttpHeaders headers) throws CrudException {
801       String sourceOfTruth = null;
802       if (headers.getRequestHeaders().containsKey("X-FromAppId")) {
803         sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
804       }
805
806       if (sourceOfTruth == null || sourceOfTruth.trim() == "") {
807         throw new CrudException("Invalid request, Missing X-FromAppId header", Status.BAD_REQUEST);
808       }
809       
810       String transId = null;
811       if (headers.getRequestHeaders().containsKey("X-TransactionId")) {
812           transId = headers.getRequestHeaders().getFirst("X-TransactionId");
813       }
814
815       if (transId == null || transId.trim() == "") {
816         throw new CrudException("Invalid request, Missing X-TransactionId header", Status.BAD_REQUEST);
817       }
818   }
819
820   void logResult(Action op, String uri, Exception e) {
821
822     logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri, Arrays.toString(e.getStackTrace()));
823
824     // Clear the MDC context so that no other transaction inadvertently
825     // uses our transaction id.
826     MDC.clear();
827   }
828
829   private Map<String, String> addParams ( UriInfo info, boolean filter, String type, String version ) {
830     String propertiesKey = CrudProperties.get ( CrudServiceConstants.CRD_COLLECTION_PROPERTIES_KEY );
831     Map<String, String> params = new HashMap<String, String> ();
832     params.put ( CrudServiceConstants.CRD_RESERVED_VERSION, version );
833     params.put ( CrudServiceConstants.CRD_RESERVED_NODE_TYPE, type );
834     if (filter) {
835       for (Map.Entry<String, List<String>> e : info.getQueryParameters ().entrySet ()) {
836         if (!e.getKey ().equals ( propertiesKey )) {
837           params.put ( e.getKey (), e.getValue ().get ( 0 ) );
838         }
839       }
840     } else {
841       for (Map.Entry<String, List<String>> e : info.getQueryParameters ().entrySet ()) {
842         params.put ( e.getKey (), e.getValue ().get ( 0 ) );
843       }
844     }
845     return params;
846   }
847 }