[AAI-26] Adding gizmo data to the repository.
[aai/gizmo.git] / src / main / java / org / openecomp / schema / OxmModelValidator.java
1 /**
2  * ============LICENSE_START=======================================================
3  * Gizmo
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property.
6  * Copyright © 2017 Amdocs
7  * All rights reserved.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  *
22  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23  */
24 package org.openecomp.schema;
25
26 import com.google.common.base.CaseFormat;
27 import com.google.gson.JsonElement;
28 import com.google.gson.JsonNull;
29
30 import org.eclipse.persistence.dynamic.DynamicType;
31 import org.eclipse.persistence.internal.helper.DatabaseField;
32 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
33 import org.eclipse.persistence.mappings.DatabaseMapping;
34 import org.eclipse.persistence.oxm.XMLField;
35 import org.openecomp.crud.entity.Vertex;
36 import org.openecomp.crud.exception.CrudException;
37 import org.openecomp.crud.util.CrudServiceUtil;
38
39 import java.util.HashMap;
40 import java.util.Map;
41 import java.util.Set;
42 import javax.ws.rs.core.Response.Status;
43
44 public class OxmModelValidator {
45   public enum Metadata {
46     NODE_TYPE("aai-node-type"),
47     URI("aai-uri"),
48     CREATED_TS("aai-created-ts"),
49     SOT("source-of-truth"),
50     LAST_MOD_SOT("last-mod-source-of-truth");
51
52     private final String propName;
53
54     Metadata(String propName) {
55       this.propName = propName;
56     }
57
58     public String propertyName() {
59       return propName;
60     }
61
62     public static boolean isProperty(String property) {
63       for (Metadata meta : Metadata.values()) {
64         if (meta.propName.equals(property)) {
65           return true;
66         }
67       }
68       return false;
69     }
70   }
71
72
73   public static Map<String, Object> resolveCollectionfilter(String version, String type,
74                                                             Map<String, String> filter)
75       throws CrudException {
76
77     DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
78
79     Map<String, Object> result = new HashMap<String, Object>();
80     if (jaxbContext == null) {
81       throw new CrudException("", Status.NOT_FOUND);
82     }
83     final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
84         .to(CaseFormat.UPPER_CAMEL,
85         CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
86
87     for (String key : filter.keySet()) {
88       String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key);
89       if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) {
90         try {
91           DatabaseMapping mapping = modelObjectType.getDescriptor()
92               .getMappingForAttributeName(keyJavaName);
93           Object value = CrudServiceUtil.validateFieldType(filter.get(key),
94               mapping.getField().getType());
95           result.put(key, value);
96         } catch (Exception ex) {
97           // Skip any exceptions thrown while validating the filter
98           // key value
99           continue;
100         }
101       }
102     }
103
104     return result;
105
106   }
107
108   public static String resolveCollectionType(String version, String type) throws CrudException {
109
110     DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
111
112     if (jaxbContext == null) {
113       throw new CrudException("", Status.NOT_FOUND);
114     }
115     // Determine if the Object part is a collection type in the model
116     // definition
117     final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL
118         .to(CaseFormat.UPPER_CAMEL,
119         CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)));
120
121     if (modelObjectType == null) {
122       throw new CrudException("", Status.NOT_FOUND);
123     }
124
125     if (modelObjectType.getDescriptor().getMappings().size() == 1
126         && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) {
127       String childJavaObjectName = modelObjectType.getDescriptor().getMappings()
128           .get(0).getAttributeName();
129       childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName);
130       final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName);
131       if (childObjectType == null) {
132         // Should not happen as child object is defined in oxm model
133         // itself
134         throw new CrudException("", Status.NOT_FOUND);
135       }
136       return childObjectType.getDescriptor().getTableName();
137     } else {
138       return modelObjectType.getDescriptor().getTableName();
139     }
140
141   }
142
143
144   public static Vertex validateIncomingUpsertPayload(String id, String version, String type,
145                                                      JsonElement properties)
146       throws CrudException {
147
148     try {
149       type = resolveCollectionType(version, type);
150       DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
151       String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
152           CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
153
154       final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
155
156       Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
157           .entrySet();
158
159       //loop through input to validate against schema
160       for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
161         String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
162
163         // check for valid field
164         if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
165           throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
166         }
167
168       }
169
170       Map<String, JsonElement> entriesMap = new HashMap<String, JsonElement>();
171       for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
172         entriesMap.put(entry.getKey(), entry.getValue());
173       }
174
175       Vertex.Builder modelVertexBuilder = new Vertex.Builder(type);
176       if (id != null) {
177         modelVertexBuilder.id(id);
178       }
179       for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) {
180         if (mapping.isAbstractDirectMapping()) {
181           DatabaseField field = mapping.getField();
182           String defaultValue = mapping.getProperties().get("defaultValue") == null ? ""
183               : mapping.getProperties().get("defaultValue").toString();
184
185           String keyName = field.getName().substring(0, field.getName().indexOf("/"));
186
187           if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
188               && !defaultValue.isEmpty()) {
189             modelVertexBuilder.property(keyName,
190                 CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
191           }
192           // if schema field is required and not set then reject
193           if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName)
194               && defaultValue.isEmpty()) {
195             throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST);
196           }
197           // If invalid field then reject
198           if (entriesMap.containsKey(keyName)) {
199             Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName)
200                 .getAsString(), field.getType());
201             modelVertexBuilder.property(keyName, value);
202           }
203
204           // Set defaults
205           if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) {
206             modelVertexBuilder.property(keyName,
207                 CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
208           }
209         }
210       }
211
212       return modelVertexBuilder.build();
213     } catch (Exception e) {
214       throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
215     }
216   }
217
218   public static Vertex validateIncomingPatchPayload(String id, String version, String type,
219                                                     JsonElement properties, Vertex existingVertex)
220       throws CrudException {
221
222     try {
223       type = resolveCollectionType(version, type);
224       DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
225       String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
226           CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type));
227
228       final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
229
230       Set<Map.Entry<String, JsonElement>> payloadEntriesSet = properties.getAsJsonObject()
231           .entrySet();
232
233       // Loop through the payload properties and merge with existing
234       // vertex props
235       for (Map.Entry<String, JsonElement> entry : payloadEntriesSet) {
236
237         String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey());
238
239         // check for valid field
240         if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) {
241           throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST);
242         }
243
244         DatabaseField field = modelObjectType.getDescriptor()
245             .getMappingForAttributeName(keyJavaName).getField();
246         String defaultValue = modelObjectType.getDescriptor()
247             .getMappingForAttributeName(keyJavaName)
248             .getProperties().get("defaultValue") == null ? ""
249             : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName)
250             .getProperties().get("defaultValue").toString();
251
252         // check if mandatory field is not set to null
253         if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
254             && !defaultValue.isEmpty()) {
255           existingVertex.getProperties().put(entry.getKey(),
256               CrudServiceUtil.validateFieldType(defaultValue, field.getType()));
257         } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
258             && defaultValue.isEmpty()) {
259           throw new CrudException("Mandatory field: " + entry.getKey()
260               + " can't be set to null",
261               Status.BAD_REQUEST);
262         } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull
263             && existingVertex.getProperties().containsKey(entry.getKey())) {
264           existingVertex.getProperties().remove(entry.getKey());
265         } else if (!(entry.getValue() instanceof JsonNull)) {
266           // add/update the value if found in existing vertex
267           Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(),
268               field.getType());
269           existingVertex.getProperties().put(entry.getKey(), value);
270         }
271
272       }
273
274       return existingVertex;
275     } catch (Exception e) {
276       throw new CrudException(e.getMessage(), Status.BAD_REQUEST);
277     }
278
279   }
280
281   private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) {
282     for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) {
283       int ix = field.getName().indexOf("/");
284       if (ix <= 0) {
285         ix = field.getName().length();
286       }
287
288       String keyName = field.getName().substring(0, ix);
289       if (fieldName.equals(keyName)) {
290         return field;
291       }
292     }
293     return null;
294   }
295
296   public static Vertex validateOutgoingPayload(String version, Vertex vertex) {
297
298     Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType())
299         .id(vertex.getId().get());
300
301     try {
302       DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version);
303       String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
304           CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL,
305               vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null
306                   ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType()));
307       final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass);
308
309       for (String key : vertex.getProperties().keySet()) {
310         DatabaseField field = getDatabaseField(key, modelObjectType);
311         if (field != null) {
312           if (!Metadata.isProperty(key)) {
313             modelVertexBuilder.property(key, vertex.getProperties().get(key));
314           }
315         }
316       }
317       return modelVertexBuilder.build();
318     } catch (Exception ex) {
319       return vertex;
320     }
321
322   }
323
324
325 }