ced84bb2d6381ae49dfaa0c98386b5e6e2f08289
[aai/spike.git] / src / main / java / org / onap / aai / spike / schema / GraphEventTransformer.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.spike.schema;
22
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.Set;
26 import com.google.common.base.CaseFormat;
27 import com.google.gson.JsonElement;
28 import com.google.gson.JsonObject;
29 import org.eclipse.persistence.dynamic.DynamicType;
30 import org.eclipse.persistence.internal.helper.DatabaseField;
31 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
32 import org.eclipse.persistence.mappings.DatabaseMapping;
33 import org.eclipse.persistence.oxm.XMLField;
34 import org.onap.aai.cl.eelf.LoggerFactory;
35 import org.onap.aai.spike.event.incoming.GizmoEdge;
36 import org.onap.aai.spike.event.incoming.GizmoGraphEvent;
37 import org.onap.aai.spike.event.incoming.GizmoVertex;
38 import org.onap.aai.spike.exception.SpikeException;
39
40 /**
41  * This class is responsible for transforming raw graph entities (such as vertices and edges) into
42  * representations which correspond to the OXM models.
43  */
44 public class GraphEventTransformer {
45
46     private static org.onap.aai.cl.api.Logger logger =
47             LoggerFactory.getInstance().getLogger(GraphEventTransformer.class.getName());
48     private static final String AAI_UUID = "aai-uuid";
49
50     /**
51      * 
52      * @param rawVertex
53      * @throws SpikeException
54      */
55     public static void validateVertexModel(GizmoVertex rawVertex) throws SpikeException {
56
57         validateVertexModel(OXMModelLoader.getLatestVersion(), rawVertex);
58     }
59
60     public static void populateUUID(GizmoGraphEvent event) throws SpikeException {
61         try {
62             if (event.getVertex() != null) {
63                 if (event.getVertex().getProperties().getAsJsonObject().has(AAI_UUID)) {
64                     event.getVertex()
65                             .setId(event.getVertex().getProperties().getAsJsonObject().get(AAI_UUID).getAsString());
66                 }
67             } else if (event.getRelationship() != null) {
68                 if (event.getRelationship().getProperties().getAsJsonObject().has(AAI_UUID)) {
69                     event.getRelationship().setId(
70                             event.getRelationship().getProperties().getAsJsonObject().get(AAI_UUID).getAsString());
71                 }
72
73                 if (event.getRelationship().getSource().getProperties().getAsJsonObject().has(AAI_UUID)) {
74                     event.getRelationship().getSource().setId(event.getRelationship().getSource().getProperties()
75                             .getAsJsonObject().get(AAI_UUID).getAsString());
76                 }
77                 if (event.getRelationship().getTarget().getProperties().getAsJsonObject().has(AAI_UUID)) {
78                     event.getRelationship().getTarget().setId(event.getRelationship().getTarget().getProperties()
79                             .getAsJsonObject().get(AAI_UUID).getAsString());
80                 }
81             }
82         } catch (Exception ex) {
83             throw new SpikeException("Unable to parse uuid in incoming event");
84         }
85     }
86
87     /**
88      * 
89      * @param version
90      * @param rawVertex
91      * @throws SpikeException
92      */
93     public static void validateVertexModel(String version, GizmoVertex rawVertex) throws SpikeException {
94
95         try {
96
97             DynamicJAXBContext jaxbContext = OXMModelLoader.getContextForVersion(version);
98             String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
99                     CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, rawVertex.getType()));
100             final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
101             final DynamicType reservedType = jaxbContext.getDynamicType("ReservedPropNames");
102
103             Set<Map.Entry<String, JsonElement>> vertexEntriesSet =
104                     rawVertex.getProperties().getAsJsonObject().entrySet();
105             Map<String, JsonElement> vertexEntriesMap = new HashMap<String, JsonElement>();
106             for (Map.Entry<String, JsonElement> entry : vertexEntriesSet) {
107                 vertexEntriesMap.put(entry.getKey(), entry.getValue());
108             }
109
110             JsonObject modelJsonElement = new JsonObject();
111             // Iterate over all of the attributes specified in the model schema,
112             // populating
113             // our dynamic instance with the corresponding values supplied in
114             // our raw vertex.
115             for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
116                 if (mapping.isAbstractDirectMapping()) {
117                     DatabaseField f = mapping.getField();
118                     String keyName = f.getName().substring(0, f.getName().indexOf("/"));
119
120                     String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
121                             : mapping.getProperties().get("defaultValue").toString();
122
123                     if (((XMLField) f).isRequired() && !vertexEntriesMap.containsKey(keyName)
124                             && !defaultValue.isEmpty()) {
125                         modelJsonElement.addProperty(keyName, defaultValue);
126
127                     }
128                     // If this is a required field, but is not present in the
129                     // raw vertex, reject this
130                     // as an invalid input since we can't build a valid object
131                     // from what we were provided.
132                     if (((XMLField) f).isRequired() && !vertexEntriesMap.containsKey(keyName)
133                             && defaultValue.isEmpty()) {
134                         throw new SpikeException("Missing required field: " + keyName);
135                     }
136
137                     // If this is a non-required field, then set it if a value
138                     // was provided in the
139                     // raw vertex.
140                     if (vertexEntriesMap.containsKey(keyName)) {
141                         validateFieldType(vertexEntriesMap.get(keyName), f.getType());
142                         modelJsonElement.add(keyName, vertexEntriesMap.get(keyName));
143                     }
144                 }
145             }
146
147             // Ensure any of the reserved properties are added to the payload
148             for (DatabaseMapping mapping : reservedType.getDescriptor().getMappings()) {
149                 if (mapping.isAbstractDirectMapping()) {
150                     DatabaseField field = mapping.getField();
151                     String keyName = field.getName().substring(0, field.getName().indexOf("/"));
152
153                     if (vertexEntriesMap.containsKey(keyName)) {
154                         validateFieldType(vertexEntriesMap.get(keyName), field.getType());
155                         modelJsonElement.add(keyName, vertexEntriesMap.get(keyName));
156                     }
157                 }
158             }
159
160             rawVertex.setProperties(modelJsonElement);
161         } catch (Exception e) {
162             throw new SpikeException(e.getMessage());
163         }
164     }
165
166     /**
167      * 
168      * @param rawEdge
169      * @throws SpikeException
170      */
171     public static void validateEdgeModel(GizmoEdge rawEdge) throws SpikeException {
172
173         validateEdgeModel(EdgeRulesLoader.getLatestSchemaVersion(), rawEdge);
174     }
175
176     /**
177      * 
178      * @param version
179      * @param rawEdge
180      * @throws SpikeException
181      */
182     public static void validateEdgeModel(String version, GizmoEdge rawEdge) throws SpikeException {
183
184         if (logger.isDebugEnabled()) {
185             logger.debug("Convert edge: " + rawEdge.toString() + " to model version: " + version);
186         }
187
188         // Get the relationship schema for the supplied version.
189         RelationshipSchema schema = EdgeRulesLoader.getSchemaForVersion(version);
190
191         try {
192
193             // Validate that our edge does have the necessary endpoints.
194             if (rawEdge.getSource() == null || rawEdge.getTarget() == null) {
195                 throw new SpikeException("Source or target endpoint not specified");
196             }
197
198             // Create a key based on source:target:relationshipType
199             String sourceNodeType = rawEdge.getSource().getType();
200             String targetNodeType = rawEdge.getTarget().getType();
201             String key = sourceNodeType + ":" + targetNodeType + ":" + rawEdge.getType();
202
203             // Now, look up the specific schema model based on the key we just
204             // constructed.
205             Map<String, Class<?>> relationshipModel = schema.lookupRelation(key);
206             if (relationshipModel == null || relationshipModel.isEmpty()) {
207                 throw new SpikeException("Invalid source/target/relationship type: " + key);
208             }
209
210             Set<Map.Entry<String, JsonElement>> edgeEntriesSet = rawEdge.getProperties().getAsJsonObject().entrySet();
211             Map<String, JsonElement> edgeEntriesMap = new HashMap<String, JsonElement>();
212             for (Map.Entry<String, JsonElement> entry : edgeEntriesSet) {
213                 edgeEntriesMap.put(entry.getKey(), entry.getValue());
214             }
215
216             JsonObject modelJsonElement = new JsonObject();
217
218             for (String property : relationshipModel.keySet()) {
219
220                 if (!edgeEntriesMap.containsKey(property)) {
221                     throw new SpikeException("Missing required field: " + property);
222                 }
223
224                 validateFieldType(edgeEntriesMap.get(property), relationshipModel.get(property));
225                 modelJsonElement.add(property, edgeEntriesMap.get(property));
226
227             }
228
229             rawEdge.setProperties(modelJsonElement);
230
231
232         } catch (Exception ex) {
233             throw new SpikeException(ex.getMessage());
234         }
235     }
236
237     @SuppressWarnings("unchecked")
238     public static Object validateFieldType(JsonElement value, Class clazz) throws SpikeException {
239         try {
240             if (clazz.isAssignableFrom(Integer.class)) {
241                 return value.getAsInt();
242             } else if (clazz.isAssignableFrom(Long.class)) {
243                 return value.getAsLong();
244             } else if (clazz.isAssignableFrom(Float.class)) {
245                 return value.getAsFloat();
246             } else if (clazz.isAssignableFrom(Double.class)) {
247                 return value.getAsDouble();
248             } else if (clazz.isAssignableFrom(Boolean.class)) {
249                 return value.getAsBoolean();
250             } else {
251                 return value;
252             }
253
254         } catch (Exception e) {
255             throw new SpikeException("Invalid property value: " + value);
256         }
257     }
258
259     public static Object validateFieldType(String value, Class clazz) throws SpikeException {
260         try {
261             if (clazz.isAssignableFrom(Integer.class)) {
262                 return Integer.parseInt(value);
263             } else if (clazz.isAssignableFrom(Long.class)) {
264                 return Long.parseLong(value);
265             } else if (clazz.isAssignableFrom(Float.class)) {
266                 return Float.parseFloat(value);
267             } else if (clazz.isAssignableFrom(Double.class)) {
268                 return Double.parseDouble(value);
269             } else if (clazz.isAssignableFrom(Boolean.class)) {
270                 if (!value.equals("true") && !value.equals("false")) {
271                     throw new SpikeException("Invalid property value: " + value);
272                 }
273                 return Boolean.parseBoolean(value);
274             } else {
275                 return value;
276             }
277         } catch (Exception e) {
278             throw new SpikeException("Invalid property value: " + value);
279         }
280     }
281
282 }