b4878e386d7b30fa2456c6a3a21551ede5a28d5f
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / introspection / tools / IntrospectorValidator.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.onap.aai.introspection.tools;
23
24 import org.onap.aai.db.props.AAIProperties;
25 import org.onap.aai.exceptions.AAIException;
26 import org.onap.aai.introspection.Introspector;
27 import org.onap.aai.introspection.IntrospectorWalker;
28 import org.onap.aai.introspection.Visibility;
29 import org.onap.aai.introspection.Wanderer;
30 import org.onap.aai.schema.enums.PropertyMetadata;
31
32 import java.util.*;
33
34 public class IntrospectorValidator implements Wanderer {
35
36         
37         private List<Issue> issues = null;
38         private List<IssueResolver> issueResolvers = null; 
39         private boolean validateRequired = true;
40         private final int maximumDepth;
41         private int currentDepth = 0;
42         
43         private final Set<String> relationshipChain;
44         /**
45          * Instantiates a new introspector validator.
46          *
47          * @param builder the builder
48          */
49         private IntrospectorValidator(Builder builder) {
50                 this.validateRequired = builder.getValidateRequired();
51                 this.issueResolvers = builder.getResolvers();
52                 this.maximumDepth = builder.getMaximumDepth();
53                 issues = new ArrayList<>();
54                 
55                 relationshipChain = new HashSet<>();
56                 
57                 relationshipChain.add("relationship-list");
58                 relationshipChain.add("relationship");
59                 relationshipChain.add("relationship-data");
60                 relationshipChain.add("related-to-property");
61
62
63         }
64         
65         /**
66          * Validate.
67          *
68          * @param obj the obj
69          * @return true, if successful
70          * @throws AAIException 
71          */
72         public boolean validate(Introspector obj) throws AAIException {
73                 IntrospectorWalker walker = new IntrospectorWalker(this);
74                 this.currentDepth = 0;
75                 walker.walk(obj);
76                 
77                 for (Issue m : issues) {
78                         if (!m.getSeverity().equals(Severity.WARNING)) {
79                                 return false;
80                         }
81                 }
82                 
83                 return true;
84         }
85         
86         /**
87          * Gets the issues.
88          *
89          * @return the issues
90          */
91         public List<Issue> getIssues() {
92                 return this.issues;
93         }
94         
95         /**
96          * Sets the issue resolvers.
97          *
98          * @param resolvers the new issue resolvers
99          */
100         public void setIssueResolvers(List<IssueResolver> resolvers) {
101                 issueResolvers = new ArrayList<>();
102                 for (IssueResolver resolver : resolvers) {
103                         issueResolvers.add(resolver);
104                 }
105         }
106         
107         /**
108          * Resolve issues.
109          *
110          * @return true, if successful
111          */
112         public boolean resolveIssues() {
113                 boolean result = true;
114                 for (Issue issue : issues) {
115                         for (IssueResolver resolver : issueResolvers) {
116                                 if (resolver.resolveIssue(issue)) {
117                                         issue.setResolved(true);
118                                 }
119                         }
120                         if (!issue.isResolved()) {
121                                 result = false;
122                         }
123                 }
124                 
125                 return result;
126         }
127         
128         /**
129          * {@inheritDoc}
130          */
131         @Override
132         public void processComplexObj(Introspector obj) {
133                                 
134                 if (this.currentDepth > this.maximumDepth && !relationshipChain.contains(obj.getDbName())) {
135                         Issue message = 
136                                         this.buildMessage(Severity.CRITICAL, IssueType.EXCEEDED_ALLOWED_DEPTH, "Maximum allowed depth of this object has been exceeded on: " + obj.getDbName());
137                         message.setIntrospector(obj);
138                         issues.add(message);
139                 }
140                 Set<String> requiredProps = obj.getRequiredProperties();
141                 Set<String> keys = obj.getKeys();
142                 Set<String> props = obj.getProperties();
143                 
144                 for (String prop : props) {
145                         Object value = obj.getValue(prop);
146                         if (keys.contains(prop)) {
147                                 if (value == null) {
148                                         Issue message = 
149                                                         this.buildMessage(Severity.CRITICAL, IssueType.MISSING_KEY_PROP, "Missing key property: " + prop);
150                                         message.setIntrospector(obj);
151                                         message.setPropName(prop);
152                                         issues.add(message);
153                                 }
154                         } else if (requiredProps.contains(prop)) {
155                                 if (value == null && validateRequired) {
156                                         Issue message = 
157                                                         this.buildMessage(Severity.CRITICAL, IssueType.MISSING_REQUIRED_PROP, "Missing required property: " + prop);
158                                         message.setIntrospector(obj);
159                                         message.setPropName(prop);
160                                         issues.add(message);
161                                 }
162                         }
163                         
164                         final Optional<String> visibility = obj.getPropertyMetadata(prop, PropertyMetadata.VISIBILITY);
165                         if(visibility.isPresent() && Visibility.internal.equals(Visibility.valueOf(visibility.get())) && obj.getValue(prop) != null) {
166                                 Issue message =
167                                                 this.buildMessage(Severity.ERROR, IssueType.PROPERTY_NOT_VISIBLE, "client attemptted to set property not visible: " + prop);
168                                 message.setIntrospector(obj);
169                                 message.setPropName(prop);
170                                 issues.add(message);
171                                 
172                         }
173                         final Optional<String> requires = obj.getPropertyMetadata(prop, PropertyMetadata.REQUIRES);
174                         if (requires.isPresent() && (obj.getValue(prop) != null && obj.getValue(requires.get()) == null)) {
175                                 Issue message =
176                                                 this.buildMessage(Severity.CRITICAL, IssueType.DEPENDENT_PROP_NOT_FOUND, prop + " requires " + requires.get() + " to also be popluated.");
177                                 message.setIntrospector(obj);
178                                 message.setPropName(prop);
179                                 issues.add(message);
180                         }
181                 }
182                 
183                 if (!relationshipChain.contains(obj.getDbName())) {
184                         this.currentDepth++;
185                 }
186
187         }
188
189         /**
190          * {@inheritDoc}
191          */
192         @Override
193         public void processPrimitive(String propName, Introspector obj) {
194                 //NO OP
195         }
196
197         /**
198          * {@inheritDoc}
199          */
200         @Override
201         public void processPrimitiveList(String propName, Introspector obj) {
202                 //NO OP
203         }
204
205         /**
206          * {@inheritDoc}
207          */
208         @Override
209         public void modifyComplexList(List<Introspector> list, List<Object> listReference, Introspector parent, Introspector child) {
210                 //NO OP
211         }
212         
213         
214         /**
215          * Builds the message.
216          *
217          * @param severity the severity
218          * @param error the error
219          * @param detail the detail
220          * @return the issue
221          */
222         private Issue buildMessage(Severity severity, IssueType error, String detail) {
223                 Issue message = new Issue();
224                 message.setSeverity(severity);
225                 message.setType(error);
226                 message.setDetail(detail);
227                 
228                 return message;
229         }
230
231         /**
232          * {@inheritDoc}
233          */
234         @Override
235         public boolean createComplexObjIfNull() {
236                 return false;
237         }
238
239         /**
240          * {@inheritDoc}
241          */
242         @Override
243         public int createComplexListSize(Introspector parent, Introspector child) {
244                 return 0;
245         }
246         
247         public static class Builder {
248                 
249                 private boolean validateRequired = true;
250                 private List<IssueResolver> issueResolvers = null;
251                 private int maximumDepth = AAIProperties.MAXIMUM_DEPTH;
252                 /**
253                  * Instantiates a new builder.
254                  *
255                  * @param llBuilder the ll builder
256                  */
257                 public Builder() {
258                         issueResolvers = new ArrayList<IssueResolver>();
259                 }
260                 
261                 /**
262                  * Validate required.
263                  *
264                  * @param validateRequired the validate required
265                  * @return the builder
266                  */
267                 public Builder validateRequired(boolean validateRequired) {
268                         this.validateRequired = validateRequired;
269                         return this;
270                 }
271                 
272                 public Builder restrictDepth(int depth) {
273                         this.maximumDepth = depth; 
274                         return this;
275                 }
276                 /**
277                  * Adds the resolver.
278                  *
279                  * @param resolver the resolver
280                  * @return the builder
281                  */
282                 public Builder addResolver(IssueResolver resolver) {
283                         issueResolvers.add(resolver);
284                         return this;
285                 }
286                 
287                 /**
288                  * Builds the.
289                  *
290                  * @return the introspector validator
291                  */
292                 public IntrospectorValidator build() {
293                         return new IntrospectorValidator(this);
294                 }
295                 
296                 /**
297                  * Gets the validate required.
298                  *
299                  * @return the validate required
300                  */
301                 public boolean getValidateRequired() {
302                         return this.validateRequired;
303                 }
304                 
305                 /**
306                  * Gets the resolvers.
307                  *
308                  * @return the resolvers
309                  */
310                 public List<IssueResolver> getResolvers() {
311                         return this.issueResolvers;
312                 }
313                 
314                 public int getMaximumDepth() {
315                         return this.maximumDepth;
316                 }
317         }
318         
319 }