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