Optionally disable authorization via policy
[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
31 import javax.security.auth.x500.X500Principal;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.ws.rs.Consumes;
34 import javax.ws.rs.DELETE;
35 import javax.ws.rs.Encoded;
36 import javax.ws.rs.GET;
37 import javax.ws.rs.POST;
38 import javax.ws.rs.PUT;
39 import javax.ws.rs.Path;
40 import javax.ws.rs.PathParam;
41 import javax.ws.rs.Produces;
42 import javax.ws.rs.core.Context;
43 import javax.ws.rs.core.EntityTag;
44 import javax.ws.rs.core.HttpHeaders;
45 import javax.ws.rs.core.MediaType;
46 import javax.ws.rs.core.Response;
47 import javax.ws.rs.core.Response.ResponseBuilder;
48 import javax.ws.rs.core.Response.Status;
49 import javax.ws.rs.core.UriInfo;
50
51 import org.apache.commons.lang3.tuple.ImmutablePair;
52 import org.apache.cxf.jaxrs.ext.PATCH;
53 import org.onap.aai.cl.api.Logger;
54 import org.onap.aai.cl.eelf.LoggerFactory;
55 import org.onap.aaiauth.auth.Auth;
56 import org.onap.crud.exception.CrudException;
57 import org.onap.crud.logging.CrudServiceMsgs;
58 import org.onap.crud.logging.LoggingUtil;
59 import org.onap.crud.parser.BulkPayload;
60 import org.onap.crud.parser.EdgePayload;
61 import org.onap.crud.parser.VertexPayload;
62 import org.onap.crud.util.CrudProperties;
63 import org.onap.crud.util.CrudServiceConstants;
64 import org.onap.crud.util.CrudServiceUtil;
65 import org.slf4j.MDC;
66
67 import com.google.gson.JsonElement;
68
69 import io.swagger.annotations.ApiImplicitParam;
70 import io.swagger.annotations.ApiImplicitParams;
71 import io.swagger.annotations.ApiOperation;
72 import io.swagger.annotations.ApiParam;
73 import io.swagger.annotations.ApiResponse;
74 import io.swagger.annotations.ApiResponses;
75
76 @Path("/inventory")
77 public class CrudRestService {
78
79     private AbstractGraphDataService graphDataService;
80     Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName());
81     Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName());
82     private Auth auth;
83     private boolean authorizationEnabled;
84
85     private String mediaType = MediaType.APPLICATION_JSON;
86     public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override";
87     public static final String TRANSACTIONID_HEADER = "X-TransactionId";
88
89     public CrudRestService(AbstractGraphDataService graphDataService) throws Exception {
90         this.graphDataService = graphDataService;
91         
92         this.authorizationEnabled = Boolean.parseBoolean(
93                 CrudProperties.get(CrudServiceConstants.CRD_AUTHORIZATION_ENABLED, "true"));
94
95         this.auth = null;
96         if (this.authorizationEnabled) {
97             this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE);
98         }        
99     }
100
101     // For unit testing
102     public CrudRestService(AbstractGraphDataService graphDataService, Auth auth) throws Exception {
103         this.graphDataService = graphDataService;
104         this.auth = auth;
105     }
106
107     public enum Action {
108         POST, GET, PUT, DELETE, PATCH
109     }
110
111     public void startup() {
112
113     }
114     
115     @ApiOperation(value = "Get Vertex" , notes="For example : https://<host>:9520/services/inventory/v11/pserver/<id>")
116     @ApiResponses({    
117       @ApiResponse(code = 200, message = "Success"),
118       @ApiResponse(code = 403, message = "Forbidden"),    
119       @ApiResponse(code = 404, message = "Not Found"),
120       @ApiResponse(code = 500, message = "Internal Server Error") })
121     @ApiImplicitParams({
122       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
123       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
124     })
125     @GET
126     @Path("/{version}/{type}/{id}")
127     @Consumes({MediaType.APPLICATION_JSON})
128     @Produces({MediaType.APPLICATION_JSON})
129     public Response getVertex(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
130             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
131             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
132
133         LoggingUtil.initMdcContext(req, headers);
134         logger.debug("Incoming request..." + content);
135
136         ResponseBuilder responseBuilder;
137         Map<String, String> params = addParams(uriInfo, false, type, version);
138
139         try {
140             if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
141                 ImmutablePair<EntityTag, String> result = graphDataService.getVertex(version, id, type, params);
142                 responseBuilder =
143                         Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType);
144             } else {
145                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
146             }
147         } catch (CrudException ce) {
148             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
149         } catch (Exception e) {
150             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
151
152         }
153
154         setTxIdOnResponseHeader(headers, responseBuilder);
155
156         Response response = responseBuilder.build();
157         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
158         return response;
159     }
160
161     @ApiOperation(value = "Get Vertices" , notes="For example : https://<host>:9520/services/inventory/v11/pserver/")
162     @ApiResponses({    
163       @ApiResponse(code = 200, message = "Success"),
164       @ApiResponse(code = 403, message = "Forbidden"),    
165       @ApiResponse(code = 404, message = "Not Found"),
166       @ApiResponse(code = 500, message = "Internal Server Error") })
167     @ApiImplicitParams({
168       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),    
169       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
170     })
171     @GET
172     @Path("/{version}/{type}/")
173     @Consumes({MediaType.APPLICATION_JSON})
174     @Produces({MediaType.APPLICATION_JSON})
175     public Response getVertices(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
176             @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
177             @Context HttpServletRequest req) {
178
179         LoggingUtil.initMdcContext(req, headers);
180         logger.debug("Incoming request..." + content);
181
182         ResponseBuilder responseBuilder;
183         try {
184             if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
185                 String propertiesKey = CrudProperties.get(CrudServiceConstants.CRD_COLLECTION_PROPERTIES_KEY);
186                 Map<String, String> filter = addParams(uriInfo, true, type, version);
187
188                 HashSet<String> properties;
189                 if (uriInfo.getQueryParameters().containsKey(propertiesKey)) {
190                     properties = new HashSet<>(uriInfo.getQueryParameters().get(propertiesKey));
191                 } else {
192                     properties = new HashSet<>();
193                 }
194
195                 ImmutablePair<EntityTag, String> result =
196                         graphDataService.getVertices(version, type, filter, properties);
197                 responseBuilder =
198                         Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType);
199             } else {
200                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
201             }
202         } catch (CrudException ce) {
203             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
204         } catch (Exception e) {
205             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
206
207         }
208
209         setTxIdOnResponseHeader(headers, responseBuilder);
210
211         Response response = responseBuilder.build();
212         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
213         return response;
214     }
215
216     @ApiOperation(value = "Get Edge" , notes="For example : https://<host>:9520/services/inventory/relationships/v11/tosca.relationships.HostedOn/<id>")
217     @ApiResponses({    
218       @ApiResponse(code = 200, message = "Success"),
219       @ApiResponse(code = 403, message = "Forbidden"),    
220       @ApiResponse(code = 404, message = "Not Found"),
221       @ApiResponse(code = 500, message = "Internal Server Error") })
222     @ApiImplicitParams({
223       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
224       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
225     })
226     @GET
227     @Path("/relationships/{version}/{type}/{id}")
228     @Consumes({MediaType.APPLICATION_JSON})
229     @Produces({MediaType.APPLICATION_JSON})
230     public Response getEdge(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
231             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
232             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
233
234         LoggingUtil.initMdcContext(req, headers);
235         logger.debug("Incoming request..." + content);
236
237         ResponseBuilder responseBuilder;
238         Map<String, String> params = addParams(uriInfo, false, type, version);
239
240         try {
241             if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
242
243                 ImmutablePair<EntityTag, String> result = graphDataService.getEdge(version, id, type, params);
244                 responseBuilder =
245                         Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType);
246             } else {
247                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
248             }
249         } catch (CrudException ce) {
250             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
251         } catch (Exception e) {
252             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
253         }
254
255         setTxIdOnResponseHeader(headers, responseBuilder);
256
257         Response response = responseBuilder.build();
258         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
259         return response;
260     }
261
262     @ApiOperation(value = "Get Edges" , notes="For example : https://<host>:9520/services/inventory/relationships/v11/tosca.relationships.HostedOn/")
263     @ApiResponses({    
264       @ApiResponse(code = 200, message = "Success"),
265       @ApiResponse(code = 403, message = "Forbidden"),    
266       @ApiResponse(code = 404, message = "Not Found"),
267       @ApiResponse(code = 500, message = "Internal Server Error") })
268     @ApiImplicitParams({
269       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
270       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
271     })
272     @GET
273     @Path("/relationships/{version}/{type}/")
274     @Consumes({MediaType.APPLICATION_JSON})
275     @Produces({MediaType.APPLICATION_JSON})
276     public Response getEdges(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
277             @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
278             @Context HttpServletRequest req) {
279
280         LoggingUtil.initMdcContext(req, headers);
281         logger.debug("Incoming request..." + content);
282
283         ResponseBuilder responseBuilder;
284         Map<String, String> filter = addParams(uriInfo, true, type, version);
285
286         try {
287             if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
288                 ImmutablePair<EntityTag, String> result = graphDataService.getEdges(version, type, filter);
289                 responseBuilder =
290                         Response.status(Status.OK).entity(result.getValue()).tag(result.getKey()).type(mediaType);
291             } else {
292                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
293             }
294         } catch (CrudException ce) {
295             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
296         } catch (Exception e) {
297             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
298         }
299
300         setTxIdOnResponseHeader(headers, responseBuilder);
301
302         Response response = responseBuilder.build();
303         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
304         return response;
305     }
306
307     @ApiOperation(value = "Update Edge" , notes = "# Payload \n"
308         + "{  \r\n" + 
309         "   \"properties\":{  \r\n" + 
310         "        \"prop1\" : \"value\",\r\n" + 
311         "        \"prop2\" :\"value\",\r\n" +  
312         "    ..}\r\n" + 
313         " }")
314     @ApiResponses({    
315       @ApiResponse(code = 200, message = "Success"),
316       @ApiResponse(code = 403, message = "Forbidden"),    
317       @ApiResponse(code = 404, message = "Not Found"),
318       @ApiResponse(code = 400, message = "Bad Request"),
319       @ApiResponse(code = 500, message = "Internal Server Error") })
320     @ApiImplicitParams({
321       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
322       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
323     })
324     @PUT
325     @Path("/relationships/{version}/{type}/{id}")
326     @Consumes({MediaType.APPLICATION_JSON})
327     @Produces({MediaType.APPLICATION_JSON})
328     public Response updateEdge(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
329             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
330             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
331
332         LoggingUtil.initMdcContext(req, headers);
333         logger.debug("Incoming request..." + content);
334
335         ResponseBuilder responseBuilder;
336
337         try {
338             if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
339                 EdgePayload payload = EdgePayload.fromJson(content);
340                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
341                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
342                 }
343                 if (payload.getId() != null && !payload.getId().equals(id)) {
344                     throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
345                 }
346                 ImmutablePair<EntityTag, String> result;
347                 if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
348                         && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
349                     result = graphDataService.patchEdge(version, id, type, payload);
350                     responseBuilder =
351                             Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
352                 } else {
353                     result = graphDataService.updateEdge(version, id, type, payload);
354                     responseBuilder =
355                             Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
356                 }
357
358             } else {
359                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
360             }
361         } catch (CrudException ce) {
362             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
363         } catch (Exception e) {
364             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
365         }
366
367         setTxIdOnResponseHeader(headers, responseBuilder);
368
369         Response response = responseBuilder.build();
370         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
371         return response;
372     }
373
374     @ApiOperation(value = "Patch Edge" , notes = "# Payload \n"
375         + "{  \r\n" + 
376         "   \"properties\":{  \r\n" + 
377         "        \"prop1\" : \"value\",\r\n" + 
378         "        \"prop2\" :\"value\",\r\n" +  
379         "    ..}\r\n" + 
380         " }")
381     @ApiResponses({    
382       @ApiResponse(code = 200, message = "Success"),
383       @ApiResponse(code = 403, message = "Forbidden"), 
384       @ApiResponse(code = 400, message = "Bad Request"),
385       @ApiResponse(code = 404, message = "Not Found"),
386       @ApiResponse(code = 500, message = "Internal Server Error") })
387     @ApiImplicitParams({
388       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
389       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
390     })
391     @PATCH
392     @Path("/relationships/{version}/{type}/{id}")
393     @Consumes({"application/merge-patch+json"})
394     @Produces({MediaType.APPLICATION_JSON})
395     public Response patchEdge(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
396             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
397             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
398
399         LoggingUtil.initMdcContext(req, headers);
400         logger.debug("Incoming request..." + content);
401
402         ResponseBuilder responseBuilder;
403
404         try {
405             if (validateRequest(req, uri, content, Action.PATCH, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
406                 EdgePayload payload = EdgePayload.fromJson(content);
407                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
408                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
409                 }
410                 if (payload.getId() != null && !payload.getId().equals(id)) {
411                     throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
412                 }
413
414                 ImmutablePair<EntityTag, String> result = graphDataService.patchEdge(version, id, type, payload);
415                 responseBuilder =
416                         Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
417             } else {
418                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
419             }
420         } catch (CrudException ce) {
421             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
422         } catch (Exception e) {
423             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
424         }
425
426         setTxIdOnResponseHeader(headers, responseBuilder);
427
428         Response response = responseBuilder.build();
429         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
430         return response;
431     }
432
433     @ApiOperation(value = "Update Vertex" , notes = "# Payload \n"
434         + "{\r\n" + 
435         "   \"type\" :\"vertex type from oxm like comcast.nodes.sdwan.vpn\",\r\n" + 
436         "    \"properties\": {\r\n" + 
437         "        \"prop1\" : \"value\",\r\n" + 
438         "        \"prop2\" :\"value\",\r\n" +          
439         "    ..}\r\n" + 
440         " }")
441     @ApiResponses({    
442       @ApiResponse(code = 200, message = "Success"),
443       @ApiResponse(code = 403, message = "Forbidden"), 
444       @ApiResponse(code = 400, message = "Bad Request"),
445       @ApiResponse(code = 404, message = "Not Found"),
446       @ApiResponse(code = 500, message = "Internal Server Error") })
447     @ApiImplicitParams({
448       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
449       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
450     })
451     @PUT
452     @Path("/{version}/{type}/{id}")
453     @Consumes({MediaType.APPLICATION_JSON})
454     @Produces({MediaType.APPLICATION_JSON})
455     public Response updateVertex(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
456             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
457             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
458
459         LoggingUtil.initMdcContext(req, headers);
460         logger.debug("Incoming request..." + content);
461
462         ResponseBuilder responseBuilder;
463
464         try {
465             if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
466                 VertexPayload payload = VertexPayload.fromJson(content);
467                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
468                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
469                 }
470                 if (payload.getId() != null && !payload.getId().equals(id)) {
471                     throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
472                 }
473
474                 payload.setProperties(
475                         CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
476
477                 ImmutablePair<EntityTag, String> result;
478                 if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null
479                         && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE).equalsIgnoreCase("PATCH")) {
480                     result = graphDataService.patchVertex(version, id, type, payload);
481                     responseBuilder =
482                             Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
483                 } else {
484                     result = graphDataService.updateVertex(version, id, type, payload);
485                     responseBuilder =
486                             Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
487                 }
488
489             } else {
490                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
491             }
492         } catch (CrudException ce) {
493             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
494         } catch (Exception e) {
495             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
496         }
497
498         setTxIdOnResponseHeader(headers, responseBuilder);
499
500         Response response = responseBuilder.build();
501         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
502         return response;
503     }
504
505     @ApiOperation(value = "Patch Vertex" , notes = "# Payload \n"
506         + "{\r\n" + 
507         "   \"type\" :\"vertex type from oxm like comcast.nodes.sdwan.vpn\",\r\n" + 
508         "    \"properties\": {\r\n" + 
509         "        \"prop1\" : \"true\",\r\n" + 
510         "        \"prop2\" :\"name1\",\r\n" +         
511         "    ..}\r\n" + 
512         " }")
513     @ApiResponses({    
514       @ApiResponse(code = 200, message = "Success"),
515       @ApiResponse(code = 403, message = "Forbidden"), 
516       @ApiResponse(code = 400, message = "Bad Request"),
517       @ApiResponse(code = 404, message = "Not Found"),
518       @ApiResponse(code = 500, message = "Internal Server Error") })
519     @ApiImplicitParams({
520       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
521       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
522     })
523     @PATCH
524     @Path("/{version}/{type}/{id}")
525     @Consumes({"application/merge-patch+json"})
526     @Produces({MediaType.APPLICATION_JSON})
527     public Response patchVertex(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
528             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
529             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
530
531         LoggingUtil.initMdcContext(req, headers);
532         logger.debug("Incoming request..." + content);
533
534         ResponseBuilder responseBuilder;
535
536         try {
537             if (validateRequest(req, uri, content, Action.PATCH, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
538                 VertexPayload payload = VertexPayload.fromJson(content);
539                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
540                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
541                 }
542                 if (payload.getId() != null && !payload.getId().equals(id)) {
543                     throw new CrudException("ID Mismatch", Status.BAD_REQUEST);
544                 }
545
546                 payload.setProperties(
547                         CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, false));
548
549                 ImmutablePair<EntityTag, String> result = graphDataService.patchVertex(version, id, type, payload);
550                 responseBuilder =
551                         Response.status(Status.OK).entity(result.getValue()).type(mediaType).tag(result.getKey());
552             } else {
553                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
554             }
555         } catch (CrudException ce) {
556             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
557         } catch (Exception e) {
558             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
559         }
560
561         setTxIdOnResponseHeader(headers, responseBuilder);
562
563         Response response = responseBuilder.build();
564         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
565         return response;
566     }
567
568     @ApiOperation(value = "Create Vertex" , notes = "# Payload \n"
569         + "{\r\n" +      
570         "    \"properties\": {\r\n" + 
571         "        \"prop1\" : \"value\",\r\n" + 
572         "        \"prop2\" :\"value\",\r\n" +         
573         "    ..}\r\n" + 
574         " }")
575     @ApiResponses({    
576       @ApiResponse(code = 201, message = "Created"),
577       @ApiResponse(code = 403, message = "Forbidden"), 
578       @ApiResponse(code = 400, message = "Bad Request"),
579       @ApiResponse(code = 404, message = "Not Found"),
580       @ApiResponse(code = 500, message = "Internal Server Error") })
581     @ApiImplicitParams({
582       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
583       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
584     })
585     @POST
586     @Path("/{version}/{type}/")
587     @Consumes({MediaType.APPLICATION_JSON})
588     @Produces({MediaType.APPLICATION_JSON})
589     public Response addVertex(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
590             @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
591             @Context HttpServletRequest req) {
592
593         LoggingUtil.initMdcContext(req, headers);
594         logger.debug("Incoming request..." + content);
595
596         ResponseBuilder responseBuilder;
597
598         try {
599             if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
600                 VertexPayload payload = VertexPayload.fromJson(content);
601                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
602                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
603                 }
604                 if (payload.getId() != null) {
605                     throw new CrudException("ID specified , use Http PUT to update Vertex", Status.BAD_REQUEST);
606                 }
607
608                 if (payload.getType() != null && !payload.getType().equals(type)) {
609                     throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST);
610                 }
611
612                 payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
613
614                 ImmutablePair<EntityTag, String> result = graphDataService.addVertex(version, type, payload);
615                 responseBuilder =
616                         Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType);
617             } else {
618                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
619             }
620         } catch (CrudException ce) {
621             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
622         } catch (Exception e) {
623             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
624         }
625
626         setTxIdOnResponseHeader(headers, responseBuilder);
627
628         Response response = responseBuilder.build();
629         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
630         return response;
631     }
632
633     private void validateBulkPayload(BulkPayload payload) throws CrudException {
634         List<String> vertices = new ArrayList<String>();
635         List<String> edges = new ArrayList<String>();
636
637         for (JsonElement v : payload.getObjects()) {
638             List<Map.Entry<String, JsonElement>> entries =
639                     new ArrayList<Map.Entry<String, JsonElement>>(v.getAsJsonObject().entrySet());
640
641             if (entries.size() != 2) {
642                 throw new CrudException("", Status.BAD_REQUEST);
643             }
644             Map.Entry<String, JsonElement> opr = entries.get(0);
645             Map.Entry<String, JsonElement> item = entries.get(1);
646
647             if (vertices.contains(item.getKey())) {
648                 throw new CrudException("duplicate vertex in payload: " + item.getKey(), Status.BAD_REQUEST);
649             }
650             VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString());
651             if (vertexPayload.getType() == null) {
652                 throw new CrudException("Vertex Type cannot be null for: " + item.getKey(), Status.BAD_REQUEST);
653             }
654
655             if (!opr.getKey().equalsIgnoreCase("operation")) {
656                 throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST);
657             }
658
659             if (!opr.getValue().getAsString().equalsIgnoreCase("add")
660                     && !opr.getValue().getAsString().equalsIgnoreCase("modify")
661                     && !opr.getValue().getAsString().equalsIgnoreCase("patch")
662                     && !opr.getValue().getAsString().equalsIgnoreCase("delete")) {
663                 throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST);
664             }
665             // check if ID is populate for modify/patch/delete operation
666             if ((opr.getValue().getAsString().equalsIgnoreCase("modify")
667                     || opr.getValue().getAsString().equalsIgnoreCase("patch")
668                     || opr.getValue().getAsString().equalsIgnoreCase("delete")) && (vertexPayload.getId() == null)) {
669
670                 throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST);
671
672             }
673
674             vertices.add(item.getKey());
675         }
676
677         for (JsonElement v : payload.getRelationships()) {
678             List<Map.Entry<String, JsonElement>> entries =
679                     new ArrayList<Map.Entry<String, JsonElement>>(v.getAsJsonObject().entrySet());
680
681             if (entries.size() != 2) {
682                 throw new CrudException("", Status.BAD_REQUEST);
683             }
684             Map.Entry<String, JsonElement> opr = entries.get(0);
685             Map.Entry<String, JsonElement> item = entries.get(1);
686
687             if (edges.contains(item.getKey())) {
688                 throw new CrudException("duplicate Edge in payload: " + item.getKey(), Status.BAD_REQUEST);
689             }
690
691             EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString());
692
693             if (!opr.getKey().equalsIgnoreCase("operation")) {
694                 throw new CrudException("operation missing in item: " + item.getKey(), Status.BAD_REQUEST);
695             }
696
697             if (!opr.getValue().getAsString().equalsIgnoreCase("add")
698                     && !opr.getValue().getAsString().equalsIgnoreCase("modify")
699                     && !opr.getValue().getAsString().equalsIgnoreCase("patch")
700                     && !opr.getValue().getAsString().equalsIgnoreCase("delete")) {
701                 throw new CrudException("Invalid operation at item: " + item.getKey(), Status.BAD_REQUEST);
702             }
703             // check if ID is populate for modify/patch/delete operation
704             if ((edgePayload.getId() == null) && (opr.getValue().getAsString().equalsIgnoreCase("modify")
705                     || opr.getValue().getAsString().equalsIgnoreCase("patch")
706                     || opr.getValue().getAsString().equalsIgnoreCase("delete"))) {
707
708                 throw new CrudException("Mising ID at item: " + item.getKey(), Status.BAD_REQUEST);
709
710             }
711             if (opr.getValue().getAsString().equalsIgnoreCase("add")) {
712                 if (edgePayload.getSource() == null || edgePayload.getTarget() == null) {
713                     throw new CrudException("Source/Target cannot be null for edge: " + item.getKey(),
714                             Status.BAD_REQUEST);
715                 }
716                 if (edgePayload.getSource().startsWith("$")
717                         && !vertices.contains(edgePayload.getSource().substring(1))) {
718                     throw new CrudException("Source Vertex " + edgePayload.getSource().substring(1)
719                             + " not found for Edge: " + item.getKey(), Status.BAD_REQUEST);
720                 }
721
722                 if (edgePayload.getTarget().startsWith("$")
723                         && !vertices.contains(edgePayload.getTarget().substring(1))) {
724                     throw new CrudException("Target Vertex " + edgePayload.getSource().substring(1)
725                             + " not found for Edge: " + item.getKey(), Status.BAD_REQUEST);
726                 }
727             }
728             edges.add(item.getKey());
729
730         }
731
732     }
733
734     @ApiOperation(value = "Bulk API" , notes="For example : https://<host>:9520/services/inventory/v11/bulk")
735     @ApiResponses({    
736       @ApiResponse(code = 200, message = "Success"),
737       @ApiResponse(code = 403, message = "Forbidden"), 
738       @ApiResponse(code = 400, message = "Bad Request"),
739       @ApiResponse(code = 404, message = "Not Found"),
740       @ApiResponse(code = 500, message = "Internal Server Error") })
741     @ApiImplicitParams({
742       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
743       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
744     })
745     @POST
746     @Path("/{version}/bulk/")
747     @Consumes({MediaType.APPLICATION_JSON})
748     @Produces({MediaType.APPLICATION_JSON})
749     public Response addBulk(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, 
750             @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
751             @Context HttpServletRequest req) {
752
753         LoggingUtil.initMdcContext(req, headers);
754         logger.debug("Incoming request..." + content);
755
756         ResponseBuilder responseBuilder;
757
758         try {
759             if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
760                 BulkPayload payload = BulkPayload.fromJson(content);
761                 if ((payload.getObjects() == null && payload.getRelationships() == null)
762                         || (payload.getObjects() != null && payload.getObjects().isEmpty()
763                                 && payload.getRelationships() != null && payload.getRelationships().isEmpty())) {
764                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
765                 }
766
767                 validateBulkPayload(payload);
768                 String result = graphDataService.addBulk(version, payload, headers);
769                 responseBuilder = Response.status(Status.OK).entity(result).type(mediaType);
770             } else {
771                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
772             }
773         } catch (CrudException ce) {
774             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
775         } catch (Exception e) {
776             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
777         }
778
779         setTxIdOnResponseHeader(headers, responseBuilder);
780
781         Response response = responseBuilder.build();
782         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
783         return response;
784     }
785
786     @ApiOperation(value = "Create Vertex (typeless endpoint)" , nickname="addVertex-typeless",notes = "# Payload \n"
787         + "{\r\n" +  
788         "   \"type\" :\"vertex type from oxm like comcast.nodes.sdwan.vpn\",\r\n" +       
789         "    \"properties\": {\r\n" + 
790         "        \"prop1\" : \"value\",\r\n" + 
791         "        \"prop2\" :\"value\",\r\n" +          
792         "    ..}\r\n" + 
793         " }")
794     @ApiResponses({   
795       @ApiResponse(code = 201, message = "Created"),
796       @ApiResponse(code = 403, message = "Forbidden"), 
797       @ApiResponse(code = 400, message = "Bad Request"),
798       @ApiResponse(code = 404, message = "Not Found"),
799       @ApiResponse(code = 500, message = "Internal Server Error") })
800     @ApiImplicitParams({
801       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
802       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
803     })
804     @POST
805     @Path("/{version}/")
806     @Consumes({MediaType.APPLICATION_JSON})
807     @Produces({MediaType.APPLICATION_JSON})
808     public Response addVertex(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version,
809             @PathParam("uri") @Encoded  @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
810             @Context HttpServletRequest req) {
811
812         LoggingUtil.initMdcContext(req, headers);
813         logger.debug("Incoming request..." + content);
814
815         ResponseBuilder responseBuilder;
816
817         try {
818
819             if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
820                 VertexPayload payload = VertexPayload.fromJson(content);
821                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
822                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
823                 }
824                 if (payload.getId() != null) {
825                     throw new CrudException("ID specified , use Http PUT to update Vertex", Status.BAD_REQUEST);
826                 }
827
828                 if (payload.getType() == null || payload.getType().isEmpty()) {
829                     throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST);
830                 }
831
832                 payload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(payload.getProperties(), headers, true));
833
834                 ImmutablePair<EntityTag, String> result =
835                         graphDataService.addVertex(version, payload.getType(), payload);
836                 responseBuilder =
837                         Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType);
838             } else {
839                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
840             }
841         } catch (CrudException ce) {
842             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
843         } catch (Exception e) {
844             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
845         }
846
847         setTxIdOnResponseHeader(headers, responseBuilder);
848
849         Response response = responseBuilder.build();
850         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
851         return response;
852     }
853
854     @ApiOperation(value = "Create Edge" , notes = "# Payload \n"
855         + "{  \r\n" + 
856         "   \"source\":\"source vertex like : services/inventory/v11/vserver/0\",\r\n" + 
857         "   \"target\":\"target vertex like : services/inventory/v11/pserver/7\",\r\n" + 
858         "   \"properties\":{  \r\n" + 
859         "        \"prop1\" : \"value\",\r\n" + 
860         "        \"prop2\" :\"value\",\r\n" +  
861         "    ..}\r\n" + 
862         " }")
863     @ApiResponses({   
864       @ApiResponse(code = 201, message = "Created"),
865       @ApiResponse(code = 403, message = "Forbidden"), 
866       @ApiResponse(code = 400, message = "Bad Request"),
867       @ApiResponse(code = 404, message = "Not Found"),
868       @ApiResponse(code = 500, message = "Internal Server Error") })
869    
870     @ApiImplicitParams({
871       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
872       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),       
873     })
874     @POST
875     @Path("/relationships/{version}/{type}/")
876     @Consumes({MediaType.APPLICATION_JSON})
877     @Produces({MediaType.APPLICATION_JSON})
878     public Response addEdge(String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
879             @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers, @Context UriInfo uriInfo,
880             @Context HttpServletRequest req) {
881
882         LoggingUtil.initMdcContext(req, headers);
883         logger.debug("Incoming request..." + content);
884
885         ResponseBuilder responseBuilder;
886
887         try {
888             if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
889                 EdgePayload payload = EdgePayload.fromJson(content);
890                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
891                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
892                 }
893                 if (payload.getId() != null) {
894                     throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
895                 }
896
897                 if (payload.getType() != null && !payload.getType().equals(type)) {
898                     throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST);
899                 }
900                 ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, type, payload);
901                 responseBuilder =
902                         Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType);
903             } else {
904                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
905             }
906         } catch (CrudException ce) {
907             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
908         } catch (Exception e) {
909             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
910         }
911
912         setTxIdOnResponseHeader(headers, responseBuilder);
913
914         Response response = responseBuilder.build();
915         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
916         return response;
917     }
918
919     @ApiOperation(value = "Create Edge (typeless endpoint)" , nickname="addEdge-typeless",notes = "# Payload \n"
920         + "{  \r\n" + 
921         "   \"type\":\"edge type like : tosca.relationships.HostedOn\",\r\n" +      
922         "   \"source\":\"source vertex like : services/inventory/v11/vserver/0\",\r\n" + 
923         "   \"target\":\"target vertex like : services/inventory/v11/pserver/7\",\r\n" + 
924         "   \"properties\":{  \r\n" + 
925         "        \"prop1\" : \"value\",\r\n" + 
926         "        \"prop2\" :\"value\",\r\n" +  
927         "    ..}\r\n" + 
928         " }")
929     @ApiResponses({  
930       @ApiResponse(code = 201, message = "Created"),
931       @ApiResponse(code = 403, message = "Forbidden"), 
932       @ApiResponse(code = 400, message = "Bad Request"),
933       @ApiResponse(code = 404, message = "Not Found"),
934       @ApiResponse(code = 500, message = "Internal Server Error") })
935     @ApiImplicitParams({
936       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
937       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
938     })
939     @POST
940     @Path("/relationships/{version}/")
941     @Consumes({MediaType.APPLICATION_JSON})
942     @Produces({MediaType.APPLICATION_JSON})
943     public Response addEdge(String content, @PathParam("version")  @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri,
944             @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
945
946         LoggingUtil.initMdcContext(req, headers);
947         logger.debug("Incoming request..." + content);
948
949         ResponseBuilder responseBuilder;
950
951         try {
952             if (validateRequest(req, uri, content, Action.POST, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
953                 EdgePayload payload = EdgePayload.fromJson(content);
954                 if (payload.getProperties() == null || payload.getProperties().isJsonNull()) {
955                     throw new CrudException("Invalid request Payload", Status.BAD_REQUEST);
956                 }
957                 if (payload.getId() != null) {
958                     throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST);
959                 }
960
961                 if (payload.getType() == null || payload.getType().isEmpty()) {
962                     payload.setType(CrudServiceUtil.determineEdgeType(payload, version));
963                 }
964
965                 ImmutablePair<EntityTag, String> result = graphDataService.addEdge(version, payload.getType(), payload);
966                 responseBuilder =
967                         Response.status(Status.CREATED).entity(result.getValue()).tag(result.getKey()).type(mediaType);
968             } else {
969                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
970             }
971         } catch (CrudException ce) {
972             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
973         } catch (Exception e) {
974             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
975         }
976
977         setTxIdOnResponseHeader(headers, responseBuilder);
978
979         Response response = responseBuilder.build();
980         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
981         return response;
982     }
983
984     @ApiOperation(value = "Delete Vertex" , notes="For example : https://<host>:9520/services/inventory/v11/pserver/<id>")
985     @ApiResponses({  
986       @ApiResponse(code = 200, message = "Success"),
987       @ApiResponse(code = 403, message = "Forbidden"), 
988       @ApiResponse(code = 400, message = "Bad Request"),
989       @ApiResponse(code = 404, message = "Not Found"),
990       @ApiResponse(code = 500, message = "Internal Server Error") })
991     @ApiImplicitParams({
992       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
993       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
994     })
995     @DELETE
996     @Path("/{version}/{type}/{id}")
997     @Consumes({MediaType.APPLICATION_JSON})
998     @Produces({MediaType.APPLICATION_JSON})
999     public Response deleteVertex(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
1000             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true)  String uri, @Context HttpHeaders headers,
1001             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
1002
1003         LoggingUtil.initMdcContext(req, headers);
1004         logger.debug("Incoming request..." + content);
1005
1006         ResponseBuilder responseBuilder;
1007
1008         try {
1009             if (validateRequest(req, uri, content, Action.DELETE, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
1010                 String result = graphDataService.deleteVertex(version, id, type);
1011                 responseBuilder = Response.status(Status.OK).entity(result).type(mediaType);
1012             } else {
1013                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
1014             }
1015         } catch (CrudException ce) {
1016             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
1017         } catch (Exception e) {
1018             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
1019         }
1020
1021         setTxIdOnResponseHeader(headers, responseBuilder);
1022
1023         Response response = responseBuilder.build();
1024         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
1025         return response;
1026     }
1027
1028     @ApiOperation(value = "Delete Edge" , notes="For example : https://<host>:9520/services/inventory/v11/pserver/<id>")
1029     @ApiResponses({ 
1030       @ApiResponse(code = 200, message = "Success"),
1031       @ApiResponse(code = 403, message = "Forbidden"), 
1032       @ApiResponse(code = 400, message = "Bad Request"),
1033       @ApiResponse(code = 404, message = "Not Found"),
1034       @ApiResponse(code = 500, message = "Internal Server Error") })
1035     @ApiImplicitParams({
1036       @ApiImplicitParam(name = "X-FromAppId", required = true, dataType = "string", paramType = "header"),
1037       @ApiImplicitParam(name = "X-TransactionId",  required = true, dataType = "string", paramType = "header"),    
1038     })
1039     @DELETE
1040     @Path("/relationships/{version}/{type}/{id}")
1041     @Consumes({MediaType.APPLICATION_JSON})
1042     @Produces({MediaType.APPLICATION_JSON})
1043     public Response deleteEdge(@ApiParam(hidden=true) String content, @PathParam("version") @ApiParam(value="oxm model version",defaultValue="v13") String version, @PathParam("type") String type,
1044             @PathParam("id") String id, @PathParam("uri") @Encoded @ApiParam(hidden=true) String uri, @Context HttpHeaders headers,
1045             @Context UriInfo uriInfo, @Context HttpServletRequest req) {
1046
1047         LoggingUtil.initMdcContext(req, headers);
1048         logger.debug("Incoming request..." + content);
1049
1050         ResponseBuilder responseBuilder;
1051
1052         try {
1053             if (validateRequest(req, uri, content, Action.DELETE, CrudServiceConstants.CRD_AUTH_POLICY_NAME, headers)) {
1054                 String result = graphDataService.deleteEdge(version, id, type);
1055                 responseBuilder = Response.status(Status.OK).entity(result).type(mediaType);
1056             } else {
1057                 responseBuilder = Response.status(Status.FORBIDDEN).entity(content).type(MediaType.APPLICATION_JSON);
1058             }
1059         } catch (CrudException ce) {
1060             responseBuilder = Response.status(ce.getHttpStatus()).entity(ce.getMessage());
1061         } catch (Exception e) {
1062             responseBuilder = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
1063         }
1064
1065         setTxIdOnResponseHeader(headers, responseBuilder);
1066
1067         Response response = responseBuilder.build();
1068         LoggingUtil.logRestRequest(logger, auditLogger, req, response);
1069         return response;
1070     }
1071
1072     protected boolean validateRequest(HttpServletRequest req, String uri, String content, Action action,
1073             String authPolicyFunctionName, HttpHeaders headers) throws CrudException {
1074         
1075         if (!authorizationEnabled) {
1076             validateRequestHeader(headers);
1077             return true;
1078         }
1079         
1080         boolean isValid = false;
1081         try {
1082             String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite");
1083             String authUser = null;
1084             if (cipherSuite != null) {
1085                 X509Certificate[] certChain =
1086                         (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
1087                 X509Certificate clientCert = certChain[0];
1088                 X500Principal subjectDn = clientCert.getSubjectX500Principal();
1089                 authUser = subjectDn.toString();
1090             }
1091             if (null != authUser) {
1092                 isValid = this.auth.validateRequest(authUser.toLowerCase(),
1093                         action.toString() + ":" + authPolicyFunctionName);
1094             }
1095         } catch (Exception e) {
1096             logResult(action, uri, e);
1097             return false;
1098         }
1099
1100         validateRequestHeader(headers);
1101
1102         return isValid;
1103     }
1104
1105     public void validateRequestHeader(HttpHeaders headers) throws CrudException {
1106         String sourceOfTruth = null;
1107         if (headers.getRequestHeaders().containsKey("X-FromAppId")) {
1108             sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
1109         }
1110
1111         if (sourceOfTruth == null || sourceOfTruth.trim() == "") {
1112             throw new CrudException("Invalid request, Missing X-FromAppId header", Status.BAD_REQUEST);
1113         }
1114
1115         String transId = null;
1116         if (headers.getRequestHeaders().containsKey("X-TransactionId")) {
1117             transId = headers.getRequestHeaders().getFirst("X-TransactionId");
1118         }
1119
1120         if (transId == null || transId.trim() == "") {
1121             throw new CrudException("Invalid request, Missing X-TransactionId header", Status.BAD_REQUEST);
1122         }
1123     }
1124
1125     void logResult(Action op, String uri, Exception e) {
1126
1127         logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri,
1128                 Arrays.toString(e.getStackTrace()));
1129
1130         // Clear the MDC context so that no other transaction inadvertently
1131         // uses our transaction id.
1132         MDC.clear();
1133     }
1134
1135     private Map<String, String> addParams(UriInfo info, boolean filter, String type, String version) {
1136         String propertiesKey = CrudProperties.get(CrudServiceConstants.CRD_COLLECTION_PROPERTIES_KEY);
1137         Map<String, String> params = new HashMap<String, String>();
1138         params.put(CrudServiceConstants.CRD_RESERVED_VERSION, version);
1139         params.put(CrudServiceConstants.CRD_RESERVED_NODE_TYPE, type);
1140         if (filter) {
1141             for (Map.Entry<String, List<String>> e : info.getQueryParameters().entrySet()) {
1142                 if (!e.getKey().equals(propertiesKey)) {
1143                     params.put(e.getKey(), e.getValue().get(0));
1144                 }
1145             }
1146         } else {
1147             for (Map.Entry<String, List<String>> e : info.getQueryParameters().entrySet()) {
1148                 params.put(e.getKey(), e.getValue().get(0));
1149             }
1150         }
1151         return params;
1152     }
1153
1154     private void setTxIdOnResponseHeader(HttpHeaders headers, ResponseBuilder responseBuilder) {
1155         String txId = headers.getHeaderString(TRANSACTIONID_HEADER);
1156         if (txId != null) {
1157             responseBuilder.header(TRANSACTIONID_HEADER, txId);
1158         }
1159     }
1160 }