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