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