Spring-boot 3.1 update
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / parsers / relationship / RelationshipToURI.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.relationship;
22
23 import java.io.UnsupportedEncodingException;
24 import java.net.URI;
25 import java.net.URISyntaxException;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Optional;
30
31 import jakarta.ws.rs.core.UriBuilder;
32
33 import org.apache.tinkerpop.gremlin.structure.Direction;
34 import org.onap.aai.config.SpringContextAware;
35 import org.onap.aai.edges.EdgeIngestor;
36 import org.onap.aai.edges.EdgeRule;
37 import org.onap.aai.edges.EdgeRuleQuery;
38 import org.onap.aai.edges.enums.AAIDirection;
39 import org.onap.aai.edges.enums.EdgeType;
40 import org.onap.aai.edges.exceptions.AmbiguousRuleChoiceException;
41 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
42 import org.onap.aai.exceptions.AAIException;
43 import org.onap.aai.introspection.Introspector;
44 import org.onap.aai.introspection.IntrospectorFactory;
45 import org.onap.aai.introspection.Loader;
46 import org.onap.aai.introspection.ModelType;
47 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
48 import org.onap.aai.parsers.exceptions.AAIIdentityMapParseException;
49 import org.onap.aai.parsers.exceptions.AmbiguousMapAAIException;
50 import org.onap.aai.parsers.uri.URIParser;
51 import org.onap.aai.schema.enums.ObjectMetadata;
52 import org.onap.aai.setup.SchemaVersions;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.context.ApplicationContext;
56
57 /**
58  * The Class RelationshipToURI.
59  */
60 public class RelationshipToURI {
61
62     private static final Logger LOGGER = LoggerFactory.getLogger(RelationshipToURI.class);
63
64     private Introspector relationship = null;
65
66     private Loader loader = null;
67
68     private ModelType modelType = null;
69
70     private EdgeIngestor edgeRules = null;
71
72     private URI uri = null;
73
74     private SchemaVersions schemaVersions;
75
76     /**
77      * Instantiates a new relationship to URI.
78      *
79      * @param loader the loader
80      * @param relationship the relationship
81      * @throws UnsupportedEncodingException the unsupported encoding exception
82      * @throws AAIException the AAI exception
83      */
84     public RelationshipToURI(Loader loader, Introspector relationship)
85             throws UnsupportedEncodingException, AAIException {
86         this.relationship = relationship;
87         this.modelType = relationship.getModelType();
88         this.loader = loader;
89         this.initEdgeIngestor();
90         this.parse();
91
92     }
93
94     protected void initEdgeIngestor() {
95         // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
96         ApplicationContext ctx = SpringContextAware.getApplicationContext();
97         edgeRules = ctx.getBean(EdgeIngestor.class);
98         schemaVersions = (SchemaVersions) ctx.getBean("schemaVersions");
99     }
100
101     /**
102      * Parses the.
103      * 
104      * @throws
105      *
106      *         @throws UnsupportedEncodingException the unsupported encoding exception
107      * @throws AAIException the AAI exception
108      */
109     protected void parse() throws AAIException {
110         String relatedLink = (String) relationship.getValue("related-link");
111         Optional<URI> result;
112         try {
113             if (loader.getVersion().compareTo(schemaVersions.getRelatedLinkVersion()) >= 0) {
114                 result = processRelatedLink(relatedLink);
115                 if (result.isEmpty()) {
116                     result = processRelationshipData();
117                 }
118             } else {
119                 result = processRelationshipData();
120                 if (result.isEmpty()) {
121                     result = processRelatedLink(relatedLink);
122                 }
123             }
124             if (result.isPresent()) {
125                 this.uri = result.get();
126             } else {
127                 throw new AAIIdentityMapParseException("nothing to parse");
128             }
129         } catch (AAIException e) {
130             throw e;
131         } catch (Exception e) {
132             throw new AAIIdentityMapParseException("Could not parse relationship-list object: " + e.getMessage(), e);
133         }
134
135     }
136
137     private Optional<URI> processRelationshipData() throws AAIException, UnsupportedEncodingException {
138         Optional<URI> result = Optional.empty();
139         StringBuilder uriBuilder = new StringBuilder();
140         List<Object> data = (List<Object>) relationship.getValue("relationship-data");
141         Introspector wrapper;
142         String key;
143         String value;
144         String objectType;
145         String propertyName;
146         String topLevelType = null;
147         String[] split;
148         HashMap<String, Introspector> map = new HashMap<>();
149         for (Object datum : data) {
150             wrapper = IntrospectorFactory.newInstance(modelType, datum);
151             key = (String) wrapper.getValue("relationship-key");
152             value = (String) wrapper.getValue("relationship-value");
153             split = key.split("\\.");
154             if (split == null || split.length != 2) {
155                 throw new AAIIdentityMapParseException(
156                         "incorrect format for key must be of the form {node-type}.{property-name}");
157             }
158             // check node name ok
159             // check prop name ok
160             objectType = split[0];
161             propertyName = split[1];
162
163             try {
164                 Introspector wrappedObj = loader.introspectorFromName(objectType);
165
166                 if (!wrappedObj.hasProperty(propertyName)) {
167                     throw new AAIIdentityMapParseException("invalid property name in map: " + propertyName);
168                 }
169                 if (map.containsKey(objectType)) {
170                     wrappedObj = map.get(objectType);
171                 } else {
172                     map.put(objectType, wrappedObj);
173                 }
174                 if (wrappedObj.getValue(propertyName) == null) {
175                     wrappedObj.setValue(propertyName, value);
176                 } else {
177                     throw new AmbiguousMapAAIException(
178                             "cannot determine where key/value goes: " + propertyName + "/" + value);
179                 }
180
181                 if (wrappedObj.getMetadata(ObjectMetadata.NAMESPACE) != null) {
182                     if (topLevelType == null) {
183                         topLevelType = objectType;
184                     } else if (!topLevelType.equals(objectType)) {
185                         throw new AmbiguousMapAAIException(
186                                 "found two top level nodes of different types: " + topLevelType + " and " + objectType);
187                     }
188                 }
189             } catch (AAIUnknownObjectException e) {
190                 throw new AAIIdentityMapParseException("invalid object name in map: " + objectType, e);
191             }
192
193         }
194         if (!map.isEmpty()) {
195             String startType = (String) relationship.getValue("related-to");
196             List<String> nodeTypes = new ArrayList<>();
197             nodeTypes.addAll(map.keySet());
198
199             String displacedType;
200             for (int i = 0; i < nodeTypes.size(); i++) {
201                 if (nodeTypes.get(i).equals(startType)) {
202                     displacedType = nodeTypes.set(nodeTypes.size() - 1, startType);
203                     nodeTypes.set(i, displacedType);
204                     break;
205                 }
206             }
207             sortRelationships(nodeTypes, startType, 1);
208             int startTypeIndex = nodeTypes.indexOf(startType);
209             int topLevelIndex = 0;
210             if (topLevelType != null) {
211                 topLevelIndex = nodeTypes.indexOf(topLevelType);
212             }
213             // remove additional types not needed if they are there
214             List<String> nodeTypesSubList = nodeTypes;
215             if (topLevelIndex != 0) {
216                 nodeTypesSubList = nodeTypes.subList(topLevelIndex, startTypeIndex + 1);
217             }
218             for (String type : nodeTypesSubList) {
219                 uriBuilder.append(map.get(type).getURI());
220             }
221             if (!nodeTypesSubList.isEmpty()) {
222                 result = Optional.of(UriBuilder.fromPath(uriBuilder.toString()).build());
223             }
224         }
225         return result;
226     }
227
228     private Optional<URI> processRelatedLink(String relatedLink)
229             throws URISyntaxException, UnsupportedEncodingException, AAIIdentityMapParseException {
230         Optional<URI> result = Optional.empty();
231         if (relatedLink != null) {
232             URI resultUri = new URI(relatedLink);
233             String path = resultUri.toString();
234             resultUri = UriBuilder.fromPath(resultUri.getRawPath()).build();
235             URIParser uriParser = new URIParser(this.loader, resultUri);
236             try {
237                 uriParser.validate();
238             } catch (AAIException e) {
239                 throw new AAIIdentityMapParseException("related link is invalid: " + relatedLink, e);
240             }
241             result = Optional.of(resultUri);
242         }
243
244         return result;
245     }
246
247     /**
248      * Sort relationships.
249      *
250      * @param data the data
251      * @param startType the start type
252      * @param i the i
253      * @return true, if successful
254      * @throws AAIException
255      */
256     private boolean sortRelationships(List<String> data, String startType, int i) throws AAIException {
257
258         if (i == data.size()) {
259             return true;
260         }
261         int j;
262         String objectType;
263         String displacedObject;
264         EdgeRule rule;
265         Direction direction;
266         for (j = (data.size() - i) - 1; j >= 0; j--) {
267             objectType = data.get(j);
268             try {
269                 rule = edgeRules
270                         .getRule(new EdgeRuleQuery.Builder(startType, objectType).edgeType(EdgeType.TREE).build());
271                 direction = rule.getDirection();
272                 if (direction != null) {
273                     if ((rule.getContains().equals(AAIDirection.OUT.toString()) && direction.equals(Direction.IN))
274                             || (rule.getContains().equals(AAIDirection.IN.toString())
275                                     && direction.equals(Direction.OUT))) {
276                         displacedObject = data.set((data.size() - i) - 1, data.get(j));
277                         data.set(j, displacedObject);
278                         if (sortRelationships(data, objectType, i + 1)) {
279                             return true;
280                         } else {
281                             // continue to process
282                         }
283                     }
284                 }
285             } catch (AAIException | EdgeRuleNotFoundException | AmbiguousRuleChoiceException e) {
286                 // ignore exceptions generated
287                 continue;
288             }
289         }
290
291         return false;
292     }
293
294     /**
295      * Gets the uri.
296      *
297      * @return the uri
298      */
299     public URI getUri() {
300         return uri;
301     }
302
303 }