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