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