Add auth mode in service
[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.AUTH.equals(key1)) {
252                             Object obj = map.get(key1);
253                             //TODO mrkanag Validate and raise exception for invalid case
254                             srv.setAuthType(obj.toString());
255                         } else if (Constants.MODE.equals(key1)) {
256                             Object obj = map.get(key1);
257                             //TODO mrkanag Validate and raise exception for invalid case
258                             srv.setMode(obj.toString());
259                         }
260                     }
261
262                     cmd.setService(srv);
263                 }
264             } else if (Constants.DEFAULT_PARAMETERS.equals(key)) {
265
266                 Map<String, List<String>> defParameters = (Map) values.get(Constants.DEFAULT_PARAMETERS);
267                 List<String> includeParams = new ArrayList<>();
268                 List<String> excludeParams = new ArrayList<>();
269
270                 if (values.containsKey(Constants.DEFAULT_PARAMETERS) && defParameters == null) {
271                     // if default parameter section is available then it must have either include
272                     // or exclude sub-section.
273                     throw new OnapCommandInvalidSchema(Constants.SCHEMA_INVALID_DEFAULT_PARAMS_SECTION);
274                 }
275
276
277                 if (defParameters != null) {
278                     // validate default parameters
279                     if (defParameters.containsKey(Constants.DEFAULT_PARAMETERS_INCLUDE)) {
280                         includeParams = defParameters.get(Constants.DEFAULT_PARAMETERS_INCLUDE);
281                     }
282
283                     List<String> invInclude = includeParams.stream()
284                             .filter(p -> !defaultParamNames.contains(p))
285                             .collect(Collectors.toList());
286
287                     if (defParameters.containsKey(Constants.DEFAULT_PARAMETERS_EXCLUDE)) {
288                         excludeParams = defParameters.get(Constants.DEFAULT_PARAMETERS_EXCLUDE);
289                     }
290
291                     List<String> invExclude = excludeParams.stream().filter(p -> !defaultParamNames.contains(p))
292                             .collect(Collectors.toList());
293
294
295                     if (!invExclude.isEmpty() || !invInclude.isEmpty()) {
296                         throw new OnapCommandInvalidDefaultParameter(Stream.concat(invInclude.stream(), invExclude.stream())
297                                 .collect(Collectors.toList()));
298                     }
299
300                     if (!includeParams.isEmpty()) {
301                         filteredDefaultParams.addAll(includeParams);
302                     } else if (!excludeParams.isEmpty()) {
303                         List<String> finalExcludeParams = excludeParams;
304                         defaultParamNames.stream().filter(p -> !finalExcludeParams.contains(p))
305                                 .forEach(filteredDefaultParams::add);
306                     }
307                 } else {
308                     filteredDefaultParams.addAll(defaultParamNames);
309                 }
310                 processNoAuth(filteredDefaultParams, cmd, includeParams, excludeParams);
311             } else if (Constants.PARAMETERS.equals(key)) {
312
313                 List<Map<String, String>> parameters = (List) values.get(key);
314
315                 if (parameters != null) {
316                     for (Map<String, String> map : parameters) {
317                         OnapCommandParameter param = new OnapCommandParameter();
318
319                         for (Map.Entry<String, String> entry1 : map.entrySet()) {
320                             String key2 = entry1.getKey();
321
322                             if (Constants.NAME.equals(key2)) {
323                                 if (names.contains(map.get(key2))) {
324                                     throw new OnapCommandParameterNameConflict(map.get(key2));
325                                 }
326                                 names.add(map.get(key2));
327                                 param.setName(map.get(key2));
328                             } else if (Constants.DESCRIPTION.equals(key2)) {
329                                 param.setDescription(map.get(key2));
330                             } else if (Constants.SHORT_OPTION.equals(key2)) {
331                                 if (shortOptions.contains(map.get(key2))) {
332                                     throw new OnapCommandParameterOptionConflict(map.get(key2));
333                                 }
334                                 shortOptions.add(map.get(key2));
335                                 param.setShortOption(map.get(key2));
336                             } else if (Constants.LONG_OPTION.equals(key2)) {
337                                 if (longOptions.contains(map.get(key2))) {
338                                     throw new OnapCommandParameterOptionConflict(map.get(key2));
339                                 }
340                                 longOptions.add(map.get(key2));
341                                 param.setLongOption(map.get(key2));
342                             } else if (Constants.DEFAULT_VALUE.equals(key2)) {
343                                 Object obj = map.get(key2);
344                                 param.setDefaultValue(obj.toString());
345                             } else if (Constants.TYPE.equals(key2)) {
346                                 param.setParameterType(ParameterType.get(map.get(key2)));
347                             } else if (Constants.IS_OPTIONAL.equals(key2)) {
348                                 if ("true".equalsIgnoreCase(String.valueOf(map.get(key2)))) {
349                                     param.setOptional(true);
350                                 } else {
351                                     param.setOptional(false);
352                                 }
353                             } else if (Constants.IS_SECURED.equals(key2)) {
354                                 if ("true".equalsIgnoreCase(String.valueOf(map.get(key2)))) {
355                                     param.setSecured(true);
356                                 } else {
357                                     param.setSecured(false);
358                                 }
359                             }
360                         }
361
362                         // Add the element to command :
363                         // 1. if parameter is available in filtered parameter list.
364                         // 2. otherwise, parameter p is available in command yaml file.
365                         if (filteredDefaultParams.contains(param.getName()) || !defaultParamNames.contains(param.getName())) {
366                             cmd.getParameters().add(param);
367                         }
368                     }
369                 }
370             } else if (Constants.RESULTS.equals(key)) {
371                 Map<String, ?> valueMap = (Map<String, ?>) values.get(key);
372                 if (valueMap != null) {
373                     OnapCommandResult result = new OnapCommandResult();
374                     for (Map.Entry<String, ?> entry1 : valueMap.entrySet()) {
375                         String key3 = entry1.getKey();
376
377                         if (Constants.DIRECTION.equals(key3)) {
378                             result.setPrintDirection(PrintDirection.get((String) valueMap.get(key3)));
379                         } else if (Constants.ATTRIBUTES.equals(key3)) {
380                             List<Map<String, String>> attrs = (ArrayList) valueMap.get(key3);
381
382                             for (Map<String, String> map : attrs) {
383                                 OnapCommandResultAttribute attr = new OnapCommandResultAttribute();
384                                 for (Map.Entry<String, String> entry4 : map.entrySet()) {
385                                     String key4 = entry4.getKey();
386
387                                     if (Constants.NAME.equals(key4)) {
388                                         attr.setName(map.get(key4));
389                                     } else if (Constants.DESCRIPTION.equals(key4)) {
390                                         attr.setDescription(map.get(key4));
391                                     } else if (Constants.SCOPE.equals(key4)) {
392                                         attr.setScope(OnapCommandResultAttributeScope.get(map.get(key4)));
393                                     } else if (Constants.TYPE.equals(key4)) {
394                                         attr.setType(ParameterType.get(map.get(key4)));
395                                     } else if (Constants.IS_SECURED.equals(key4)) {
396                                         if ("true".equals(String.valueOf(map.get(key4)))) {
397                                             attr.setSecured(true);
398                                         } else {
399                                             attr.setSecured(false);
400                                         }
401                                     }
402
403                                 }
404                                 result.getRecords().add(attr);
405                             }
406                         }
407                     }
408                     cmd.setResult(result);
409                 }
410             }
411         }
412     }
413
414     /**
415      * Load the schema.
416      *
417      * @param cmd
418      *            OnapSwaggerBasedCommand
419      * @param schemaName
420      *            schema name
421      * @throws OnapCommandParameterNameConflict
422      *             param name conflict exception
423      * @throws OnapCommandParameterOptionConflict
424      *             param option conflict exception
425      * @throws OnapCommandInvalidParameterType
426      *             invalid param type exception
427      * @throws OnapCommandInvalidPrintDirection
428      *             invalid print direction exception
429      * @throws OnapCommandInvalidResultAttributeScope
430      *             invalid scope exception
431      * @throws OnapCommandSchemaNotFound
432      *             schema not found
433      * @throws OnapCommandInvalidSchema
434      *             invalid schema
435      * @throws OnapCommandInvalidSchemaVersion
436      *             invalid schema version
437      */
438     public static void loadSchema(OnapSwaggerCommand cmd, String schemaName) throws OnapCommandException {
439         try {
440             Map<String, ?> values = (Map<String, ?>) validateSchemaVersion(schemaName, cmd.getSchemaVersion());
441             Map<String, String> valueMap = (Map<String, String>) values.get(Constants.EXECUTOR);
442             OnapCommandExecutor exec = new OnapCommandExecutor();
443
444             for (Map.Entry<String, String> entry1 : valueMap.entrySet()) {
445                 String key1 = entry1.getKey();
446
447                 if (Constants.API.equals(key1)) {
448                     exec.setApi(valueMap.get(key1));
449                 } else if (Constants.CLIENT.equals(key1)) {
450                     exec.setClient(valueMap.get(key1));
451                 } else if (Constants.ENTITY.equals(key1)) {
452                     exec.setEntity(valueMap.get(key1));
453                 } else if (Constants.EXCEPTION.equals(key1)) {
454                     exec.setException(valueMap.get(key1));
455                 } else if (Constants.METHOD.equals(key1)) {
456                     exec.setMethod(valueMap.get(key1));
457                 }
458             }
459
460             cmd.setExecutor(exec);
461         } catch (OnapCommandException e) {
462             throw e;
463         } catch (Exception e) {
464             throw new OnapCommandInvalidSchema(schemaName, e);
465         }
466     }
467
468     /**
469      * Load the schema.
470      *
471      * @param cmd
472      *            OnapHttpCommand
473      * @param schemaName
474      *            schema name
475      * @throws OnapCommandParameterNameConflict
476      *             param name conflict exception
477      * @throws OnapCommandParameterOptionConflict
478      *             param option conflict exception
479      * @throws OnapCommandInvalidParameterType
480      *             invalid param type exception
481      * @throws OnapCommandInvalidPrintDirection
482      *             invalid print direction exception
483      * @throws OnapCommandInvalidResultAttributeScope
484      *             invalid scope exception
485      * @throws OnapCommandSchemaNotFound
486      *             schema not found
487      * @throws OnapCommandInvalidSchema
488      *             invalid schema
489      * @throws OnapCommandInvalidSchemaVersion
490      *             invalid schema version
491      */
492     public static void loadSchema(OnapHttpCommand cmd, String schemaName) throws OnapCommandException {
493         try {
494             Map<String, ?> values = (Map<String, ?>) validateSchemaVersion(schemaName, cmd.getSchemaVersion());
495             Map<String, ?> valMap = (Map<String, ?>) values.get(Constants.HTTP);
496
497             for (Map.Entry<String, ?> entry1 : valMap.entrySet()) {
498                 String key1 = entry1.getKey();
499                 if (Constants.REQUEST.equals(key1)) {
500                     Map<String, ?> map = (Map<String, ?>) valMap.get(key1);
501
502                     for (Map.Entry<String, ?> entry2 : map.entrySet()) {
503                         String key2 = entry2.getKey();
504
505                         if (Constants.URI.equals(key2)) {
506                             Object obj = map.get(key2);
507                             cmd.getInput().setUri(obj.toString());
508                         } else if (Constants.MERHOD.equals(key2)) {
509                             Object obj = map.get(key2);
510                             cmd.getInput().setMethod(obj.toString());
511                         } else if (Constants.BODY.equals(key2)) {
512                             Object obj = map.get(key2);
513                             cmd.getInput().setBody(obj.toString());
514                         } else if (Constants.HEADERS.equals(key2)) {
515                             Map<String, String> head = (Map<String, String>) map.get(key2);
516                             cmd.getInput().setReqHeaders(head);
517                         } else if (Constants.QUERIES.equals(key2)) {
518                             Map<String, String> query = (Map<String, String>) map.get(key2);
519
520                             cmd.getInput().setReqQueries(query);
521                         }
522                     }
523                 } else if (Constants.SUCCESS_CODES.equals(key1)) {
524                     cmd.setSuccessStatusCodes((ArrayList) valMap.get(key1));
525                 } else if (Constants.RESULT_MAP.equals(key1)) {
526                     cmd.setResultMap((Map<String, String>) valMap.get(key1));
527                 } else if (Constants.SAMPLE_RESPONSE.equals(key1)) {
528                     // (mrkanag) implement sample response handling
529                 }
530             }
531
532         } catch (OnapCommandException e) {
533             throw e;
534         } catch (Exception e) {
535             throw new OnapCommandInvalidSchema(schemaName, e);
536         }
537     }
538
539     /**
540      * Returns Help.
541      *
542      * @param cmd
543      *            OnapCommand
544      * @return help string
545      * @throws OnapCommandHelpFailed
546      *             help failed exception
547      */
548     public static String help(OnapCommand cmd) throws OnapCommandHelpFailed {
549         String help = "usage: onap " + cmd.getName();
550
551         // Add description
552         help += "\n\n" + cmd.getDescription();
553
554         // Add service
555         help += "\n\nOnap service: " + cmd.getService();
556
557         // Add whole command
558         String commandOptions = "";
559
560         // Add parameters
561         OnapCommandResult paramTable = new OnapCommandResult();
562         paramTable.setPrintDirection(PrintDirection.LANDSCAPE);
563         paramTable.setType(ResultType.TABLE);
564         paramTable.setIncludeTitle(false);
565         paramTable.setIncludeSeparator(false);
566
567         OnapCommandResultAttribute attrName = new OnapCommandResultAttribute();
568         attrName.setName(Constants.NAME);
569         attrName.setDescription(Constants.NAME);
570         attrName.setScope(OnapCommandResultAttributeScope.SHORT);
571         paramTable.getRecords().add(attrName);
572
573         OnapCommandResultAttribute attrDescription = new OnapCommandResultAttribute();
574         attrDescription.setName(Constants.DESCRIPTION);
575         attrDescription.setDescription(Constants.DESCRIPTION);
576         attrDescription.setScope(OnapCommandResultAttributeScope.SHORT);
577         paramTable.getRecords().add(attrDescription);
578
579         int newLineOptions = 0;
580         for (OnapCommandParameter param : cmd.getParameters()) {
581             // First column Option or positional args
582             String optFirstCol;
583             if (newLineOptions == 3) {
584                 newLineOptions = 0;
585                 commandOptions += "\n";
586             }
587
588             if (param.getShortOption() != null || param.getLongOption() != null) {
589                 optFirstCol = OnapCommandParameter.printShortOption(param.getShortOption()) + " | "
590                         + OnapCommandParameter.printLongOption(param.getLongOption());
591                 commandOptions += "[" + optFirstCol + "] ";
592             } else {
593                 optFirstCol = param.getName();
594                 commandOptions += "<" + optFirstCol + "> ";
595             }
596
597             newLineOptions++;
598
599             attrName.getValues().add(optFirstCol);
600
601             // Second column description
602             String optSecondCol = param.getDescription().trim();
603             if (!optSecondCol.endsWith(".")) {
604                 optSecondCol += ".";
605             }
606             optSecondCol += " It is of type " + param.getParameterType().name() + ".";
607
608             if (param.getParameterType().equals(ParameterType.JSON)
609                     || param.getParameterType().equals(ParameterType.YAML)) {
610                 optSecondCol += " It's recommended to input the complete path of the file, which is having the value for it.";
611             }
612             if (param.isOptional()) {
613                 optSecondCol += " It is optional.";
614             }
615
616             String defaultMsg = " By default, it is ";
617             if (param.isDefaultValueAnEnv()) {
618                 optSecondCol += defaultMsg + "read from environment variable " + param.getEnvVarNameFromDefaultValue()
619                         + ".";
620             } else if (param.getDefaultValue() != null && !((String)param.getDefaultValue()).isEmpty()) {
621                 optSecondCol += defaultMsg + param.getDefaultValue() + ".";
622             }
623
624             if (param.isSecured()) {
625                 optSecondCol += " Secured.";
626             }
627             // (mrkanag) Add help msg for reading default value from env
628             attrDescription.getValues().add(optSecondCol);
629         }
630
631         try {
632             help += "\n\nOptions:\n" + commandOptions + "\nwhere,\n" + paramTable.print();
633         } catch (OnapCommandException e) {
634             throw new OnapCommandHelpFailed(e);
635         }
636
637         // Add results
638         OnapCommandResult resultTable = new OnapCommandResult();
639         resultTable.setPrintDirection(PrintDirection.PORTRAIT);
640         resultTable.setType(ResultType.TABLE);
641         resultTable.setIncludeTitle(false);
642         resultTable.setIncludeSeparator(false);
643
644         for (OnapCommandResultAttribute attr : cmd.getResult().getRecords()) {
645             OnapCommandResultAttribute attrHelp = new OnapCommandResultAttribute();
646             attrHelp.setName(attr.getName());
647             attrHelp.setDescription(attr.getDescription());
648             String msg = attr.getDescription() + " and is of type " + attr.getType().name() + ".";
649             if (attr.isSecured()) {
650                 msg += " It is secured.";
651             }
652             attrHelp.getValues().add(msg);
653             attrHelp.setType(attr.getType());
654             resultTable.getRecords().add(attrHelp);
655         }
656         try {
657             help += "\n\nResults:\n" + resultTable.print();
658         } catch (OnapCommandException e) {
659             throw new OnapCommandHelpFailed(e);
660         }
661
662         // Error
663         help += "\n\nError:\nOn error, it prints <HTTP STATUS CODE>::<ERROR CODE>::<ERROR MESSAGE>\n";
664         return help;
665     }
666
667     /**
668      * Helps to create OnapCredentials from default params.
669      *
670      * @param params
671      *            list of parameters
672      * @return OnapCredentials
673      * @throws OnapCommandInvalidParameterValue
674      *             exception
675      */
676     public static OnapCredentials fromParameters(List<OnapCommandParameter> params)
677             throws OnapCommandInvalidParameterValue {
678         Map<String, String> paramMap = new HashMap<>();
679
680         for (OnapCommandParameter param : params) {
681             paramMap.put(param.getName(), param.getValue().toString());
682         }
683
684         return new OnapCredentials(paramMap.get(Constants.DEAFULT_PARAMETER_USERNAME),
685                 paramMap.get(Constants.DEAFULT_PARAMETER_PASS_WORD),
686                 paramMap.get(Constants.DEAFULT_PARAMETER_HOST_URL));
687     }
688
689     /**
690      * Create Dict from list of Parameters.
691      *
692      * @param inputs
693      *            list of parameters
694      * @return map
695      */
696     public static Map<String, OnapCommandParameter> getInputMap(List<OnapCommandParameter> inputs) {
697         Map<String, OnapCommandParameter> map = new HashMap<>();
698         for (OnapCommandParameter param : inputs) {
699             map.put(param.getName(), param);
700         }
701         return map;
702     }
703
704     /**
705      * Discover the Onap commands.
706      *
707      * @return list
708      */
709     public static List<Class<OnapCommand>> findOnapCommands() {
710         ServiceLoader<OnapCommand> loader = ServiceLoader.load(OnapCommand.class);
711         List<Class<OnapCommand>> clss = new ArrayList<>();
712         for (OnapCommand implClass : loader) {
713             clss.add((Class<OnapCommand>) implClass.getClass());
714         }
715
716         return clss;
717     }
718
719     /**
720      * sort the set.
721      *
722      * @param col
723      *            set
724      * @return list
725      */
726     public static List<String> sort(Set<String> col) {
727         List<String> results = new ArrayList<>();
728         results.addAll(col);
729         Collections.sort(results);
730         return results;
731     }
732
733     /**
734      * Flatten the json list.
735      *
736      * @param jsons
737      *            list json strings
738      * @return list
739      */
740     public static List<String> jsonFlatten(List<String> jsons) {
741         List<String> results = new ArrayList<>();
742         for (String json : jsons) {
743             try {
744                 results.add(JsonPath.parse(json).jsonString());
745             } catch (Exception e) { // NOSONAR
746                 results.add(json);
747             }
748         }
749
750         return results;
751     }
752
753     /**
754      * Construct method name.
755      *
756      * @param name
757      *            name
758      * @param prefix
759      *            prefix
760      * @return string
761      */
762     public static String formMethodNameFromAttributeName(String name, String prefix) {
763         if (name == null || name.isEmpty()) {
764             return name;
765         }
766
767         String methodName = prefix;
768         for (String tk : name.split("-")) {
769             methodName += Character.toString(tk.charAt(0)).toUpperCase();
770             methodName += tk.substring(1);
771         }
772         return methodName;
773     }
774
775     private static String replaceLineFromInputParameters(String line, Map<String, OnapCommandParameter> params)
776             throws OnapCommandException {
777         String result = "";
778
779         if (!line.contains("${")) {
780             return line;
781         }
782
783         int currentIdx = 0;
784         while (currentIdx < line.length()) {
785             int idxS = line.indexOf("${", currentIdx);
786             if (idxS == -1) {
787                 result += line.substring(currentIdx);
788                 break;
789             }
790             int idxE = line.indexOf("}", idxS);
791             String paramName = line.substring(idxS + 2, idxE);
792             paramName = paramName.trim();
793             if (!params.containsKey(paramName)) {
794                 throw new OnapCommandParameterNotFound(paramName);
795             }
796
797             String value = params.get(paramName).getValue().toString();
798
799             OnapCommandParameter param = params.get(paramName);
800             if (ParameterType.ARRAY.equals(param.getParameterType())
801                     || ParameterType.MAP.equals(param.getParameterType())
802                     || ParameterType.JSON.equals(param.getParameterType())
803                     || ParameterType.YAML.equals(param.getParameterType())) {
804                 // ignore the front and back double quotes in json body
805                 result += line.substring(currentIdx, idxS - 1) + value;
806                 currentIdx = idxE + 2;
807             } else {
808                 result += line.substring(currentIdx, idxS) + value;
809                 currentIdx = idxE + 1;
810             }
811         }
812
813         return result;
814     }
815
816     private static ArrayList<String> replaceLineFromOutputResults(String line, HttpResult resultHttp)
817             throws OnapCommandHttpHeaderNotFound, OnapCommandHttpInvalidResponseBody,
818             OnapCommandResultMapProcessingFailed, OnapCommandResultEmpty {
819         String headerProcessedLine = "";
820
821         ArrayList<String> result = new ArrayList<>();
822         if (!line.contains("$b{") && !line.contains("$h{")) {
823             result.add(line);
824             return result;
825         }
826
827         /**
828          * In case of empty response body [] or {}
829          **/
830         if (resultHttp.getBody().length() <= 2) {
831             return result;
832         }
833
834         /**
835          * Process headers macros : line: $h{abc}-$b{$.[*].xyz} , After processing line will be [abc's
836          * value]-$b{$.[*].xyz}
837          **/
838         int currentIdx = 0;
839         while (currentIdx < line.length()) {
840             int idxS = line.indexOf("$h{", currentIdx);
841             if (idxS == -1) {
842                 headerProcessedLine += line.substring(currentIdx);
843                 break;
844             }
845             int idxE = line.indexOf("}", idxS);
846             String headerName = line.substring(idxS + 3, idxE);
847             headerName = headerName.trim();
848             if (!resultHttp.getRespHeaders().containsKey(headerName)) {
849                 throw new OnapCommandHttpHeaderNotFound(headerName);
850             }
851             String value = resultHttp.getRespHeaders().get(headerName);
852
853             headerProcessedLine += line.substring(currentIdx, idxS) + value;
854             currentIdx = idxE + 1;
855         }
856
857         // Process body jsonpath macros
858         List<Object> values = new ArrayList<>();
859         String bodyProcessedPattern = "";
860         currentIdx = 0;
861         int maxRows = 1; // in normal case, only one row will be there
862         while (currentIdx < headerProcessedLine.length()) {
863             int idxS = headerProcessedLine.indexOf("$b{", currentIdx);
864             if (idxS == -1) {
865                 bodyProcessedPattern += headerProcessedLine.substring(currentIdx);
866                 break;
867             }
868             int idxE = headerProcessedLine.indexOf("}", idxS);
869             String jsonPath = headerProcessedLine.substring(idxS + 3, idxE);
870             jsonPath = jsonPath.trim();
871             try {
872                 // JSONArray or String
873                 Object value = JsonPath.read(resultHttp.getBody(), jsonPath);
874                 if (value instanceof JSONArray) {
875                     JSONArray arr = (JSONArray) value;
876                     if (arr.size() > maxRows) {
877                         maxRows = arr.size();
878                     }
879                 }
880                 bodyProcessedPattern += headerProcessedLine.substring(currentIdx, idxS) + "%s";
881                 values.add(value);
882                 currentIdx = idxE + 1;
883             } catch (Exception e) {
884                 throw new OnapCommandHttpInvalidResponseBody(jsonPath, e);
885             }
886         }
887
888         if (bodyProcessedPattern.isEmpty()) {
889             result.add(headerProcessedLine);
890             return result;
891         } else {
892             for (int i = 0; i < maxRows; i++) {
893                 currentIdx = 0;
894                 String bodyProcessedLine = "";
895                 int positionalIdx = 0; // %s positional idx
896                 while (currentIdx < bodyProcessedPattern.length()) {
897                     int idxS = bodyProcessedPattern.indexOf("%s", currentIdx);
898                     if (idxS == -1) {
899                         bodyProcessedLine += bodyProcessedPattern.substring(currentIdx);
900                         break;
901                     }
902                     int idxE = idxS + 2; // %s
903                     try {
904                         Object value = values.get(positionalIdx);
905                         String valueS = String.valueOf(value);
906                         if (value instanceof JSONArray) {
907                             JSONArray arr = (JSONArray) value;
908                             if (!arr.isEmpty()) {
909                                 valueS = arr.get(i).toString();
910                             } else {
911                                 throw new OnapCommandResultEmpty();
912                             }
913                         }
914
915                         bodyProcessedLine += bodyProcessedPattern.substring(currentIdx, idxS) + valueS;
916                         currentIdx = idxE;
917                         positionalIdx++;
918                     } catch (OnapCommandResultEmpty e) {
919                         throw e;
920                     } catch (Exception e) {
921                         throw new OnapCommandResultMapProcessingFailed(line, e);
922                     }
923                 }
924                 result.add(bodyProcessedLine);
925             }
926
927             return result;
928         }
929     }
930
931     /**
932      * Set argument to param value.
933      *
934      * @param params
935      *            map
936      * @param input
937      *            HttpInput
938      * @return HttpInput
939      * @throws OnapCommandParameterNotFound
940      *             exception
941      * @throws OnapCommandInvalidParameterValue
942      *             exception
943      */
944     public static HttpInput populateParameters(Map<String, OnapCommandParameter> params, HttpInput input)
945             throws OnapCommandException {
946         HttpInput inp = new HttpInput();
947         for (OnapCommandParameter param : params.values()) {
948             if (ParameterType.BINARY.equals(param.getParameterType())) {
949                 inp.setBinaryData(true);
950                 break;
951             }
952         }
953         inp.setBody(replaceLineFromInputParameters(input.getBody(), params));
954         inp.setUri(replaceLineFromInputParameters(input.getUri(), params));
955         inp.setMethod(input.getMethod().toLowerCase());
956         for (String h : input.getReqHeaders().keySet()) {
957             String value = input.getReqHeaders().get(h);
958             inp.getReqHeaders().put(h, replaceLineFromInputParameters(value, params));
959         }
960
961         for (String h : input.getReqQueries().keySet()) {
962             String value = input.getReqQueries().get(h);
963             inp.getReqQueries().put(h, replaceLineFromInputParameters(value, params));
964         }
965
966         return inp;
967     }
968
969     /**
970      * Populate result.
971      *
972      * @param resultMap
973      *            map
974      * @param resultHttp
975      *            HttpResult
976      * @return map
977      * @throws OnapCommandHttpHeaderNotFound
978      *             header not found exception
979      * @throws OnapCommandHttpInvalidResponseBody
980      *             invalid response body exception
981      * @throws OnapCommandResultMapProcessingFailed
982      *             map processing failed exception
983      */
984     public static Map<String, ArrayList<String>> populateOutputs(Map<String, String> resultMap, HttpResult resultHttp)
985             throws OnapCommandException {
986         Map<String, ArrayList<String>> resultsProcessed = new HashMap<>();
987
988         for (Entry<String, String> entry : resultMap.entrySet()) {
989             String key = entry.getKey();
990             resultsProcessed.put(key, replaceLineFromOutputResults(resultMap.get(key), resultHttp));
991         }
992
993         return resultsProcessed;
994     }
995
996     /**
997      * Find external schema files.
998      *
999      * @return list ExternalSchema
1000      * @throws OnapCommandDiscoveryFailed
1001      *             exception
1002      * @throws OnapCommandInvalidSchema
1003      *             exception
1004      */
1005     public static List<ExternalSchema> findAllExternalSchemas() throws OnapCommandException {
1006         List<ExternalSchema> extSchemas = new ArrayList<>();
1007         try {
1008             Resource[] res = getExternalResources(Constants.EXTERNAL_SCHEMA_PATH_PATERN);
1009             if (res != null && res.length > 0) {
1010                 Map<String, ?> resourceMap;
1011                 for (Resource resource : res) {
1012                     resourceMap = getExternalSchemaMap(resource);
1013                     if (resourceMap != null && resourceMap.size() > 0) {
1014                         ExternalSchema schema = new ExternalSchema();
1015                         schema.setSchemaName(resource.getFilename());
1016                         schema.setCmdName((String) resourceMap.get(Constants.NAME));
1017                         Object obj = resourceMap.get(Constants.ONAP_CMD_SCHEMA_VERSION);
1018                         schema.setVersion(obj.toString());
1019                         extSchemas.add(schema);
1020                     }
1021                 }
1022             }
1023         } catch (IOException e) {
1024             throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_SCHEMA_DIRECTORY, e);
1025         }
1026
1027         return extSchemas;
1028     }
1029
1030     /**
1031      * Returns all resources available under certain directory in class-path.
1032      *
1033      * @param pattern
1034      *            search pattern
1035      * @return resources found resources
1036      * @throws IOException
1037      *             exception
1038      */
1039     public static Resource[] getExternalResources(String pattern) throws IOException {
1040         ClassLoader cl = OnapCommandUtils.class.getClassLoader();
1041         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
1042         return resolver.getResources("classpath*:" + pattern);
1043     }
1044
1045     /**
1046      * Returns a resource available under certain directory in class-path.
1047      *
1048      * @param pattern
1049      *            search pattern
1050      * @return found resource
1051      * @throws IOException
1052      *             exception
1053      */
1054     public static Resource getExternalResource(String fileName, String pattern) throws IOException {
1055         Resource[] resources = getExternalResources(pattern);
1056         if (resources != null && resources.length > 0) {
1057             for (Resource res : resources) {
1058                 if (res.getFilename().equals(fileName)) {
1059                     return res;
1060                 }
1061             }
1062         }
1063
1064         return null;
1065     }
1066
1067     /**
1068      * Get schema map.
1069      *
1070      * @param resource
1071      *            resource obj
1072      * @return map
1073      * @throws OnapCommandInvalidSchema
1074      *             exception
1075      */
1076     public static Map<String, ?> getExternalSchemaMap(Resource resource) throws OnapCommandInvalidSchema {
1077         Map<String, ?> values = null;
1078         try {
1079             values = (Map<String, ?>) new Yaml().load(resource.getInputStream());
1080         } catch (Exception e) {
1081             throw new OnapCommandInvalidSchema(resource.getFilename(), e);
1082         }
1083         return values;
1084     }
1085
1086     /**
1087      * Persist the external schema details.
1088      *
1089      * @param schemas
1090      *            list
1091      * @throws OnapCommandDiscoveryFailed
1092      *             exception
1093      */
1094     public static void persist(List<ExternalSchema> schemas) throws OnapCommandDiscoveryFailed {
1095         if (schemas != null) {
1096             try {
1097                 Resource[] resources = getExternalResources(Constants.EXTERNAL_DISCOVERY_DIRECTORY);
1098                 if (resources != null && resources.length == 1) {
1099                     String path = resources[0].getURI().getPath();
1100                     File file = new File(path + File.separator + Constants.EXTERNAL_DISCOVERY_FILE);
1101                     ObjectMapper mapper = new ObjectMapper();
1102                     mapper.writerWithDefaultPrettyPrinter().writeValue(file, schemas);
1103                 }
1104             } catch (IOException e1) {
1105                 throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1106                         Constants.EXTERNAL_DISCOVERY_FILE, e1);
1107             }
1108         }
1109     }
1110
1111     /**
1112      * Check if json file discovered or not.
1113      *
1114      * @return boolean
1115      * @throws OnapCommandDiscoveryFailed
1116      *             exception
1117      */
1118     public static boolean isJsonFileDiscovered() throws OnapCommandDiscoveryFailed {
1119         Resource resource = null;
1120         try {
1121             resource = getExternalResource(Constants.EXTERNAL_DISCOVERY_FILE,
1122                     Constants.EXTERNAL_DISCOVERY_DIRECTORY_PATTERN);
1123             if (resource != null) {
1124                 return true;
1125             }
1126         } catch (IOException e) {
1127             throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1128                     Constants.EXTERNAL_DISCOVERY_FILE, e);
1129         }
1130
1131         return false;
1132     }
1133
1134     /**
1135      * Load the previous discovered json file.
1136      *
1137      * @return list
1138      * @throws OnapCommandInvalidSchema
1139      *             exception
1140      * @throws OnapCommandDiscoveryFailed
1141      *             exception
1142      */
1143     public static List<ExternalSchema> loadExternalSchemasFromJson() throws OnapCommandException {
1144         List<ExternalSchema> schemas = new ArrayList<>();
1145         if (!isJsonFileDiscovered()) {
1146             schemas = findAllExternalSchemas();
1147             if (!schemas.isEmpty()) {
1148                 persist(schemas);
1149             }
1150         } else {
1151             try {
1152                 Resource resource = getExternalResource(Constants.EXTERNAL_DISCOVERY_FILE,
1153                         Constants.EXTERNAL_DISCOVERY_DIRECTORY_PATTERN);
1154                 if (resource != null) {
1155                     File file = new File(resource.getURI().getPath());
1156                     ObjectMapper mapper = new ObjectMapper();
1157                     ExternalSchema[] list = mapper.readValue(file, ExternalSchema[].class);
1158                     schemas.addAll(Arrays.asList(list));
1159                 }
1160             } catch (IOException e) {
1161                 throw new OnapCommandDiscoveryFailed(Constants.EXTERNAL_DISCOVERY_DIRECTORY,
1162                         Constants.EXTERNAL_DISCOVERY_FILE, e);
1163             }
1164         }
1165
1166         return schemas;
1167     }
1168
1169     /**
1170      * Fetch a particular schema details.
1171      *
1172      * @param cmd
1173      *            command name
1174      * @return ExternalSchema obj
1175      * @throws OnapCommandInvalidSchema
1176      *             exception
1177      * @throws OnapCommandDiscoveryFailed
1178      *             exception
1179      */
1180     public static ExternalSchema loadExternalSchemaFromJson(String cmd) throws OnapCommandException {
1181         List<ExternalSchema> list = loadExternalSchemasFromJson();
1182         ExternalSchema schemaStr = null;
1183         if (list != null) {
1184             for (ExternalSchema schema : list) {
1185                 if (cmd.equals(schema.getCmdName())) {
1186                     schemaStr = schema;
1187                     break;
1188                 }
1189             }
1190         }
1191         return schemaStr;
1192     }
1193 }