Merge "Reduced code complexity"
[cli.git] / framework / src / main / java / org / onap / cli / fw / schema / OnapCommandSchemaLoader.java
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
3  * Copyright 2020 Nokia
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.onap.cli.fw.schema;
19
20 import static org.onap.cli.fw.conf.OnapCommandConstants.ATTRIBUTES;
21 import static org.onap.cli.fw.conf.OnapCommandConstants.BOOLEAN_TRUE;
22 import static org.onap.cli.fw.conf.OnapCommandConstants.COMMAND_TYPE_VALUES;
23 import static org.onap.cli.fw.conf.OnapCommandConstants.DEFAULT_PARAMETER_FILE_NAME;
24 import static org.onap.cli.fw.conf.OnapCommandConstants.DEFAULT_VALUE;
25 import static org.onap.cli.fw.conf.OnapCommandConstants.DESCRIPTION;
26 import static org.onap.cli.fw.conf.OnapCommandConstants.DIRECTION;
27 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO;
28 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_AUTHOR;
29 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_IGNORE;
30 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_METADATA;
31 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_PARAMS_LIST;
32 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_PARAMS_MANDATORY_LIST;
33 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_PRODUCT;
34 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_SERVICE;
35 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_STATE;
36 import static org.onap.cli.fw.conf.OnapCommandConstants.INFO_TYPE;
37 import static org.onap.cli.fw.conf.OnapCommandConstants.INPUT_PARAMS_LIST;
38 import static org.onap.cli.fw.conf.OnapCommandConstants.INPUT_PARAMS_MANDATORY_LIST;
39 import static org.onap.cli.fw.conf.OnapCommandConstants.IS_DEFAULT_ATTR;
40 import static org.onap.cli.fw.conf.OnapCommandConstants.IS_DEFAULT_PARAM;
41 import static org.onap.cli.fw.conf.OnapCommandConstants.IS_INCLUDE;
42 import static org.onap.cli.fw.conf.OnapCommandConstants.IS_OPTIONAL;
43 import static org.onap.cli.fw.conf.OnapCommandConstants.IS_SECURED;
44 import static org.onap.cli.fw.conf.OnapCommandConstants.LONG_OPTION;
45 import static org.onap.cli.fw.conf.OnapCommandConstants.NAME;
46 import static org.onap.cli.fw.conf.OnapCommandConstants.OPEN_CLI_SCHEMA_VERSION;
47 import static org.onap.cli.fw.conf.OnapCommandConstants.PARAMETERS;
48 import static org.onap.cli.fw.conf.OnapCommandConstants.RESULTS;
49 import static org.onap.cli.fw.conf.OnapCommandConstants.RESULT_PARAMS_LIST;
50 import static org.onap.cli.fw.conf.OnapCommandConstants.RESULT_PARAMS_MANDATORY_LIST;
51 import static org.onap.cli.fw.conf.OnapCommandConstants.SCHEMA_FILE_NOT_EXIST;
52 import static org.onap.cli.fw.conf.OnapCommandConstants.SCHEMA_FILE_WRONG_EXTN;
53 import static org.onap.cli.fw.conf.OnapCommandConstants.SCHEMA_PATH_PATERN;
54 import static org.onap.cli.fw.conf.OnapCommandConstants.SCOPE;
55 import static org.onap.cli.fw.conf.OnapCommandConstants.SHORT_OPTION;
56 import static org.onap.cli.fw.conf.OnapCommandConstants.TOP_LEVEL_MANDATORY_LIST;
57 import static org.onap.cli.fw.conf.OnapCommandConstants.TOP_LEVEL_PARAMS_LIST;
58 import static org.onap.cli.fw.conf.OnapCommandConstants.TYPE;
59
60 import java.io.File;
61 import java.io.FileInputStream;
62 import java.io.FileNotFoundException;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Set;
72
73 import org.onap.cli.fw.cmd.OnapCommand;
74 import org.onap.cli.fw.cmd.OnapCommandType;
75 import org.onap.cli.fw.conf.OnapCommandConfig;
76 import org.onap.cli.fw.conf.OnapCommandConstants;
77 import org.onap.cli.fw.error.OnapCommandException;
78 import org.onap.cli.fw.error.OnapCommandInvalidSchema;
79 import org.onap.cli.fw.error.OnapCommandInvalidSchemaVersion;
80 import org.onap.cli.fw.error.OnapCommandParameterNameConflict;
81 import org.onap.cli.fw.error.OnapCommandParameterOptionConflict;
82 import org.onap.cli.fw.error.OnapCommandSchemaNotFound;
83 import org.onap.cli.fw.info.OnapCommandInfo;
84 import org.onap.cli.fw.info.OnapCommandState;
85 import org.onap.cli.fw.input.OnapCommandParameter;
86 import org.onap.cli.fw.input.OnapCommandParameterType;
87 import org.onap.cli.fw.output.OnapCommandPrintDirection;
88 import org.onap.cli.fw.output.OnapCommandResultAttribute;
89 import org.onap.cli.fw.output.OnapCommandResultAttributeScope;
90 import org.onap.cli.fw.utils.OnapCommandDiscoveryUtils;
91 import org.onap.cli.fw.utils.OnapCommandUtils;
92 import org.springframework.core.io.Resource;
93
94 public class OnapCommandSchemaLoader {
95
96     private OnapCommandSchemaLoader() {
97         // As per the java guidelines
98     }
99
100     /**
101      * Validates schema version.
102      *
103      * @param schemaName schema name
104      * @param version    schema version
105      * @return map
106      * @throws OnapCommandInvalidSchemaVersion invalid schema version exception
107      * @throws OnapCommandInvalidSchema        invalid schema
108      * @throws OnapCommandSchemaNotFound       schema not found
109      */
110     public static Map<String, Object> validateSchemaVersion(String schemaName, String version) throws OnapCommandException {
111         Map<String, Object> values = null;
112         try {
113             InputStream inputStream = OnapCommandUtils.class.getClassLoader().getResourceAsStream(schemaName);
114
115             Resource resource = OnapCommandDiscoveryUtils.findResource(schemaName, SCHEMA_PATH_PATERN);
116
117             if (resource != null) {
118                 inputStream = resource.getInputStream();
119             }
120
121             if (inputStream == null) {
122                 inputStream = loadSchemaFromFile(schemaName);
123             }
124
125             values = loadSchema(inputStream, schemaName);
126             String schemaVersion = "";
127             if (values.keySet().contains(OPEN_CLI_SCHEMA_VERSION)) {
128                 Object obj = values.get(OPEN_CLI_SCHEMA_VERSION);
129                 schemaVersion = obj.toString();
130             }
131
132             if (!version.equals(schemaVersion)) {
133                 throw new OnapCommandInvalidSchemaVersion(schemaVersion);
134             }
135             inputStream.close();
136         } catch (IOException e) {
137             throw new OnapCommandSchemaNotFound(schemaName, e);
138         }
139         return values;
140     }
141
142     /**
143      * Retrieve OnapCommand from schema.
144      *
145      * @param cmd            OnapCommand
146      * @param schemaName     schema name
147      * @param includeDefault include if default
148      * @param validateSchema flag to represent validation
149      * @throws OnapCommandException  on error
150      */
151     public static List<String> loadSchema(OnapCommand cmd, String schemaName, boolean includeDefault,
152                                           boolean validateSchema) throws OnapCommandException {
153         try {
154             List<String> errors = new ArrayList<>();
155             if (includeDefault) {
156                 Map<String, ?> defaultParameterMap = validateSchemaVersion(DEFAULT_PARAMETER_FILE_NAME, cmd.getSchemaVersion());
157                 //mrkanag default_parameter is supported only for parameters.
158                 if (defaultParameterMap.containsKey(INFO)) {
159                     defaultParameterMap.remove(OnapCommandConstants.INFO);
160                 }
161
162                 errors.addAll(parseSchema(cmd, defaultParameterMap, validateSchema));
163             }
164
165             Map<String, Object> commandYamlMap = validateSchemaVersion(schemaName, cmd.getSchemaVersion());
166
167             errors.addAll(parseSchema(cmd, commandYamlMap, validateSchema));
168
169             return errors;
170         } catch (OnapCommandException e) {
171             throw e;
172         } catch (Exception e) {
173             throw new OnapCommandInvalidSchema(schemaName, e);
174         }
175     }
176
177     public static List<String> parseSchema(OnapCommand cmd,
178                                             final Map<String, ?> values,
179                                             boolean validate) throws OnapCommandException {
180
181         List<String> exceptionList = new ArrayList<>();
182         List<String> shortOptions = new ArrayList<>();
183         List<String> longOptions = new ArrayList<>();
184
185         if (validate) {
186             OnapCommandUtils.validateTags(exceptionList, values, OnapCommandConfig.getCommaSeparatedList(TOP_LEVEL_PARAMS_LIST),
187                     OnapCommandConfig.getCommaSeparatedList(TOP_LEVEL_MANDATORY_LIST), "root level");
188         }
189
190
191         List<String> sections = Arrays.asList(NAME, DESCRIPTION, INFO, PARAMETERS, RESULTS);
192
193         for (String key : sections) {
194
195             switch (key) {
196                 case NAME:
197                     Object val = values.get(key);
198                     if (val != null) {
199                         cmd.setName(val.toString());
200                     }
201                     break;
202
203                 case DESCRIPTION:
204                     Object description = values.get(key);
205                     if (description != null) {
206                         cmd.setDescription(description.toString());
207                     }
208                     break;
209
210                 case INFO:
211                     Map<String, String> infoMap = (Map<String, String>) values.get(key);
212
213                     if (infoMap != null) {
214                         if (validate) {
215                             OnapCommandUtils.validateTags(exceptionList, (Map<String, Object>) values.get(key),
216                                     OnapCommandConfig.getCommaSeparatedList(INFO_PARAMS_LIST),
217                                     OnapCommandConfig.getCommaSeparatedList(INFO_PARAMS_MANDATORY_LIST), INFO);
218
219                             HashMap<String, String> validationMap = new HashMap<>();
220                             validationMap.put(INFO_TYPE, COMMAND_TYPE_VALUES);
221
222                             for (Map.Entry<String,String> entry : validationMap.entrySet()) {
223                                 String secKey=entry.getKey();
224                                 if (infoMap.containsKey(secKey)) {
225                                     String value = infoMap.get(secKey);
226                                     if (value == null) {
227                                         exceptionList.add("Attribute '" + secKey + "' under '" + INFO + "' is empty");
228                                     } else {
229                                         if (!OnapCommandConfig.getCommaSeparatedList(validationMap.get(secKey)).contains(value)) {
230                                             exceptionList.add("Attribute '" + secKey + "' contains invalid value. Valide values are "
231                                                     + OnapCommandConfig.getCommaSeparatedList(validationMap.get(key))); //
232                                         }
233                                     }
234                                 }
235                             }
236                         }
237
238                         OnapCommandInfo info = new OnapCommandInfo();
239
240                         for (Map.Entry<String, String> entry1 : infoMap.entrySet()) {
241                             String key1 = entry1.getKey();
242
243                             switch (key1) {
244                                 case INFO_PRODUCT:
245                                     info.setProduct(infoMap.get(key1));
246                                     break;
247
248                                 case INFO_SERVICE:
249                                     info.setService(infoMap.get(key1));
250                                     break;
251
252                                 case INFO_TYPE:
253                                     Object obj = infoMap.get(key1);
254                                     info.setCommandType(OnapCommandType.get(obj.toString()));
255                                     break;
256
257                                 case INFO_STATE:
258                                     Object state = infoMap.get(key1);
259                                     info.setState(OnapCommandState.get(state.toString()));
260                                     break;
261
262                                 case INFO_AUTHOR:
263                                     Object mode = infoMap.get(key1);
264                                     info.setAuthor(mode.toString());
265                                     break;
266
267                                 case INFO_METADATA:
268                                     Object metadata = infoMap.get(key1);
269                                     info.setMetadata((Map<String,String>)metadata);
270                                     break;
271
272                                 case INFO_IGNORE:
273                                     Object ignore = infoMap.get(key1);
274                                     info.setIgnore(ignore.toString().equalsIgnoreCase(OnapCommandConstants.BOOLEAN_TRUE));
275                                     break;
276                                 default : // Do nothing
277                             }
278                         }
279
280                         cmd.setInfo(info);
281                     }
282                     break;
283
284                 case PARAMETERS:
285
286                     List<Map<String, String>> parameters = (List) values.get(key);
287
288                     if (parameters != null) {
289                         Set<String> names = new HashSet<>();
290
291                         for (Map<String, String> parameter : parameters) {
292                             OnapCommandParameter param = new OnapCommandParameter();
293
294                             if (validate) {
295                                 OnapCommandUtils.validateTags(exceptionList, parameter, OnapCommandConfig.getCommaSeparatedList(INPUT_PARAMS_LIST),
296                                         OnapCommandConfig.getCommaSeparatedList(INPUT_PARAMS_MANDATORY_LIST), PARAMETERS);
297                             }
298
299                             for (Map.Entry<String, String> entry1 : parameter.entrySet()) {
300                                 String key2 = entry1.getKey();
301
302                                 switch (key2) {
303                                     case NAME:
304                                         if (names.contains(parameter.get(key2))) {
305                                             OnapCommandUtils.throwOrCollect(new OnapCommandParameterNameConflict(parameter.get(key2)), exceptionList, validate);
306                                         } else {
307                                             names.add(parameter.get(key2));
308                                         }
309
310                                         param.setName(parameter.get(key2));
311                                         break;
312
313                                     case DESCRIPTION:
314                                         param.setDescription(parameter.get(key2));
315                                         break;
316
317                                     case SHORT_OPTION:
318                                         if (shortOptions.contains(parameter.get(key2))) {
319                                             OnapCommandUtils.throwOrCollect(new OnapCommandParameterOptionConflict(
320                                                     cmd.getSchemaName(),
321                                                     parameter.get(key2)), exceptionList, validate);
322                                         }
323                                         shortOptions.add(parameter.get(key2));
324                                         param.setShortOption(parameter.get(key2));
325                                         break;
326
327                                     case LONG_OPTION:
328                                         if (longOptions.contains(parameter.get(key2))) {
329                                             OnapCommandUtils.throwOrCollect(new OnapCommandParameterOptionConflict(
330                                                     cmd.getSchemaName(),
331                                                     parameter.get(key2)), exceptionList, validate);
332                                         }
333                                         longOptions.add(parameter.get(key2));
334                                         param.setLongOption(parameter.get(key2));
335                                         break;
336
337                                     case DEFAULT_VALUE:
338                                         Object obj = parameter.get(key2);
339                                         param.setRawDefaultValue(obj.toString());
340                                         break;
341
342                                     case TYPE:
343                                         try {
344                                             param.setParameterType(OnapCommandParameterType.get(parameter.get(key2)));
345                                         } catch (OnapCommandException ex) {
346                                             OnapCommandUtils.throwOrCollect(ex, exceptionList, validate);
347                                         }
348                                         break;
349
350                                     case IS_OPTIONAL:
351                                         if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(parameter.get(key2)))) {
352                                             exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(parameter.get(NAME),
353                                                     IS_OPTIONAL, String.valueOf(parameter.get(key2))));
354                                         }
355
356                                         param.setOptional(BOOLEAN_TRUE.equalsIgnoreCase(String.valueOf(parameter.get(key2))));
357                                         break;
358
359                                     case IS_SECURED:
360                                             if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(parameter.get(key2)))) {
361                                                 exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(parameter.get(NAME),
362                                                         IS_SECURED, String.valueOf(parameter.get(key2))));
363                                             }
364
365                                         param.setSecured(BOOLEAN_TRUE.equalsIgnoreCase(String.valueOf(parameter.get(key2))));
366                                         break;
367
368                                     case IS_INCLUDE:
369                                         if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(parameter.get(key2)))) {
370                                             exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(parameter.get(NAME),
371                                                     IS_INCLUDE, String.valueOf(parameter.get(key2))));
372                                         }
373
374                                         param.setInclude(BOOLEAN_TRUE.equalsIgnoreCase(String.valueOf(parameter.get(key2))));
375                                         break;
376
377                                     case IS_DEFAULT_PARAM:
378                                             if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(parameter.get(key2)))) {
379                                                 exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(parameter.get(NAME),
380                                                         IS_DEFAULT_PARAM, String.valueOf(parameter.get(key2))));
381                                             }
382
383                                         param.setDefaultParam(BOOLEAN_TRUE.equalsIgnoreCase(String.valueOf(parameter.get(key2))));
384                                         break;
385                                     default : // Do nothing
386                                 }
387                             }
388
389                             cmd.getParameters().add(param);
390                         }
391                     }
392                     break;
393
394                 case RESULTS:
395                     Map<String, ?> valueMap = (Map<String, ?>) values.get(key);
396                     if (valueMap != null) {
397                         for (Map.Entry<String, ?> entry1 : valueMap.entrySet()) {
398                             String key3 = entry1.getKey();
399
400                             switch (key3) {
401                                 case DIRECTION:
402                                     try {
403                                         cmd.getResult().setPrintDirection(OnapCommandPrintDirection.get((String) valueMap.get(key3)));
404                                     } catch (OnapCommandException ex) {
405                                         OnapCommandUtils.throwOrCollect(ex, exceptionList, validate);
406                                     }
407                                     break;
408
409                                 case ATTRIBUTES:
410                                     List<Map<String, String>> attrs = (ArrayList) valueMap.get(key3);
411
412                                     for (Map<String, String> map : attrs) {
413                                         OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
414                                         if (validate) {
415                                             OnapCommandUtils.validateTags(exceptionList, map, OnapCommandConfig.getCommaSeparatedList(RESULT_PARAMS_LIST),
416                                                     OnapCommandConfig.getCommaSeparatedList(RESULT_PARAMS_MANDATORY_LIST), ATTRIBUTES);
417                                         }
418
419                                         Set<String> resultParamNames = new HashSet<>();
420
421                                         for (Map.Entry<String, String> entry4 : map.entrySet()) {
422                                             String key4 = entry4.getKey();
423
424                                             switch (key4) {
425                                                 case NAME:
426                                                     if (resultParamNames.contains(map.get(key4))) {
427                                                         exceptionList.add("Attribute name='" + map.get(key4) + "' under '"
428                                                                 + ATTRIBUTES + ":' is already used, Take different one.");
429
430                                                     } else {
431                                                         attr.setName(map.get(key4));
432                                                         resultParamNames.add(map.get(key4));
433                                                     }
434                                                     break;
435
436                                                 case DESCRIPTION:
437                                                     attr.setDescription(map.get(key4));
438                                                     break;
439
440                                                 case SCOPE:
441                                                     try {
442                                                         attr.setScope(OnapCommandResultAttributeScope.get(map.get(key4)));
443                                                     } catch (OnapCommandException ex) {
444                                                         OnapCommandUtils.throwOrCollect(ex, exceptionList, validate);
445                                                     }
446                                                     break;
447
448                                                 case TYPE:
449                                                     try {
450                                                         attr.setType(OnapCommandParameterType.get(map.get(key4)));
451                                                     } catch (OnapCommandException ex) {
452                                                         OnapCommandUtils.throwOrCollect(ex, exceptionList, validate);
453                                                     }
454                                                     break;
455
456                                                 case DEFAULT_VALUE:
457                                                     Object obj = map.get(key4);
458                                                     attr.setDefaultValue(obj.toString());
459                                                     break;
460
461                                                 case IS_SECURED:
462                                                         if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(map.get(key4)))) {
463                                                             exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(ATTRIBUTES,
464                                                                     IS_SECURED, String.valueOf(map.get(key4))));
465                                                         }
466                                                     attr.setSecured(BOOLEAN_TRUE.equals(String.valueOf(map.get(key4))));
467                                                     break;
468
469                                                 case IS_DEFAULT_ATTR:
470                                                         if (validate && !OnapCommandUtils.validateBoolean(String.valueOf(map.get(key4)))) {
471                                                             exceptionList.add(OnapCommandUtils.invalidBooleanValueMessage(ATTRIBUTES,
472                                                                     IS_DEFAULT_ATTR, String.valueOf(map.get(key4))));
473                                                         }
474                                                     attr.setDefaultAttr(BOOLEAN_TRUE.equals(String.valueOf(map.get(key4))));
475                                                     break;
476                                                 default : // Do nothing
477                                             }
478
479                                         }
480                                         cmd.getResult().getRecords().add(attr);
481                                     }
482                                     break;
483                                 default : // Do nothing
484                             }
485                         }
486                     }
487                     break;
488                 default : // Do nothing
489             }
490         }
491
492         return exceptionList;
493     }
494
495     public static InputStream loadSchemaFromFile(String schemaLocation) throws OnapCommandInvalidSchema {
496         File schemaFile = new File(schemaLocation);
497         try {
498             FileInputStream inputFileStream = new FileInputStream(schemaFile);  // NOSONAR
499             if (!schemaFile.isFile()) {
500                 throw new OnapCommandInvalidSchema(schemaFile.getName(), SCHEMA_FILE_NOT_EXIST);
501             }
502
503             if (!schemaFile.getName().endsWith(".yaml")) {
504                 throw new OnapCommandInvalidSchema(schemaFile.getName(), SCHEMA_FILE_WRONG_EXTN);
505             }
506             return inputFileStream;
507         }catch (FileNotFoundException e) {
508             throw new OnapCommandInvalidSchema(schemaFile.getName(), e);
509         }
510     }
511
512     /**
513      * Get schema map.
514      *
515      * @param stream
516      * @param schemaName
517      * @return map
518      * @throws OnapCommandInvalidSchema
519      *             exception
520      */
521     public static Map<String, Object> loadSchema(InputStream stream, String schemaName) throws OnapCommandInvalidSchema  { //NOSONAR
522         return OnapCommandDiscoveryUtils.loadYaml(stream);
523
524     }
525 }