9a425397e83c45e0fe6ca0d669502f4ff73b18a5
[cli.git] / framework / src / main / java / org / onap / cli / fw / utils / OnapCommandUtils.java
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.cli.fw.utils;
18
19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.jayway.jsonpath.JsonPath;
21 import net.minidev.json.JSONArray;
22 import org.onap.cli.fw.OnapCommand;
23 import org.onap.cli.fw.ad.OnapCredentials;
24 import org.onap.cli.fw.ad.OnapService;
25 import org.onap.cli.fw.cmd.OnapHttpCommand;
26 import org.onap.cli.fw.cmd.OnapSwaggerCommand;
27 import org.onap.cli.fw.conf.Constants;
28 import org.onap.cli.fw.conf.OnapCommandConfg;
29 import org.onap.cli.fw.error.OnapCommandDiscoveryFailed;
30 import org.onap.cli.fw.error.OnapCommandException;
31 import org.onap.cli.fw.error.OnapCommandHelpFailed;
32 import org.onap.cli.fw.error.OnapCommandHttpHeaderNotFound;
33 import org.onap.cli.fw.error.OnapCommandHttpInvalidResponseBody;
34 import org.onap.cli.fw.error.OnapCommandInvalidDefaultParameter;
35 import org.onap.cli.fw.error.OnapCommandInvalidParameterType;
36 import org.onap.cli.fw.error.OnapCommandInvalidParameterValue;
37 import org.onap.cli.fw.error.OnapCommandInvalidPrintDirection;
38 import org.onap.cli.fw.error.OnapCommandInvalidResultAttributeScope;
39 import org.onap.cli.fw.error.OnapCommandInvalidSchema;
40 import org.onap.cli.fw.error.OnapCommandInvalidSchemaVersion;
41 import org.onap.cli.fw.error.OnapCommandParameterNameConflict;
42 import org.onap.cli.fw.error.OnapCommandParameterNotFound;
43 import org.onap.cli.fw.error.OnapCommandParameterOptionConflict;
44 import org.onap.cli.fw.error.OnapCommandResultEmpty;
45 import org.onap.cli.fw.error.OnapCommandResultMapProcessingFailed;
46 import org.onap.cli.fw.error.OnapCommandSchemaNotFound;
47 import org.onap.cli.fw.http.HttpInput;
48 import org.onap.cli.fw.http.HttpResult;
49 import org.onap.cli.fw.input.OnapCommandParameter;
50 import org.onap.cli.fw.input.ParameterType;
51 import org.onap.cli.fw.output.OnapCommandResult;
52 import org.onap.cli.fw.output.OnapCommandResultAttribute;
53 import org.onap.cli.fw.output.OnapCommandResultAttributeScope;
54 import org.onap.cli.fw.output.PrintDirection;
55 import org.onap.cli.fw.output.ResultType;
56 import org.onap.cli.fw.run.OnapCommandExecutor;
57 import org.springframework.core.io.Resource;
58 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
59 import org.springframework.core.io.support.ResourcePatternResolver;
60 import org.yaml.snakeyaml.Yaml;
61
62 import java.io.File;
63 import java.io.IOException;
64 import java.io.InputStream;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.Collections;
68 import java.util.HashMap;
69 import java.util.HashSet;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Map.Entry;
73 import java.util.ServiceLoader;
74 import java.util.Set;
75 import java.util.stream.Collectors;
76 import java.util.stream.Stream;
77
78 /**
79  * Provides helper method to parse Yaml files and produce required objects.
80  *
81  */
82 public class OnapCommandUtils {
83
84     /**
85      * Private constructor.
86      */
87     private OnapCommandUtils() {
88
89     }
90
91     /**
92      * Validates schema version.
93      *
94      * @param schemaName schema name
95      * @param version    schema version
96      * @return map
97      * @throws OnapCommandInvalidSchemaVersion invalid schema version exception
98      * @throws OnapCommandInvalidSchema        invalid schema
99      * @throws OnapCommandSchemaNotFound       schema not found
100      */
101     public static Map<String, ?> validateSchemaVersion(String schemaName, String version) throws OnapCommandException {
102         InputStream inputStream = OnapCommandUtils.class.getClassLoader().getResourceAsStream(schemaName);
103
104         try {
105             Resource resource = getExternalResource(schemaName, Constants.EXTERNAL_SCHEMA_PATH_PATERN);
106
107             if (resource != null) {
108                 inputStream = resource.getInputStream();
109             }
110
111         } catch (IOException e) {
112             throw new OnapCommandSchemaNotFound(schemaName, e);
113         }
114         if (inputStream == null) {
115             throw new OnapCommandSchemaNotFound(schemaName);
116         }
117
118         Map<String, ?> values = null;
119         try {
120             values = (Map<String, ?>) new Yaml().load(inputStream);
121         } catch (Exception e) {
122             throw new OnapCommandInvalidSchema(schemaName, e);
123         }
124         String schemaVersion = "";
125         if (values.keySet().contains(Constants.ONAP_CMD_SCHEMA_VERSION)) {
126             Object obj = values.get(Constants.ONAP_CMD_SCHEMA_VERSION);
127             schemaVersion = obj.toString();
128         }
129
130         if (!version.equals(schemaVersion)) {
131             throw new OnapCommandInvalidSchemaVersion(schemaVersion);
132         }
133
134         return values;
135     }
136
137     /**
138      * Retrieve OnapCommand from schema.
139      *
140      * @param cmd            OnapCommand
141      * @param schemaName     schema name
142      * @param includeDefault include if default
143      * @throws OnapCommandParameterNameConflict       param name conflict exception
144      * @throws OnapCommandParameterOptionConflict     param option conflict exception
145      * @throws OnapCommandInvalidParameterType        invalid param type exception
146      * @throws OnapCommandInvalidPrintDirection       invalid print direction exception
147      * @throws OnapCommandInvalidResultAttributeScope invalid scope exception
148      * @throws OnapCommandSchemaNotFound              schema not found
149      * @throws OnapCommandInvalidSchema               invalid schema
150      * @throws OnapCommandInvalidSchemaVersion        invalid schema version
151      */
152     public static void loadSchema(OnapCommand cmd, String schemaName, boolean includeDefault)
153             throws OnapCommandException {
154         try {
155             Map<String, ?> defaultParameterMap = includeDefault ?
156                     validateSchemaVersion(Constants.DEFAULT_PARAMETER_FILE_NAME, cmd.getSchemaVersion()) : new HashMap<>();
157             Map<String, List<Map<String, String>>> commandYamlMap = (Map<String, List<Map<String, String>>>)validateSchemaVersion(schemaName, cmd.getSchemaVersion());
158
159             List<String> defParams = new ArrayList<>();
160
161             if (includeDefault) {
162                 if (commandYamlMap.get(Constants.PARAMETERS) == null) {
163                     commandYamlMap.put(Constants.PARAMETERS, (List<Map<String, String>>) defaultParameterMap.get(Constants.PARAMETERS));
164                 } else {
165                     commandYamlMap.get(Constants.PARAMETERS).addAll((List<Map<String, String>>) defaultParameterMap.get(Constants.PARAMETERS));
166                 }
167                 defParams = ((List<Map<String, String>>) defaultParameterMap.get(Constants.PARAMETERS)).stream()
168                         .map(p -> p.get(Constants.NAME)).collect(Collectors.toList());
169             }
170
171             parseSchema(cmd, commandYamlMap, defParams);
172         } catch (OnapCommandException e) {
173             throw e;
174         } catch (Exception e) {
175             throw new OnapCommandInvalidSchema(schemaName, e);
176         }
177     }
178
179     private static void processNoAuth(Set<String> parameterSet, final OnapCommand cmd, final List<String> includeParams,
180                                       final List<String> excludeParams) throws OnapCommandInvalidDefaultParameter {
181         // processing for no-auth type
182         if (cmd.getService() != null) {
183             List<String> includeAuthParams = new ArrayList();
184             List<String> excludeAuthParams = new ArrayList<>();
185             boolean noAuth = cmd.getService().isNoAuth();
186
187             if (cmd.isCommandInternal()) {
188                 excludeAuthParams.addAll(OnapCommandConfg.getExcludeParamsForInternalCmd());
189             } else {
190                 if (noAuth) {
191                     includeAuthParams.addAll(OnapCommandConfg.getIncludeParamsForNoAuthEnableExternalCmd());
192                     excludeAuthParams.addAll(OnapCommandConfg.getExcludeParamsForNoAuthEnableExternalCmd());
193                 } else {
194                     includeAuthParams.addAll(OnapCommandConfg.getIncludeParamsForNoAuthDisableExternalCmd());
195                 }
196             }
197
198             List<String> invalidExclude = excludeAuthParams.stream().filter(includeParams::contains)
199                     .collect(Collectors.toList());
200
201             List<String> invalidInclude = includeAuthParams.stream().filter(excludeParams::contains)
202                     .filter(p->!includeParams.contains(p)).collect(Collectors.toList());
203
204             if (!invalidExclude.isEmpty() || !invalidInclude.isEmpty()) {
205                 throw new OnapCommandInvalidDefaultParameter(Stream.concat(invalidExclude.stream(), invalidInclude.stream())
206                         .collect(Collectors.toList()));
207             }
208
209
210             parameterSet.addAll(includeAuthParams);
211             parameterSet.removeAll(excludeAuthParams);
212         }
213     }
214
215     private static void parseSchema(OnapCommand cmd,
216                                     final Map<String, ?> values,
217                                     final List<String> defaultParamNames) throws OnapCommandException {
218
219         List<String> shortOptions = new ArrayList<>();
220         List<String> longOptions = new ArrayList<>();
221         List<String> names = new ArrayList<>();
222         Set<String> filteredDefaultParams = new HashSet<>();
223
224         List<String> sections = Arrays.asList(Constants.NAME, Constants.DESCRIPTION, Constants.SERVICE,
225                 Constants.DEFAULT_PARAMETERS, Constants.PARAMETERS, Constants.RESULTS);
226
227         for (String key : sections) {
228
229             if (Constants.NAME.equals(key)) {
230                 Object val = values.get(key);
231                 if (val != null) {
232                     cmd.setName(val.toString());
233                 }
234             } else if (Constants.DESCRIPTION.equals(key)) {
235                 Object val = values.get(key);
236                 if (val != null) {
237                     cmd.setDescription(val.toString());
238                 }
239             } else if (Constants.SERVICE.equals(key)) {
240                 Map<String, String> map = (Map<String, String>) values.get(key);
241                 if (map != null) {
242                     OnapService srv = new OnapService();
243
244                     for (Map.Entry<String, String> entry1 : map.entrySet()) {
245                         String key1 = entry1.getKey();
246
247                         if (Constants.NAME.equals(key1)) {
248                             srv.setName(map.get(key1));
249                         } else if (Constants.VERSION.equals(key1)) {
250                             srv.setVersion(map.get(key1));
251                         } else if (Constants.NO_AUTH.equals(key1)) {
252                             Object obj = map.get(key1);
253                             srv.setNoAuth("true".equalsIgnoreCase(obj.toString()));
254                         } else if (Constants.MODE.equals(key1)) {
255                             Object obj = map.get(key1);
256                             //TODO mrkanag Validate and raise exception for invalid case
257                             srv.setMode(obj.toString());
258                         }
259                     }
260
261                     cmd.setService(srv);
262                 }
263             } else if (Constants.DEFAULT_PARAMETERS.equals(key)) {
264
265                 Map<String, List<String>> defParameters = (Map) values.get(Constants.DEFAULT_PARAMETERS);
266                 List<String> includeParams = new ArrayList<>();
267                 List<String> excludeParams = new ArrayList<>();
268
269                 if (values.containsKey(Constants.DEFAULT_PARAMETERS) && defParameters == null) {
270                     // if default parameter section is available then it must have either include
271                     // or exclude sub-section.
272                     throw new OnapCommandInvalidSchema(Constants.SCHEMA_INVALID_DEFAULT_PARAMS_SECTION);
273                 }
274
275
276                 if (defParameters != null) {
277                     // validate default parameters
278                     if (defParameters.containsKey(Constants.DEFAULT_PARAMETERS_INCLUDE)) {
279                         includeParams = defParameters.get(Constants.DEFAULT_PARAMETERS_INCLUDE);
280                     }
281
282                     List<String> invInclude = includeParams.stream()
283                             .filter(p -> !defaultParamNames.contains(p))
284                             .collect(Collectors.toList());
285
286                     if (defParameters.containsKey(Constants.DEFAULT_PARAMETERS_EXCLUDE)) {
287                         excludeParams = defParameters.get(Constants.DEFAULT_PARAMETERS_EXCLUDE);
288                     }
289
290                     List<String> invExclude = excludeParams.stream().filter(p -> !defaultParamNames.contains(p))
291                             .collect(Collectors.toList());
292
293
294                     if (!invExclude.isEmpty() || !invInclude.isEmpty()) {
295                         throw new OnapCommandInvalidDefaultParameter(Stream.concat(invInclude.stream(), invExclude.stream())
296                                 .collect(Collectors.toList()));
297                     }
298
299                     if (!includeParams.isEmpty()) {
300                         filteredDefaultParams.addAll(includeParams);
301                     } else if (!excludeParams.isEmpty()) {
302                         List<String> finalExcludeParams = excludeParams;
303                         defaultParamNames.stream().filter(p -> !finalExcludeParams.contains(p))
304                                 .forEach(filteredDefaultParams::add);
305                     }
306                 } else {
307                     filteredDefaultParams.addAll(defaultParamNames);
308                 }
309                 processNoAuth(filteredDefaultParams, cmd, includeParams, excludeParams);
310             } else if (Constants.PARAMETERS.equals(key)) {
311
312                 List<Map<String, String>> parameters = (List) values.get(key);
313
314                 if (parameters != null) {
315                     for (Map<String, String> map : parameters) {
316                         OnapCommandParameter param = new OnapCommandParameter();
317
318                         for (Map.Entry<String, String> entry1 : map.entrySet()) {
319                             String key2 = entry1.getKey();
320
321                             if (Constants.NAME.equals(key2)) {
322                                 if (names.contains(map.get(key2))) {
323                                     throw new OnapCommandParameterNameConflict(map.get(key2));
324                                 }
325                                 names.add(map.get(key2));
326                                 param.setName(map.get(key2));
327                             } else if (Constants.DESCRIPTION.equals(key2)) {
328                                 param.setDescription(map.get(key2));
329                             } else if (Constants.SHORT_OPTION.equals(key2)) {
330                                 if (shortOptions.contains(map.get(key2))) {
331                                     throw new OnapCommandParameterOptionConflict(map.get(key2));
332                                 }
333                                 shortOptions.add(map.get(key2));
334                                 param.setShortOption(map.get(key2));
335                             } else if (Constants.LONG_OPTION.equals(key2)) {
336                                 if (longOptions.contains(map.get(key2))) {
337                                     throw new OnapCommandParameterOptionConflict(map.get(key2));
338                                 }
339                                 longOptions.add(map.get(key2));
340                                 param.setLongOption(map.get(key2));
341                             } else if (Constants.DEFAULT_VALUE.equals(key2)) {
342                                 Object obj = map.get(key2);
343                                 param.setDefaultValue(obj.toString());
344                             } else if (Constants.TYPE.equals(key2)) {
345                                 param.setParameterType(ParameterType.get(map.get(key2)));
346                             } else if (Constants.IS_OPTIONAL.equals(key2)) {
347                                 if ("true".equalsIgnoreCase(String.valueOf(map.get(key2)))) {
348                                     param.setOptional(true);
349                                 } else {
350                                     param.setOptional(false);
351                                 }
352                             } else if (Constants.IS_SECURED.equals(key2)) {
353                                 if ("true".equalsIgnoreCase(String.valueOf(map.get(key2)))) {
354                                     param.setSecured(true);
355                                 } else {
356                                     param.setSecured(false);
357                                 }
358                             }
359                         }
360
361                         // Add the element to command :
362                         // 1. if parameter is available in filtered parameter list.
363                         // 2. otherwise, parameter p is available in command yaml file.
364                         if (filteredDefaultParams.contains(param.getName()) || !defaultParamNames.contains(param.getName())) {
365                             cmd.getParameters().add(param);
366                         }
367                     }
368                 }
369             } else if (Constants.RESULTS.equals(key)) {
370                 Map<String, ?> valueMap = (Map<String, ?>) values.get(key);
371                 if (valueMap != null) {
372                     OnapCommandResult result = new OnapCommandResult();
373                     for (Map.Entry<String, ?> entry1 : valueMap.entrySet()) {
374                         String key3 = entry1.getKey();
375
376                         if (Constants.DIRECTION.equals(key3)) {
377                             result.setPrintDirection(PrintDirection.get((String) valueMap.get(key3)));
378                         } else if (Constants.ATTRIBUTES.equals(key3)) {
379                             List<Map<String, String>> attrs = (ArrayList) valueMap.get(key3);
380
381                             for (Map<String, String> map : attrs) {
382                                 OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
383                                 for (Map.Entry<String, String> entry4 : map.entrySet()) {
384                                     String key4 = entry4.getKey();
385
386                                     if (Constants.NAME.equals(key4)) {
387                                         attr.setName(map.get(key4));
388                                     } else if (Constants.DESCRIPTION.equals(key4)) {
389                                         attr.setDescription(map.get(key4));
390                                     } else if (Constants.SCOPE.equals(key4)) {
391                                         attr.setScope(OnapCommandResultAttributeScope.get(map.get(key4)));
392                                     } else if (Constants.TYPE.equals(key4)) {
393                                         attr.setType(ParameterType.get(map.get(key4)));
394                                     } else if (Constants.IS_SECURED.equals(key4)) {
395                                         if ("true".equals(String.valueOf(map.get(key4)))) {
396                                             attr.setSecured(true);
397                                         } else {
398                                             attr.setSecured(false);
399                                         }
400                                     }
401
402                                 }
403                                 result.getRecords().add(attr);
404                             }
405                         }
406                     }
407                     cmd.setResult(result);
408                 }
409             }
410         }
411     }
412
413     /**
414      * Load the schema.
415      *
416      * @param cmd
417      *            OnapSwaggerBasedCommand
418      * @param schemaName
419      *            schema name
420      * @throws OnapCommandParameterNameConflict
421      *             param name conflict exception
422      * @throws OnapCommandParameterOptionConflict
423      *             param option conflict exception
424      * @throws OnapCommandInvalidParameterType
425      *             invalid param type exception
426      * @throws OnapCommandInvalidPrintDirection
427      *             invalid print direction exception
428      * @throws OnapCommandInvalidResultAttributeScope
429      *             invalid scope exception
430      * @throws OnapCommandSchemaNotFound
431      *             schema not found
432      * @throws OnapCommandInvalidSchema
433      *             invalid schema
434      * @throws OnapCommandInvalidSchemaVersion
435      *             invalid schema version
436      */
437     public static void loadSchema(OnapSwaggerCommand cmd, String schemaName) throws OnapCommandException {
438         try {
439             Map<String, ?> values = (Map<String, ?>) validateSchemaVersion(schemaName, cmd.getSchemaVersion());
440             Map<String, String> valueMap = (Map<String, String>) values.get(Constants.EXECUTOR);
441             OnapCommandExecutor exec = new OnapCommandExecutor();
442
443             for (Map.Entry<String, String> entry1 : valueMap.entrySet()) {
444                 String key1 = entry1.getKey();
445
446                 if (Constants.API.equals(key1)) {
447                     exec.setApi(valueMap.get(key1));
448                 } else if (Constants.CLIENT.equals(key1)) {
449                     exec.setClient(valueMap.get(key1));
450                 } else if (Constants.ENTITY.equals(key1)) {
451                     exec.setEntity(valueMap.get(key1));
452                 } else if (Constants.EXCEPTION.equals(key1)) {
453                     exec.setException(valueMap.get(key1));
454                 } else if (Constants.METHOD.equals(key1)) {
455                     exec.setMethod(valueMap.get(key1));
456                 }
457             }
458
459             cmd.setExecutor(exec);
460         } catch (OnapCommandException e) {
461             throw e;
462         } catch (Exception e) {
463             throw new OnapCommandInvalidSchema(schemaName, e);
464         }
465     }
466
467     /**
468      * Load the schema.
469      *
470      * @param cmd
471      *            OnapHttpCommand
472      * @param schemaName
473      *            schema name
474      * @throws OnapCommandParameterNameConflict
475      *             param name conflict exception
476      * @throws OnapCommandParameterOptionConflict
477      *             param option conflict exception
478      * @throws OnapCommandInvalidParameterType
479      *             invalid param type exception
480      * @throws OnapCommandInvalidPrintDirection
481      *             invalid print direction exception
482      * @throws OnapCommandInvalidResultAttributeScope
483      *             invalid scope exception
484      * @throws OnapCommandSchemaNotFound
485      *             schema not found
486      * @throws OnapCommandInvalidSchema
487      *             invalid schema
488      * @throws OnapCommandInvalidSchemaVersion
489      *             invalid schema version
490      */
491     public static void loadSchema(OnapHttpCommand cmd, String schemaName) throws OnapCommandException {
492         try {
493             Map<String, ?> values = (Map<String, ?>) validateSchemaVersion(schemaName, cmd.getSchemaVersion());
494             Map<String, ?> valMap = (Map<String, ?>) values.get(Constants.HTTP);
495
496             for (Map.Entry<String, ?> entry1 : valMap.entrySet()) {
497                 String key1 = entry1.getKey();
498                 if (Constants.REQUEST.equals(key1)) {
499                     Map<String, ?> map = (Map<String, ?>) valMap.get(key1);
500
501                     for (Map.Entry<String, ?> entry2 : map.entrySet()) {
502                         String key2 = entry2.getKey();
503
504                         if (Constants.URI.equals(key2)) {
505                             Object obj = map.get(key2);
506                             cmd.getInput().setUri(obj.toString());
507                         } else if (Constants.MERHOD.equals(key2)) {
508                             Object obj = map.get(key2);
509                             cmd.getInput().setMethod(obj.toString());
510                         } else if (Constants.BODY.equals(key2)) {
511                             Object obj = map.get(key2);
512                             cmd.getInput().setBody(obj.toString());
513                         } else if (Constants.HEADERS.equals(key2)) {
514                             Map<String, String> head = (Map<String, String>) map.get(key2);
515                             cmd.getInput().setReqHeaders(head);
516                         } else if (Constants.QUERIES.equals(key2)) {
517                             Map<String, String> query = (Map<String, String>) map.get(key2);
518
519                             cmd.getInput().setReqQueries(query);
520                         }
521                     }
522                 } else if (Constants.SUCCESS_CODES.equals(key1)) {
523                     cmd.setSuccessStatusCodes((ArrayList) valMap.get(key1));
524                 } else if (Constants.RESULT_MAP.equals(key1)) {
525                     cmd.setResultMap((Map<String, String>) valMap.get(key1));
526                 } else if (Constants.SAMPLE_RESPONSE.equals(key1)) {
527                     // (mrkanag) implement sample response handling
528                 }
529             }
530
531         } catch (OnapCommandException e) {
532             throw e;
533         } catch (Exception e) {
534             throw new OnapCommandInvalidSchema(schemaName, e);
535         }
536     }
537
538     /**
539      * Returns Help.
540      *
541      * @param cmd
542      *            OnapCommand
543      * @return help string
544      * @throws OnapCommandHelpFailed
545      *             help failed exception
546      */
547     public static String help(OnapCommand cmd) throws OnapCommandHelpFailed {
548         String help = "usage: onap " + cmd.getName();
549
550         // Add description
551         help += "\n\n" + cmd.getDescription();
552
553         // Add service
554         help += "\n\nOnap service: " + cmd.getService();
555
556         // Add whole command
557         String commandOptions = "";
558
559         // Add parameters
560         OnapCommandResult paramTable = new OnapCommandResult();
561         paramTable.setPrintDirection(PrintDirection.LANDSCAPE);
562         paramTable.setType(ResultType.TABLE);
563         paramTable.setIncludeTitle(false);
564         paramTable.setIncludeSeparator(false);
565
566         OnapCommandResultAttribute attrName = new OnapCommandResultAttribute();
567         attrName.setName(Constants.NAME);
568         attrName.setDescription(Constants.NAME);
569         attrName.setScope(OnapCommandResultAttributeScope.SHORT);
570         paramTable.getRecords().add(attrName);
571
572         OnapCommandResultAttribute attrDescription = new OnapCommandResultAttribute();
573         attrDescription.setName(Constants.DESCRIPTION);
574         attrDescription.setDescription(Constants.DESCRIPTION);
575         attrDescription.setScope(OnapCommandResultAttributeScope.SHORT);
576         paramTable.getRecords().add(attrDescription);
577
578         int newLineOptions = 0;
579         for (OnapCommandParameter param : cmd.getParameters()) {
580             // First column Option or positional args
581             String optFirstCol;
582             if (newLineOptions == 3) {
583                 newLineOptions = 0;
584                 commandOptions += "\n";
585             }
586
587             if (param.getShortOption() != null || param.getLongOption() != null) {
588                 optFirstCol = OnapCommandParameter.printShortOption(param.getShortOption()) + " | "
589                         + OnapCommandParameter.printLongOption(param.getLongOption());
590                 commandOptions += "[" + optFirstCol + "] ";
591             } else {
592                 optFirstCol = param.getName();
593                 commandOptions += "<" + optFirstCol + "> ";
594             }
595
596             newLineOptions++;
597
598             attrName.getValues().add(optFirstCol);
599
600             // Second column description
601             String optSecondCol = param.getDescription().trim();
602             if (!optSecondCol.endsWith(".")) {
603                 optSecondCol += ".";
604             }
605             optSecondCol += " It is of type " + param.getParameterType().name() + ".";
606
607             if (param.getParameterType().equals(ParameterType.JSON)
608                     || param.getParameterType().equals(ParameterType.YAML)) {
609                 optSecondCol += " It's recommended to input the complete path of the file, which is having the value for it.";
610             }
611             if (param.isOptional()) {
612                 optSecondCol += " It is optional.";
613             }
614
615             String defaultMsg = " By default, it is ";
616             if (param.isDefaultValueAnEnv()) {
617                 optSecondCol += defaultMsg + "read from environment variable " + param.getEnvVarNameFromDefaultValue()
618                         + ".";
619             } else if (param.getDefaultValue() != null && !((String)param.getDefaultValue()).isEmpty()) {
620                 optSecondCol += defaultMsg + param.getDefaultValue() + ".";
621             }
622
623             if (param.isSecured()) {
624                 optSecondCol += " Secured.";
625             }
626             // (mrkanag) Add help msg for reading default value from env
627             attrDescription.getValues().add(optSecondCol);
628         }
629
630         try {
631             help += "\n\nOptions:\n" + commandOptions + "\nwhere,\n" + paramTable.print();
632         } catch (OnapCommandException e) {
633             throw new OnapCommandHelpFailed(e);
634         }
635
636         // Add results
637         OnapCommandResult resultTable = new OnapCommandResult();
638         resultTable.setPrintDirection(PrintDirection.PORTRAIT);
639         resultTable.setType(ResultType.TABLE);
640         resultTable.setIncludeTitle(false);
641         resultTable.setIncludeSeparator(false);
642
643         for (OnapCommandResultAttribute attr : cmd.getResult().getRecords()) {
644             OnapCommandResultAttribute attrHelp = new OnapCommandResultAttribute();
645             attrHelp.setName(attr.getName());
646             attrHelp.setDescription(attr.getDescription());
647             String msg = attr.getDescription() + " and is of type " + attr.getType().name() + ".";
648             if (attr.isSecured()) {
649                 msg += " It is secured.";
650             }
651             attrHelp.getValues().add(msg);
652             attrHelp.setType(attr.getType());
653             resultTable.getRecords().add(attrHelp);
654         }
655         try {
656             help += "\n\nResults:\n" + resultTable.print();
657         } catch (OnapCommandException e) {
658             throw new OnapCommandHelpFailed(e);
659         }
660
661         // Error
662         help += "\n\nError:\nOn error, it prints <HTTP STATUS CODE>::<ERROR CODE>::<ERROR MESSAGE>\n";
663         return help;
664     }
665
666     /**
667      * Helps to create OnapCredentials from default params.
668      *
669      * @param params
670      *            list of parameters
671      * @return OnapCredentials
672      * @throws OnapCommandInvalidParameterValue
673      *             exception
674      */
675     public static OnapCredentials fromParameters(List<OnapCommandParameter> params)
676             throws OnapCommandInvalidParameterValue {
677         Map<String, String> paramMap = new HashMap<>();
678
679         for (OnapCommandParameter param : params) {
680             paramMap.put(param.getName(), param.getValue().toString());
681         }
682
683         return new OnapCredentials(paramMap.get(Constants.DEAFULT_PARAMETER_USERNAME),
684                 paramMap.get(Constants.DEAFULT_PARAMETER_PASS_WORD),
685                 paramMap.get(Constants.DEAFULT_PARAMETER_HOST_URL));
686     }
687
688     /**
689      * Create Dict from list of Parameters.
690      *
691      * @param inputs
692      *            list of parameters
693      * @return map
694      */
695     public static Map<String, OnapCommandParameter> getInputMap(List<OnapCommandParameter> inputs) {
696         Map<String, OnapCommandParameter> map = new HashMap<>();
697         for (OnapCommandParameter param : inputs) {
698             map.put(param.getName(), param);
699         }
700         return map;
701     }
702
703     /**
704      * Discover the Onap commands.
705      *
706      * @return list
707      */
708     public static List<Class<OnapCommand>> findOnapCommands() {
709         ServiceLoader<OnapCommand> loader = ServiceLoader.load(OnapCommand.class);
710         List<Class<OnapCommand>> clss = new ArrayList<>();
711         for (OnapCommand implClass : loader) {
712             clss.add((Class<OnapCommand>) implClass.getClass());
713         }
714
715         return clss;
716     }
717
718     /**
719      * sort the set.
720      *
721      * @param col
722      *            set
723      * @return list
724      */
725     public static List<String> sort(Set<String> col) {
726         List<String> results = new ArrayList<>();
727         results.addAll(col);
728         Collections.sort(results);
729         return results;
730     }
731
732     /**
733      * Flatten the json list.
734      *
735      * @param jsons
736      *            list json strings
737      * @return list
738      */
739     public static List<String> jsonFlatten(List<String> jsons) {
740         List<String> results = new ArrayList<>();
741         for (String json : jsons) {
742             try {
743                 results.add(JsonPath.parse(json).jsonString());
744             } catch (Exception e) { // NOSONAR
745                 results.add(json);
746             }
747         }
748
749         return results;
750     }
751
752     /**
753      * Construct method name.
754      *
755      * @param name
756      *            name
757      * @param prefix
758      *            prefix
759      * @return string
760      */
761     public static String formMethodNameFromAttributeName(String name, String prefix) {
762         if (name == null || name.isEmpty()) {
763             return name;
764         }
765
766         String methodName = prefix;
767         for (String tk : name.split("-")) {
768             methodName += Character.toString(tk.charAt(0)).toUpperCase();
769             methodName += tk.substring(1);
770         }
771         return methodName;
772     }
773
774     private static String replaceLineFromInputParameters(String line, Map<String, OnapCommandParameter> params)
775             throws OnapCommandException {
776         String result = "";
777
778         if (!line.contains("${")) {
779             return line;
780         }
781
782         int currentIdx = 0;
783         while (currentIdx < line.length()) {
784             int idxS = line.indexOf("${", currentIdx);
785             if (idxS == -1) {
786                 result += line.substring(currentIdx);
787                 break;
788             }
789             int idxE = line.indexOf("}", idxS);
790             String paramName = line.substring(idxS + 2, idxE);
791             paramName = paramName.trim();
792             if (!params.containsKey(paramName)) {
793                 throw new OnapCommandParameterNotFound(paramName);
794             }
795
796             String value = params.get(paramName).getValue().toString();
797
798             OnapCommandParameter param = params.get(paramName);
799             if (ParameterType.ARRAY.equals(param.getParameterType())
800                     || ParameterType.MAP.equals(param.getParameterType())
801                     || ParameterType.JSON.equals(param.getParameterType())
802                     || ParameterType.YAML.equals(param.getParameterType())) {
803                 // ignore the front and back double quotes in json body
804                 result += line.substring(currentIdx, idxS - 1) + value;
805                 currentIdx = idxE + 2;
806             } else {
807                 result += line.substring(currentIdx, idxS) + value;
808                 currentIdx = idxE + 1;
809             }
810         }
811
812         return result;
813     }
814
815     private static ArrayList<String> replaceLineFromOutputResults(String line, HttpResult resultHttp)
816             throws OnapCommandHttpHeaderNotFound, OnapCommandHttpInvalidResponseBody,
817             OnapCommandResultMapProcessingFailed, OnapCommandResultEmpty {
818         String headerProcessedLine = "";
819
820         ArrayList<String> result = new ArrayList<>();
821         if (!line.contains("$b{") && !line.contains("$h{")) {
822             result.add(line);
823             return result;
824         }
825
826         /**
827          * In case of empty response body [] or {}
828          **/
829         if (resultHttp.getBody().length() <= 2) {
830             return result;
831         }
832
833         /**
834          * Process headers macros : line: $h{abc}-$b{$.[*].xyz} , After processing line will be [abc's
835          * value]-$b{$.[*].xyz}
836          **/
837         int currentIdx = 0;
838         while (currentIdx < line.length()) {
839             int idxS = line.indexOf("$h{", currentIdx);
840             if (idxS == -1) {
841                 headerProcessedLine += line.substring(currentIdx);
842                 break;
843             }
844             int idxE = line.indexOf("}", idxS);
845             String headerName = line.substring(idxS + 3, idxE);
846             headerName = headerName.trim();
847             if (!resultHttp.getRespHeaders().containsKey(headerName)) {
848                 throw new OnapCommandHttpHeaderNotFound(headerName);
849             }
850             String value = resultHttp.getRespHeaders().get(headerName);
851
852             headerProcessedLine += line.substring(currentIdx, idxS) + value;
853             currentIdx = idxE + 1;
854         }
855
856         // Process body jsonpath macros
857         List<Object> values = new ArrayList<>();
858         String bodyProcessedPattern = "";
859         currentIdx = 0;
860         int maxRows = 1; // in normal case, only one row will be there
861         while (currentIdx < headerProcessedLine.length()) {
862             int idxS = headerProcessedLine.indexOf("$b{", currentIdx);
863             if (idxS == -1) {
864                 bodyProcessedPattern += headerProcessedLine.substring(currentIdx);
865                 break;
866             }
867             int idxE = headerProcessedLine.indexOf("}", idxS);
868             String jsonPath = headerProcessedLine.substring(idxS + 3, idxE);
869             jsonPath = jsonPath.trim();
870             try {
871                 // JSONArray or String
872                 Object value = JsonPath.read(resultHttp.getBody(), jsonPath);
873                 if (value instanceof JSONArray) {
874                     JSONArray arr = (JSONArray) value;
875                     if (arr.size() > maxRows) {
876                         maxRows = arr.size();
877                     }
878                 }
879                 bodyProcessedPattern += headerProcessedLine.substring(currentIdx, idxS) + "%s";
880                 values.add(value);
881                 currentIdx = idxE + 1;
882             } catch (Exception e) {
883                 throw new OnapCommandHttpInvalidResponseBody(jsonPath, e);
884             }
885         }
886
887         if (bodyProcessedPattern.isEmpty()) {
888             result.add(headerProcessedLine);
889             return result;
890         } else {
891             for (int i = 0; i < maxRows; i++) {
892                 currentIdx = 0;
893                 String bodyProcessedLine = "";
894                 int positionalIdx = 0; // %s positional idx
895                 while (currentIdx < bodyProcessedPattern.length()) {
896                     int idxS = bodyProcessedPattern.indexOf("%s", currentIdx);
897                     if (idxS == -1) {
898                         bodyProcessedLine += bodyProcessedPattern.substring(currentIdx);
899                         break;
900                     }
901                     int idxE = idxS + 2; // %s
902                     try {
903                         Object value = values.get(positionalIdx);
904                         String valueS = String.valueOf(value);
905                         if (value instanceof JSONArray) {
906                             JSONArray arr = (JSONArray) value;
907                             if (!arr.isEmpty()) {
908                                 valueS = arr.get(i).toString();
909                             } else {
910                                 throw new OnapCommandResultEmpty();
911                             }
912                         }
913
914                         bodyProcessedLine += bodyProcessedPattern.substring(currentIdx, idxS) + valueS;
915                         currentIdx = idxE;
916                         positionalIdx++;
917                     } catch (OnapCommandResultEmpty e) {
918                         throw e;
919                     } catch (Exception e) {
920                         throw new OnapCommandResultMapProcessingFailed(line, e);
921                     }
922                 }
923                 result.add(bodyProcessedLine);
924             }
925
926             return result;
927         }
928     }
929
930     /**
931      * Set argument to param value.
932      *
933      * @param params
934      *            map
935      * @param input
936      *            HttpInput
937      * @return HttpInput
938      * @throws OnapCommandParameterNotFound
939      *             exception
940      * @throws OnapCommandInvalidParameterValue
941      *             exception
942      */
943     public static HttpInput populateParameters(Map<String, OnapCommandParameter> params, HttpInput input)
944             throws OnapCommandException {
945         HttpInput inp = new HttpInput();
946         for (OnapCommandParameter param : params.values()) {
947             if (ParameterType.BINARY.equals(param.getParameterType())) {
948                 inp.setBinaryData(true);
949                 break;
950             }
951         }
952         inp.setBody(replaceLineFromInputParameters(input.getBody(), params));
953         inp.setUri(replaceLineFromInputParameters(input.getUri(), params));
954         inp.setMethod(input.getMethod().toLowerCase());
955         for (String h : input.getReqHeaders().keySet()) {
956             String value = input.getReqHeaders().get(h);
957             inp.getReqHeaders().put(h, replaceLineFromInputParameters(value, params));
958         }
959
960         for (String h : input.getReqQueries().keySet()) {
961             String value = input.getReqQueries().get(h);
962             inp.getReqQueries().put(h, replaceLineFromInputParameters(value, params));
963         }
964
965         return inp;
966     }
967
968     /**
969      * Populate result.
970      *
971      * @param resultMap
972      *            map
973      * @param resultHttp
974      *            HttpResult
975      * @return map
976      * @throws OnapCommandHttpHeaderNotFound
977      *             header not found exception
978      * @throws OnapCommandHttpInvalidResponseBody
979      *             invalid response body exception
980      * @throws OnapCommandResultMapProcessingFailed
981      *             map processing failed exception
982      */
983     public static Map<String, ArrayList<String>> populateOutputs(Map<String, String> resultMap, HttpResult resultHttp)
984             throws OnapCommandException {
985         Map<String, ArrayList<String>> resultsProcessed = new HashMap<>();
986
987         for (Entry<String, String> entry : resultMap.entrySet()) {
988             String key = entry.getKey();
989             resultsProcessed.put(key, replaceLineFromOutputResults(resultMap.get(key), resultHttp));
990         }
991
992         return resultsProcessed;
993     }
994
995     /**
996      * Find external schema files.
997      *
998      * @return list ExternalSchema
999      * @throws OnapCommandDiscoveryFailed
1000      *             exception
1001      * @throws OnapCommandInvalidSchema
1002      *             exception
1003      */
1004     public static List<ExternalSchema> findAllExternalSchemas() throws OnapCommandException {
1005         List<ExternalSchema> extSchemas = new ArrayList<>();
1006         try {
1007             Resource[] res = getExternalResources(Constants.EXTERNAL_SCHEMA_PATH_PATERN);
1008             if (res != null && res.length > 0) {
1009                 Map<String, ?> resourceMap;
1010                 for (Resource resource : res) {
1011                     resourceMap = getExternalSchemaMap(resource);
1012                     if (resourceMap != null && resourceMap.size() > 0) {
1013                         ExternalSchema schema = new ExternalSchema();
1014                         schema.setSchemaName(resource.getFilename());
1015                         schema.setCmdName((String) resourceMap.get(Constants.NAME));
1016                         Object obj = resourceMap.get(Constants.ONAP_CMD_SCHEMA_VERSION);
1017                         schema.setVersion(obj.toString());
1018                         extSchemas.add(schema);
1019                     }
1020                 }
1021             }
1022         } catch (IOException e) {
1023             throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_SCHEMA_DIRECTORY, e);
1024         }
1025
1026         return extSchemas;
1027     }
1028
1029     /**
1030      * Returns all resources available under certain directory in class-path.
1031      *
1032      * @param pattern
1033      *            search pattern
1034      * @return resources found resources
1035      * @throws IOException
1036      *             exception
1037      */
1038     public static Resource[] getExternalResources(String pattern) throws IOException {
1039         ClassLoader cl = OnapCommandUtils.class.getClassLoader();
1040         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
1041         return resolver.getResources("classpath*:" + pattern);
1042     }
1043
1044     /**
1045      * Returns a resource available under certain directory in class-path.
1046      *
1047      * @param pattern
1048      *            search pattern
1049      * @return found resource
1050      * @throws IOException
1051      *             exception
1052      */
1053     public static Resource getExternalResource(String fileName, String pattern) throws IOException {
1054         Resource[] resources = getExternalResources(pattern);
1055         if (resources != null && resources.length > 0) {
1056             for (Resource res : resources) {
1057                 if (res.getFilename().equals(fileName)) {
1058                     return res;
1059                 }
1060             }
1061         }
1062
1063         return null;
1064     }
1065
1066     /**
1067      * Get schema map.
1068      *
1069      * @param resource
1070      *            resource obj
1071      * @return map
1072      * @throws OnapCommandInvalidSchema
1073      *             exception
1074      */
1075     public static Map<String, ?> getExternalSchemaMap(Resource resource) throws OnapCommandInvalidSchema {
1076         Map<String, ?> values = null;
1077         try {
1078             values = (Map<String, ?>) new Yaml().load(resource.getInputStream());
1079         } catch (Exception e) {
1080             throw new OnapCommandInvalidSchema(resource.getFilename(), e);
1081         }
1082         return values;
1083     }
1084
1085     /**
1086      * Persist the external schema details.
1087      *
1088      * @param schemas
1089      *            list
1090      * @throws OnapCommandDiscoveryFailed
1091      *             exception
1092      */
1093     public static void persist(List<ExternalSchema> schemas) throws OnapCommandDiscoveryFailed {
1094         if (schemas != null) {
1095             try {
1096                 Resource[] resources = getExternalResources(Constants.EXTERNAL_DISCOVERY_DIRECTORY);
1097                 if (resources != null && resources.length == 1) {
1098                     String path = resources[0].getURI().getPath();
1099                     File file = new File(path + File.separator + Constants.EXTERNAL_DISCOVERY_FILE);
1100                     ObjectMapper mapper = new ObjectMapper();
1101                     mapper.writerWithDefaultPrettyPrinter().writeValue(file, schemas);
1102                 }
1103             } catch (IOException e1) {
1104                 throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1105                         Constants.EXTERNAL_DISCOVERY_FILE, e1);
1106             }
1107         }
1108     }
1109
1110     /**
1111      * Check if json file discovered or not.
1112      *
1113      * @return boolean
1114      * @throws OnapCommandDiscoveryFailed
1115      *             exception
1116      */
1117     public static boolean isJsonFileDiscovered() throws OnapCommandDiscoveryFailed {
1118         Resource resource = null;
1119         try {
1120             resource = getExternalResource(Constants.EXTERNAL_DISCOVERY_FILE,
1121                     Constants.EXTERNAL_DISCOVERY_DIRECTORY_PATTERN);
1122             if (resource != null) {
1123                 return true;
1124             }
1125         } catch (IOException e) {
1126             throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1127                     Constants.EXTERNAL_DISCOVERY_FILE, e);
1128         }
1129
1130         return false;
1131     }
1132
1133     /**
1134      * Load the previous discovered json file.
1135      *
1136      * @return list
1137      * @throws OnapCommandInvalidSchema
1138      *             exception
1139      * @throws OnapCommandDiscoveryFailed
1140      *             exception
1141      */
1142     public static List<ExternalSchema> loadExternalSchemasFromJson() throws OnapCommandException {
1143         List<ExternalSchema> schemas = new ArrayList<>();
1144         if (!isJsonFileDiscovered()) {
1145             schemas = findAllExternalSchemas();
1146             if (!schemas.isEmpty()) {
1147                 persist(schemas);
1148             }
1149         } else {
1150             try {
1151                 Resource resource = getExternalResource(Constants.EXTERNAL_DISCOVERY_FILE,
1152                         Constants.EXTERNAL_DISCOVERY_DIRECTORY_PATTERN);
1153                 if (resource != null) {
1154                     File file = new File(resource.getURI().getPath());
1155                     ObjectMapper mapper = new ObjectMapper();
1156                     ExternalSchema[] list = mapper.readValue(file, ExternalSchema[].class);
1157                     schemas.addAll(Arrays.asList(list));
1158                 }
1159             } catch (IOException e) {
1160                 throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1161                         Constants.EXTERNAL_DISCOVERY_FILE, e);
1162             }
1163         }
1164
1165         return schemas;
1166     }
1167
1168     /**
1169      * Fetch a particular schema details.
1170      *
1171      * @param cmd
1172      *            command name
1173      * @return ExternalSchema obj
1174      * @throws OnapCommandInvalidSchema
1175      *             exception
1176      * @throws OnapCommandDiscoveryFailed
1177      *             exception
1178      */
1179     public static ExternalSchema loadExternalSchemaFromJson(String cmd) throws OnapCommandException {
1180         List<ExternalSchema> list = loadExternalSchemasFromJson();
1181         ExternalSchema schemaStr = null;
1182         if (list != null) {
1183             for (ExternalSchema schema : list) {
1184                 if (cmd.equals(schema.getCmdName())) {
1185                     schemaStr = schema;
1186                     break;
1187                 }
1188             }
1189         }
1190         return schemaStr;
1191     }
1192 }