Moving Historical Queries to spring boot
[aai/data-router.git] / src / main / java / org / onap / aai / datarouter / query / ChameleonRouter.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.aai.datarouter.query;
22
23 import java.security.InvalidParameterException;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import javax.annotation.PostConstruct;
32 import javax.ws.rs.core.MediaType;
33 import javax.ws.rs.core.Response.Status;
34
35 import org.eclipse.jetty.util.security.Password;
36 import org.onap.aai.cl.api.Logger;
37 import org.onap.aai.cl.eelf.LoggerFactory;
38 import org.onap.aai.datarouter.exception.DataRouterException;
39 import org.onap.aai.datarouter.util.DataRouterConstants;
40 import org.onap.aai.restclient.client.OperationResult;
41 import org.onap.aai.restclient.client.RestClient;
42 import org.springframework.beans.factory.annotation.Qualifier;
43 import org.springframework.stereotype.Component;
44
45 @Component
46 @Qualifier("chameleon")
47 public class ChameleonRouter implements QueryRouter {
48
49   Logger logger = LoggerFactory.getInstance().getLogger(ChameleonRouter.class.getName());
50
51   private String chameleonBaseURL;
52
53   private RestClient restClient ;
54
55   private enum ChameleonAction {
56     GET_OBJECT_BY_ID, GET_REL_BY_ID, GET_OBJECT_RELS, GET_OBJECTS_BY_FILTER, GET_RELS_BY_FILTER
57   };
58
59   private static final Pattern QUERY_OBJECT_FILTER_URL_MATCH = Pattern.compile("/objects/filter(.*)");
60   private static final Pattern QUERY_REL_FILTER_URL_MATCH = Pattern.compile("/relationships/filter(.*)");
61   private static final Pattern QUERY_OBJECT_REL_URL_MATCH = Pattern.compile("/objects/relationships/(.*)");
62   private static final Pattern QUERY_OBJECT_ID_URL_MATCH = Pattern.compile("/objects/(.*)");
63   private static final Pattern QUERY_REL_ID_URL_MATCH = Pattern.compile("/relationships/(.*)");
64
65
66   public ChameleonRouter(){}
67   
68   
69   public ChameleonRouter(String chameleonBaseURL, RestClientConfig config) {   
70     this.chameleonBaseURL = chameleonBaseURL;
71     this.restClient = new RestClient().validateServerHostname(false).validateServerCertChain(true)
72         .clientCertFile(config.getCertPath())
73         .clientCertPassword(Password.deobfuscate(config.getCertPassword()))
74         .trustStore(config.getTrustStorePath())
75         .connectTimeoutMs(config.getConnectionTimeout())
76         .readTimeoutMs(config.getReadTimeout());
77     validate();
78   }
79
80   
81   public void validate() {
82     String baseURL = chameleonBaseURL.endsWith("/") ? chameleonBaseURL.substring(0, chameleonBaseURL.length() - 1)
83         : chameleonBaseURL;
84     if (baseURL.contains(DATA_ROUTER_PORT)) {
85       logger.error(QueryMsgs.QUERY_ERROR,
86           "Invalid chameleonBaseURL : Can't re-route back to DataRouter " + chameleonBaseURL);
87       throw new InvalidParameterException(
88           "Invalid chameleonBaseURL : Can't re-route back to DataRouter " + chameleonBaseURL);
89     }
90     this.chameleonBaseURL = baseURL;
91   }
92
93   private boolean urlMatcher(Pattern p, String url) {
94     Matcher m = p.matcher(url);
95     if (m.matches() && !m.group(1).contains("/")) {
96       return true;
97     } else {
98       return false;
99     }
100   }
101
102   private ChameleonAction resolveChameleonAction(String urlContext) throws DataRouterException {
103
104     urlContext = urlContext.endsWith("/") ? urlContext.substring(0, urlContext.length() - 1) : urlContext;
105     ChameleonAction action;
106
107     if (urlMatcher(QUERY_OBJECT_FILTER_URL_MATCH, urlContext)) {
108       action = ChameleonAction.GET_OBJECTS_BY_FILTER;
109     } else if (urlMatcher(QUERY_REL_FILTER_URL_MATCH, urlContext)) {
110       action = ChameleonAction.GET_RELS_BY_FILTER;
111     } else if (urlMatcher(QUERY_OBJECT_REL_URL_MATCH, urlContext)) {
112       action = ChameleonAction.GET_OBJECT_RELS;
113     } else if (urlMatcher(QUERY_OBJECT_ID_URL_MATCH, urlContext)) {
114       action = ChameleonAction.GET_OBJECT_BY_ID;
115     } else if (urlMatcher(QUERY_REL_ID_URL_MATCH, urlContext)) {
116       action = ChameleonAction.GET_REL_BY_ID;
117     } else {
118
119       throw new DataRouterException("", Status.NOT_FOUND);
120     }
121     return action;
122   }
123
124   private String buildUrl(String urlContext, String queryParams, ChameleonAction action) {
125
126     urlContext = urlContext.endsWith("/") ? urlContext.substring(0, urlContext.length() - 1) : urlContext;
127     String ecompUrl = "";
128     String ID = "";
129
130     switch (action) {
131     case GET_OBJECT_BY_ID:
132       ID = urlContext.substring(urlContext.lastIndexOf("/") + 1, urlContext.length());
133       if (ID == null || ID.isEmpty()) {
134         throw new IllegalArgumentException("Invalid URI path with no Object ID: " + urlContext);
135       } else {
136         if (queryParams != null && !queryParams.isEmpty()) {
137           ecompUrl = chameleonBaseURL + "/" + ID + "?" + queryParams;
138
139         } else {
140           ecompUrl = chameleonBaseURL + "/" + ID;
141         }
142       }
143
144       break;
145
146     case GET_REL_BY_ID:
147       ID = urlContext.substring(urlContext.lastIndexOf("/") + 1, urlContext.length());
148       if (ID == null || ID.isEmpty()) {
149         throw new IllegalArgumentException("Invalid URI path with no Relationship ID: " + urlContext);
150       } else {
151         if (queryParams != null && !queryParams.isEmpty()) {
152           ecompUrl = chameleonBaseURL + "/" + ID + "?" + queryParams;
153
154         } else {
155           ecompUrl = chameleonBaseURL + "/" + ID;
156         }
157       }
158
159       break;
160
161     case GET_OBJECT_RELS:
162       ID = urlContext.substring(urlContext.lastIndexOf("/") + 1, urlContext.length());
163       if (ID == null || ID.isEmpty()) {
164         throw new IllegalArgumentException("Invalid URI path with no Object ID: " + urlContext);
165       } else {
166         if (queryParams != null && !queryParams.isEmpty()) {
167           // TODO: Fix the URL for getting object relations when Chameloen
168           // supports this API
169           ecompUrl = chameleonBaseURL + "/relations" + ID + "?" + queryParams;
170
171         } else {
172           ecompUrl = chameleonBaseURL + "/relations" + ID;
173         }
174       }
175
176       break;
177
178     case GET_OBJECTS_BY_FILTER:
179       if (queryParams != null && !queryParams.isEmpty()) {
180         // TODO: Fix the URL for getting object filter when Chameloen
181         // supports this API
182         ecompUrl = chameleonBaseURL + "/filter?" + queryParams;
183       } else {
184         ecompUrl = chameleonBaseURL + "/filter";
185       }
186
187       break;
188
189     case GET_RELS_BY_FILTER:
190       if (queryParams != null && !queryParams.isEmpty()) {
191         // TODO: Fix the URL for getting rel filter when Chameloen
192         // supports this API
193         ecompUrl = chameleonBaseURL + "/filter?" + queryParams;
194       } else {
195         ecompUrl = chameleonBaseURL + "/filter";
196       }
197
198       break;
199
200     }
201
202     return ecompUrl;
203   }
204
205   private String parseResponse(String urlContext, OperationResult result, ChameleonAction action)
206       throws DataRouterException {
207
208     Integer httpResponseCode = result.getResultCode();
209     String ID = urlContext.substring(urlContext.lastIndexOf("/") + 1, urlContext.length());
210
211     switch (action) {
212     case GET_OBJECT_BY_ID:
213       if (httpResponseCode >= 200 && httpResponseCode <= 299) {
214         if (ID == null || ID.isEmpty()) {
215           throw new DataRouterException("", Status.BAD_REQUEST);
216         } else {
217           return ChameleonResponseBuiler.buildEntity(result.getResult(), ID);
218         }
219       } else {
220         throw new DataRouterException("", Status.fromStatusCode(httpResponseCode));
221       }
222
223     case GET_REL_BY_ID:
224       if (httpResponseCode >= 200 && httpResponseCode <= 299) {
225
226         if (ID == null || ID.isEmpty()) {
227           throw new DataRouterException("", Status.BAD_REQUEST);
228         } else {
229           return ChameleonResponseBuiler.buildEntity(result.getResult(), ID);
230         }
231       } else {
232         throw new DataRouterException("", Status.fromStatusCode(httpResponseCode));
233       }
234
235     case GET_OBJECT_RELS:
236
237       // TODO:Return 200 with empty body for now until chameleon supports this
238       // query
239       if (ID == null || ID.isEmpty()) {
240         throw new DataRouterException("", Status.BAD_REQUEST);
241       } else {
242         return ChameleonResponseBuiler.buildObjectRelationship(result.getResult(), ID);
243       }
244
245     case GET_OBJECTS_BY_FILTER:
246       // TODO:Return 200 with empty body for now until chameleon supports this
247       // query
248       return ChameleonResponseBuiler.buildCollection(result.getResult());
249
250     case GET_RELS_BY_FILTER:
251       // TODO:Return 200 with empty body for now until chameleon supports this
252       // query
253       return ChameleonResponseBuiler.buildCollection(result.getResult());
254     default:
255       throw new DataRouterException("", Status.NOT_FOUND);
256
257     }
258
259   }
260
261   @Override
262   public String process(String urlContext, String queryParams, Map<String, List<String>> headers)
263       throws DataRouterException {
264     String response;
265     ChameleonAction action = resolveChameleonAction(urlContext);
266     String chameleonURL = buildUrl(urlContext, queryParams, action);
267     logger.info(QueryMsgs.QUERY_INFO, "Routing request to Chameleon service URL: " + chameleonURL);
268
269     headers = headers == null ? new HashMap<String, List<String>>() : headers;
270     headers.put("X-FromAppId", Arrays.asList(DataRouterConstants.DATA_ROUTER_SERVICE_NAME));
271     OperationResult result = restClient.get(chameleonURL, headers, MediaType.APPLICATION_JSON_TYPE);
272
273     try {
274       response = parseResponse(urlContext, result, action);
275     } catch (DataRouterException ex) {
276       logger.info(QueryMsgs.QUERY_ERROR,
277           "Error while calling Chameleon service URL: " + chameleonURL + " failure cause: " + result.getFailureCause());
278       throw ex;
279     }
280
281     return response;
282   }
283
284 }