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