d8f401eea670dc10aa22927760815b98517e7ad6
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / parsers / uri / URIParser.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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.aai.parsers.uri;
22
23 import java.io.UnsupportedEncodingException;
24 import java.net.URI;
25 import java.util.Set;
26
27 import javax.ws.rs.core.MultivaluedHashMap;
28 import javax.ws.rs.core.MultivaluedMap;
29 import javax.ws.rs.core.UriBuilder;
30
31 import org.onap.aai.config.SpringContextAware;
32 import org.onap.aai.edges.enums.EdgeType;
33 import org.onap.aai.exceptions.AAIException;
34 import org.onap.aai.introspection.Introspector;
35 import org.onap.aai.introspection.Loader;
36 import org.onap.aai.introspection.LoaderFactory;
37 import org.onap.aai.logging.ErrorLogHelper;
38 import org.onap.aai.parsers.exceptions.DoesNotStartWithValidNamespaceException;
39 import org.onap.aai.rest.RestTokens;
40 import org.onap.aai.schema.enums.ObjectMetadata;
41 import org.onap.aai.setup.SchemaVersion;
42 import org.onap.aai.util.AAIConfig;
43 import org.springframework.web.util.UriUtils;
44
45 /**
46  * The Class URIParser.
47  */
48 public class URIParser {
49
50     private URI uri = null;
51
52     protected Loader loader = null;
53
54     protected Loader originalLoader = null;
55
56     private URI originalURI = null;
57
58     private MultivaluedMap<String, String> queryParams = null;
59
60     /**
61      * Instantiates a new URI parser.
62      *
63      * @param loader the loader
64      * @param uri the uri
65      */
66     public URIParser(Loader loader, URI uri) {
67         this.uri = uri;
68
69         this.originalLoader = loader;
70         // Load the latest version because we need it for cloud region
71
72         this.loader = loader;
73     }
74
75     /**
76      * Instantiates a new URI parser.
77      *
78      * @param loader the loader
79      * @param uri the uri
80      * @param queryParams the query params
81      */
82     public URIParser(Loader loader, URI uri, MultivaluedMap<String, String> queryParams) {
83         this(loader, uri);
84         this.queryParams = queryParams;
85     }
86
87     public Loader getLoader() {
88
89         return this.loader;
90
91     }
92
93     /**
94      * Gets the original URI.
95      *
96      * @return the original URI
97      */
98     public URI getOriginalURI() {
99         return this.originalURI;
100     }
101
102     /**
103      * Parses the.
104      *
105      * @param p the p
106      * @throws UnsupportedEncodingException the unsupported encoding exception
107      * @throws AAIException the AAI exception
108      */
109     public void parse(Parsable p) throws UnsupportedEncodingException, AAIException {
110         try {
111             boolean isRelative = false;
112             uri = this.trimURI(uri);
113             uri = handleCloudRegion(p.getCloudRegionTransform(), uri);
114             if (p.useOriginalLoader()) {
115                 this.loader = this.originalLoader;
116             }
117             this.originalURI = UriBuilder.fromPath(uri.getRawPath()).build();
118             if (uri.getRawPath().startsWith("./")) {
119                 uri = new URI(uri.getRawPath().replaceFirst("\\./", ""));
120                 isRelative = true;
121             }
122             String[] parts = uri.getRawPath().split("/");
123             Introspector validNamespaces = loader.introspectorFromName("inventory");
124             Set<String> keys = null;
125             String part = "";
126             Introspector previousObj = null;
127             EdgeType type = EdgeType.TREE;
128             for (int i = 0; i < parts.length;) {
129                 part = parts[i];
130                 Introspector introspector = null;
131                 if (part.equals(RestTokens.COUSIN.toString())) {
132                     if (i == parts.length - 1) {
133                         throw new AAIException("AAI_3000",
134                                 uri + " not a valid path. Cannot end in " + RestTokens.COUSIN);
135                     }
136                     introspector = loader.introspectorFromName(parts[i + 1]);
137                     if (null == previousObj) {
138                         throw new AAIException("AAI_3001");
139                     }
140                     if (previousObj.isContainer() && introspector.isContainer()) {
141                         throw new AAIException("AAI_3000", uri + " not a valid path. Cannot chain plurals together");
142                     }
143                     MultivaluedMap<String, String> uriKeys = new MultivaluedHashMap<>();
144                     if (i == parts.length - 2 && queryParams != null) {
145                         Set<String> queryKeys = queryParams.keySet();
146                         for (String key : queryKeys) {
147                             uriKeys.put(key, queryParams.get(key));
148
149                         }
150                     }
151                     if (introspector.isContainer()) {
152                         boolean isFinalContainer = i == parts.length - 2;
153                         /*
154                          * Related-to could be COUSIN OR TREE and in some cases BOTH. So Let EdgeRuleBuilder use all the
155                          * edgeTypes
156                          */
157                         p.processContainer(introspector, EdgeType.ALL, uriKeys, isFinalContainer);
158                     }
159                     previousObj = introspector;
160                     type = EdgeType.ALL;
161                     i += 2;
162                     continue;
163                 }
164                 introspector = loader.introspectorFromName(part);
165                 if (introspector != null) {
166
167                     // previous has current as property
168                     if (previousObj != null && !previousObj.hasChild(introspector)
169                             && !previousObj.getDbName().equals("nodes")) {
170                         throw new AAIException("AAI_3001", uri + " not a valid path. " + part + " not valid");
171                     } else if (previousObj == null) {
172                         String abstractType = introspector.getMetadata(ObjectMetadata.ABSTRACT);
173                         if (abstractType == null) {
174                             abstractType = "";
175                         }
176                         // first time through, make sure it starts from a namespace
177                         // ignore abstract types
178                         if (!isRelative && !abstractType.equals("true") && !validNamespaces.hasChild(introspector)) {
179                             throw new DoesNotStartWithValidNamespaceException(
180                                     uri + " not a valid path. It does not start from a valid namespace");
181                         }
182                     }
183
184                     keys = introspector.getKeys();
185                     if (keys.size() > 0) {
186                         MultivaluedMap<String, String> uriKeys = new MultivaluedHashMap<>();
187                         i++;
188                         if (i == parts.length && queryParams != null) {
189                             Set<String> queryKeys = queryParams.keySet();
190                             for (String key : queryKeys) {
191                                 uriKeys.put(key, queryParams.get(key));
192                             }
193                         } else {
194                             for (String key : keys) {
195                                 part = UriUtils.decode(parts[i], "UTF-8");
196
197                                 introspector.setValue(key, part);
198
199                                 // skip this for further processing
200                                 i++;
201                             }
202                         }
203
204                         p.processObject(introspector, type, uriKeys);
205                         type = EdgeType.TREE;
206                     } else if (introspector.isContainer()) {
207                         boolean isFinalContainer = i == parts.length - 1;
208                         MultivaluedMap<String, String> uriKeys = new MultivaluedHashMap<>();
209
210                         if (isFinalContainer && queryParams != null) {
211                             Set<String> queryKeys = queryParams.keySet();
212                             for (String key : queryKeys) {
213                                 uriKeys.put(key, queryParams.get(key));
214
215                             }
216                         }
217                         p.processContainer(introspector, type, uriKeys, isFinalContainer);
218                         i++;
219                     } else {
220                         p.processNamespace(introspector);
221                         // namespace case
222                         i++;
223                     }
224                     previousObj = introspector;
225                 } else {
226                     // invalid item found should log
227                     // original said bad path
228                     throw new AAIException("AAI_3001", "invalid item found in path: " + part);
229                 }
230             }
231         } catch (AAIException e) {
232             throw e;
233         } catch (Exception e) {
234             throw new AAIException("AAI_3001", e);
235         }
236     }
237
238     public boolean validate() throws UnsupportedEncodingException, AAIException {
239         this.parse(new URIValidate());
240         return true;
241     }
242
243     /**
244      * Handle cloud region.
245      *
246      * @param action the action
247      * @param uri the uri
248      * @return the uri
249      */
250     protected URI handleCloudRegion(String action, URI uri) {
251
252         return uri;
253
254     }
255
256     /**
257      * Trim URI.
258      *
259      * @param uri the uri
260      * @return the uri
261      */
262     protected URI trimURI(URI uri) {
263
264         String result = uri.getRawPath();
265         if (result.startsWith("/")) {
266             result = result.substring(1, result.length());
267         }
268
269         if (result.endsWith("/")) {
270             result = result.substring(0, result.length() - 1);
271         }
272
273         // TODO - Check if this makes to do for model driven for base uri path
274         result = result.replaceFirst("[a-z][a-z]*/v\\d+/", "");
275
276         return UriBuilder.fromPath(result).build();
277     }
278
279 }