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