Merge "Revive SO's OutgoingRequestHeadersTest"
[vid.git] / vid-app-common / src / main / java / org / onap / vid / mso / RestMsoImplementation.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.mso;
22
23 import static org.onap.vid.utils.Logging.ONAP_REQUEST_ID_HEADER_KEY;
24 import static org.onap.vid.utils.Logging.REQUEST_ID_HEADER_KEY;
25 import static org.onap.vid.utils.Logging.getMethodCallerName;
26 import static org.onap.vid.utils.Logging.getMethodName;
27
28 import com.att.eelf.configuration.EELFLogger;
29 import com.fasterxml.jackson.databind.ObjectMapper;
30 import java.util.Collections;
31 import java.util.Optional;
32 import javax.ws.rs.client.Client;
33 import javax.ws.rs.client.Entity;
34 import javax.ws.rs.client.Invocation;
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.MultivaluedHashMap;
37 import javax.ws.rs.core.Response;
38 import org.apache.commons.codec.binary.Base64;
39 import org.apache.http.HttpException;
40 import org.eclipse.jetty.util.security.Password;
41 import org.glassfish.jersey.client.ClientProperties;
42 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
43 import org.onap.vid.aai.ExceptionWithRequestInfo;
44 import org.onap.vid.aai.util.HttpClientMode;
45 import org.onap.vid.aai.util.HttpsAuthClient;
46 import org.onap.vid.client.HttpBasicClient;
47 import org.onap.vid.exceptions.GenericUncheckedException;
48 import org.onap.vid.mso.rest.RestInterface;
49 import org.onap.vid.utils.Logging;
50 import org.onap.vid.utils.SystemPropertiesWrapper;
51 import org.springframework.beans.factory.annotation.Autowired;
52 import org.springframework.http.HttpMethod;
53
54 /**
55  * Created by pickjonathan on 26/06/2017.
56  */
57 public class RestMsoImplementation implements RestInterface {
58
59     
60     /**
61      * The logger.
62      */
63     protected EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMsoImplementation.class);
64     private final EELFLogger outgoingRequestsLogger = Logging.getRequestsLogger("mso");
65
66     /** The client. */
67     private Client client = null;
68
69
70     protected HttpsAuthClient httpsAuthClient;
71     protected SystemPropertiesWrapper systemProperties;
72     protected final Logging loggingService;
73
74     private static final String START_LOG = " start";
75     private static final String APPLICATION_JSON = "application/json";
76     private static final String WITH_STATUS = " with status=";
77     private static final String URL_LOG = ", url=";
78     private static final String NO_RESPONSE_ENTITY_LOG = " No response entity, this is probably ok, e=";
79     private static final String WITH_URL_LOG = " with url=";
80     private static final String EXCEPTION_LOG = ", Exception: ";
81     private static final String REST_API_SUCCESSFULL_LOG = " REST api was successfull!";
82     private static final String REST_MSG_TEMPLATE = "start {}->{}({}, {}, {})";
83     /** The common headers. */
84     /**
85      * Instantiates a new mso rest interface.
86      */
87
88     @Autowired
89     protected RestMsoImplementation(HttpsAuthClient httpsAuthClient, SystemPropertiesWrapper systemProperties, Logging loggingService){
90         this.httpsAuthClient=httpsAuthClient;
91         this.systemProperties = systemProperties;
92         this.loggingService = loggingService;
93     }
94
95     @SuppressWarnings("Duplicates")
96     protected MultivaluedHashMap<String, Object> initMsoClient()
97     {
98         final String methodname = "initRestClient()";
99
100         final String username = systemProperties.getProperty(MsoProperties.MSO_USER_NAME);
101         final String password = systemProperties.getProperty(MsoProperties.MSO_PASSWORD);
102         final String mso_url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL);
103         final String decrypted_password = Password.deobfuscate(password);
104
105         String authString = username + ":" + decrypted_password;
106
107         byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
108         String authStringEnc = new String(authEncBytes);
109
110         MultivaluedHashMap<String, Object> commonHeaders = new MultivaluedHashMap();
111         commonHeaders.put("Authorization",  Collections.singletonList(("Basic " + authStringEnc)));
112         commonHeaders.put("X-ONAP-PartnerName", Collections.singletonList("VID"));
113
114         String requestIdValue = Logging.extractOrGenerateRequestId();
115         commonHeaders.put(REQUEST_ID_HEADER_KEY, Collections.singletonList(requestIdValue));
116         commonHeaders.put(ONAP_REQUEST_ID_HEADER_KEY, Collections.singletonList(requestIdValue));
117
118
119         boolean useSsl = true;
120         if ( (mso_url != null) && ( !(mso_url.isEmpty()) ) ) {
121             useSsl = mso_url.startsWith("https");
122         }
123         if (client == null) {
124
125             try {
126                 if ( useSsl ) {
127                     client = httpsAuthClient.getClient(HttpClientMode.WITHOUT_KEYSTORE);
128                 }
129                 else {
130                     client = HttpBasicClient.getClient();
131                 }
132             } catch (Exception e) {
133                 logger.info(EELFLoggerDelegate.errorLogger,methodname + " Unable to get the SSL client");
134             }
135         }
136
137         return commonHeaders;
138     }
139
140     public <T> RestObjectWithRequestInfo<T> Get(T t, String path, RestObject<T> restObject, boolean warpException) {
141         String methodName = "Get";
142
143         logger.debug(EELFLoggerDelegate.debugLogger, methodName + START_LOG);
144
145         String url = null;
146         String rawData = null;
147         Integer status = null;
148
149         try {
150             restObject.set(t);
151             url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL) + path;
152
153             MultivaluedHashMap<String, Object> commonHeaders = initMsoClient();
154             loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url);
155                 final Response cres = client.target(url)
156                     .request()
157                     .accept(APPLICATION_JSON)
158                     .headers(commonHeaders)
159                     .get();
160             loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, cres);
161
162             cres.bufferEntity();
163             status = cres.getStatus();
164             rawData = cres.readEntity(String.class);
165
166             restObject.setStatusCode(status);
167
168             if (status == 200 || status == 202) {
169                 t = (T) cres.readEntity(t.getClass());
170                 restObject.set(t);
171                 logger.debug(EELFLoggerDelegate.debugLogger, methodName + REST_API_SUCCESSFULL_LOG);
172
173             } else {
174                 throw new GenericUncheckedException(new HttpException(methodName + WITH_STATUS + status + " (200 or 202 expected), url= " + url));
175             }
176
177             logger.debug(EELFLoggerDelegate.debugLogger, methodName + " received status=" + status);
178
179             return new RestObjectWithRequestInfo<>(HttpMethod.GET, url, restObject, status, rawData);
180         } catch (RuntimeException e) {
181             throw warpException ? new ExceptionWithRequestInfo(HttpMethod.GET, url, rawData, status, e) : e;
182         }
183     }
184
185     @Override
186     public <T> RestObject<T> GetForObject(String path, Class<T> clazz) {
187         final String methodName = getMethodName();
188         logger.debug(EELFLoggerDelegate.debugLogger, "start {}->{}({}, {})", getMethodCallerName(), methodName, path, clazz);
189
190         String url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL) + path;
191         logger.debug(EELFLoggerDelegate.debugLogger, "<== " +  methodName + " sending request to url= " + url);
192
193         MultivaluedHashMap<String, Object> commonHeaders = initMsoClient();
194         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url);
195         final Response cres = client.target(url)
196                 .request()
197                 .accept(APPLICATION_JSON)
198                 .headers(commonHeaders)
199                 .get();
200         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, cres);
201         final RestObject<T> restObject = cresToRestObject(cres, clazz);
202         int status = cres.getStatus();
203
204         if (status == 200 || status == 202) {
205             logger.debug(EELFLoggerDelegate.debugLogger, methodName + REST_API_SUCCESSFULL_LOG);
206         } else {
207             logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + WITH_STATUS +status+ URL_LOG +url);
208         }
209
210         logger.debug(EELFLoggerDelegate.debugLogger,methodName + " received status=" + status );
211
212         return restObject;
213     }
214
215     public <T> RestObject<T> PostForObject(Object requestDetails, String path, Class<T> clazz) {
216         logger.debug(EELFLoggerDelegate.debugLogger, REST_MSG_TEMPLATE, getMethodCallerName(), getMethodName(), requestDetails, path, clazz);
217         return restCall(HttpMethod.POST, clazz, requestDetails, path);
218     }
219
220     public <T> RestObject<T> DeleteForObject(Object requestDetails, String path, Class<T> clazz) {
221         logger.debug(EELFLoggerDelegate.debugLogger, REST_MSG_TEMPLATE, getMethodCallerName(), getMethodName(), requestDetails, path, clazz);
222         return restCall(HttpMethod.DELETE, clazz, requestDetails, path);
223     }
224
225     @Override
226     public void Post(String t, Object r, String path, RestObject<String> restObject) {
227         logger.debug(EELFLoggerDelegate.debugLogger, REST_MSG_TEMPLATE, getMethodCallerName(), getMethodName(), t.getClass(), r, path);
228         restObject.copyFrom(restCall(HttpMethod.POST, String.class, r, path));
229     }
230
231     public Invocation.Builder prepareClient(String path, String methodName) {
232         MultivaluedHashMap<String, Object> commonHeaders = initMsoClient();
233
234         String url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL) + path;
235         logger.debug(EELFLoggerDelegate.debugLogger,"<== " +  methodName + " sending request to url= " + url);
236         // Change the content length
237         return client.target(url)
238                 .request()
239                 .accept(APPLICATION_JSON)
240                 .headers(commonHeaders);
241     }
242
243     public <T> RestObject<T> restCall(HttpMethod httpMethod, Class<T> tClass, Object payload, String path) {
244         return restCall(httpMethod, tClass, payload, path, Optional.empty());
245     }
246
247
248     /*
249     user id is needed to be pass as X-RequestorID in new MSO flows like Delete instanceGroup
250      */
251     public <T> RestObject<T> restCall(HttpMethod httpMethod, Class<T> tClass, Object payload, String path, Optional<String> userId)  {
252         String methodName = httpMethod.name();
253         String url="";
254
255         try {
256
257             MultivaluedHashMap<String, Object> commonHeaders = initMsoClient();
258             userId.ifPresent(id->commonHeaders.put("X-RequestorID", Collections.singletonList(id)));
259
260             url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL) + path;
261             loggingService.logRequest(outgoingRequestsLogger, httpMethod, url, payload);
262             // Change the content length
263             final Invocation.Builder restBuilder = client.target(url)
264                     .request()
265                     .accept(APPLICATION_JSON)
266                     .headers(commonHeaders)
267                     .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
268                 ;
269
270             Invocation restInvocation = payload==null ?
271                     restBuilder.build(httpMethod.name()) :
272                     restBuilder.build(httpMethod.name(), Entity.entity(payload, MediaType.APPLICATION_JSON));
273             final Response cres = restInvocation.invoke();
274
275             loggingService.logResponse(outgoingRequestsLogger, httpMethod, url, cres);
276             return cresToRestObject(cres, tClass);
277         }
278         catch (Exception e) {
279             logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + WITH_URL_LOG +url+ EXCEPTION_LOG + e.toString());
280             throw e;
281         }
282
283     }
284
285     private <T> RestObject<T> cresToRestObject(Response cres, Class<?> tClass) {
286         RestObject<T> restObject = new RestObject<>();
287
288         String rawEntity = null;
289         try {
290             cres.bufferEntity();
291             rawEntity = cres.readEntity(String.class);
292             restObject.setRaw(rawEntity);
293             T t = (T) new ObjectMapper().readValue(rawEntity, tClass);
294             restObject.set(t);
295         }
296         catch ( Exception e ) {
297             try {
298                 logger.debug(EELFLoggerDelegate.debugLogger, "<== " + getMethodCallerName() + " Error reading response entity as " + tClass + ": , e="
299                         + e.getMessage() + ", Entity=" + rawEntity);
300             } catch (Exception e2) {
301                 logger.debug(EELFLoggerDelegate.debugLogger, "<== " + getMethodCallerName() + NO_RESPONSE_ENTITY_LOG
302                         + e.getMessage());
303             }
304         }
305
306         int status = cres.getStatus();
307         restObject.setStatusCode (status);
308
309         return restObject;
310     }
311
312     @Override
313     public <T> void Put(T t, org.onap.vid.changeManagement.RequestDetailsWrapper r, String path, RestObject<T> restObject) {
314
315         String methodName = "Put";
316         String url="";
317
318         logger.debug(EELFLoggerDelegate.debugLogger,"<== " +  methodName + START_LOG);
319
320         try {
321
322             MultivaluedHashMap<String, Object> commonHeaders = initMsoClient();
323
324             url = systemProperties.getProperty(MsoProperties.MSO_SERVER_URL) + path;
325             loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, r);
326             // Change the content length
327             final Response cres = client.target(url)
328                     .request()
329                     .accept(APPLICATION_JSON)
330                     .headers(commonHeaders)
331                     //.header("content-length", 201)
332                     .put(Entity.entity(r, MediaType.APPLICATION_JSON));
333
334             loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, cres);
335
336             try {
337                 t = (T) cres.readEntity(t.getClass());
338                 restObject.set(t);
339             }
340             catch ( Exception e ) {
341                 logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + NO_RESPONSE_ENTITY_LOG
342                         + e.getMessage());
343                 throw e;
344             }
345
346             int status = cres.getStatus();
347             restObject.setStatusCode (status);
348
349             if ( status >= 200 && status <= 299 ) {
350                 logger.info(EELFLoggerDelegate.errorLogger, "<== " + methodName + REST_API_SUCCESSFULL_LOG);
351                 logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + REST_API_SUCCESSFULL_LOG);
352
353             } else {
354                 logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + WITH_STATUS +status+ URL_LOG +url);
355             }
356
357         } catch (Exception e)
358         {
359             logger.debug(EELFLoggerDelegate.debugLogger,"<== " + methodName + WITH_URL_LOG +url+ EXCEPTION_LOG + e.toString());
360             throw e;
361
362         }
363     }
364 }