Use static constants instead
[cli.git] / profiles / http / src / main / java / org / onap / cli / fw / http / schema / OnapCommandSchemaHttpLoader.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.http.schema;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28
29 import org.onap.cli.fw.cmd.OnapCommand;
30 import org.onap.cli.fw.cmd.OnapCommandType;
31 import org.onap.cli.fw.conf.OnapCommandConfig;
32 import org.onap.cli.fw.conf.OnapCommandConstants;
33 import org.onap.cli.fw.error.OnapCommandException;
34 import org.onap.cli.fw.error.OnapCommandInvalidSchema;
35 import org.onap.cli.fw.error.OnapCommandNotFound;
36 import org.onap.cli.fw.http.auth.OnapCommandHttpService;
37 import org.onap.cli.fw.http.cmd.OnapHttpCommand;
38 import org.onap.cli.fw.http.conf.OnapCommandHttpConstants;
39 import org.onap.cli.fw.http.error.OnapCommandHttpInvalidResultMap;
40 import org.onap.cli.fw.registrar.OnapCommandRegistrar;
41 import org.onap.cli.fw.schema.OnapCommandSchemaLoader;
42 import org.onap.cli.fw.utils.OnapCommandUtils;
43
44 import com.fasterxml.jackson.databind.ObjectMapper;
45
46 import net.minidev.json.JSONObject;
47
48 public class OnapCommandSchemaHttpLoader {
49
50     private static final String ATTRIBUTE = "Attribute '";
51
52     private OnapCommandSchemaHttpLoader() {
53         // to follow standards !
54     }
55
56     public static List<String> loadHttpSchema(OnapHttpCommand cmd, String schemaName, boolean includeDefault,
57                                           boolean validateSchema) throws OnapCommandException {
58         try {
59             List<String> errors = new ArrayList<>();
60             if (includeDefault) {
61                 Map<String, ?> defaultParameterMap = OnapCommandSchemaLoader.validateSchemaVersion(OnapCommandHttpConstants.DEFAULT_PARAMETER_HTTP_FILE_NAME, cmd.getSchemaVersion());
62
63                 //mrkanag default_parameter is supported only for parameters.
64                 if (defaultParameterMap.containsKey(OnapCommandConstants.INFO)) {
65                     defaultParameterMap.remove(OnapCommandConstants.INFO);
66                 }
67
68                 errors.addAll(OnapCommandSchemaLoader.parseSchema(cmd, defaultParameterMap, validateSchema));
69             }
70
71             Map<String, List<Map<String, String>>> commandYamlMap =
72                     (Map<String, List<Map<String, String>>>)OnapCommandSchemaLoader.validateSchemaVersion(schemaName, cmd.getSchemaVersion());
73
74             errors.addAll(parseHttpSchema(cmd, commandYamlMap, validateSchema));
75
76             return errors;
77
78         } catch (OnapCommandException e) {
79             throw e;
80         } catch (Exception e) {
81             throw new OnapCommandInvalidSchema(schemaName, e);
82         }
83     }
84
85     /**
86      * Load the schema.
87      *
88      * @param cmd
89      *            OnapHttpCommand
90      * @param schemaName
91      *            schema name
92      * @throws OnapCommandException
93      *             on error
94      */
95     public static ArrayList<String> parseHttpSchema(OnapHttpCommand cmd,
96                                                     final Map<String, ?> values,
97                                                     boolean validate) throws OnapCommandException {
98         ArrayList<String> errorList = new ArrayList<>();
99         try {
100             Map<String, ?> valMap = (Map<String, ?>) values.get(OnapCommandHttpConstants.HTTP);
101
102             if (valMap != null) {
103                 if (validate) {
104                     OnapCommandUtils.validateTags(errorList, valMap, OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_SECTIONS),
105                             OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_MANDATORY_SECTIONS), OnapCommandHttpConstants.HTTP);
106                     errorList.addAll(validateHttpSchemaSection(values));
107                 }
108                 for (Map.Entry<String, ?> entry1 : valMap.entrySet()) {
109                     String key1 = entry1.getKey();
110
111                     switch (key1) {
112                         case OnapCommandHttpConstants.REQUEST:
113                             Map<String, ?> map = (Map<String, ?>) valMap.get(key1);
114
115                             for (Map.Entry<String, ?> entry2 : map.entrySet()) {
116                                 try {
117                                     String key2 = entry2.getKey();
118
119                                     switch (key2) {
120                                         case OnapCommandHttpConstants.URI:
121                                             Object obj = map.get(key2);
122                                             cmd.getInput().setUri(obj.toString());
123                                             break;
124                                         case OnapCommandHttpConstants.METHOD_TYPE:
125                                             Object method = map.get(key2);
126                                             cmd.getInput().setMethod(method.toString());
127                                             break;
128                                         case OnapCommandHttpConstants.BODY:
129                                             Object body = map.get(key2);
130                                             cmd.getInput().setBody(body.toString());
131                                             break;
132                                         case OnapCommandHttpConstants.HEADERS:
133                                             Map<String, String> head = (Map<String, String>) map.get(key2);
134                                             cmd.getInput().setReqHeaders(head);
135                                             break;
136                                         case OnapCommandHttpConstants.QUERIES:
137                                             Map<String, String> query = (Map<String, String>) map.get(key2);
138
139                                             cmd.getInput().setReqQueries(query);
140                                             break;
141                                         case OnapCommandHttpConstants.CONTEXT:
142                                             Map<String, Object> context = (Map<String, Object>) map.get(key2);
143
144                                             for (String key: context.keySet()) {
145                                                 switch (key) {
146                                                     case OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES:
147                                                         Boolean flag = (Boolean) context.get(OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES);
148                                                         cmd.getInput().getContext().put(OnapCommandHttpConstants.CONTEXT_REMOVE_EMPTY_JSON_NODES, flag.toString());
149                                                         break;
150                                                 }
151                                             }
152
153
154                                             break;
155                                         case OnapCommandHttpConstants.MULTIPART_ENTITY_NAME:
156                                             Object multipartEntityName = map.get(key2);
157                                             cmd.getInput().setMultipartEntityName(multipartEntityName.toString());
158                                             break;
159                                     }
160                                 }catch (Exception ex) {
161                                     OnapCommandUtils.throwOrCollect(new OnapCommandInvalidSchema(cmd.getSchemaName(), ex), errorList, validate);
162                                 }
163                             }
164                             break;
165
166                         case OnapCommandHttpConstants.SERVICE:
167                             Map<String, String> serviceMap = (Map<String, String>) valMap.get(key1);
168
169                             if (serviceMap != null) {
170                                 if (validate) {
171                                     OnapCommandUtils.validateTags(errorList, (Map<String, Object>) valMap.get(key1),
172                                             OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.SERVICE_PARAMS_LIST),
173                                             OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.SERVICE_PARAMS_MANDATORY_LIST), OnapCommandHttpConstants.SERVICE);
174
175                                     HashMap<String, String> validationMap = new HashMap<>();
176                                     validationMap.put(OnapCommandHttpConstants.AUTH, OnapCommandHttpConstants.AUTH_VALUES);
177                                     validationMap.put(OnapCommandHttpConstants.MODE, OnapCommandHttpConstants.MODE_VALUES);
178
179                                     for (String secKey : validationMap.keySet()) {
180                                         if (serviceMap.containsKey(secKey)) {
181                                             Object obj = serviceMap.get(secKey);
182                                             if (obj == null) {
183                                                 errorList.add(ATTRIBUTE + secKey + "' under '" + OnapCommandHttpConstants.SERVICE + "' is empty");
184                                             } else {
185                                                 String value = String.valueOf(obj);
186                                                 if (!OnapCommandConfig.getCommaSeparatedList(validationMap.get(secKey)).contains(value)) {
187                                                     errorList.add(ATTRIBUTE + secKey + "' contains invalid value. Valide values are "
188                                                             + OnapCommandConfig.getCommaSeparatedList(validationMap.get(key1))); //
189                                                 }
190                                             }
191                                         }
192                                     }
193                                 }
194
195                                 OnapCommandHttpService srv = new OnapCommandHttpService();
196
197                                 for (Map.Entry<String, String> entry : serviceMap.entrySet()) {
198                                     String key = entry.getKey();
199
200                                     switch (key) {
201                                         case OnapCommandConstants.NAME:
202                                             srv.setName(serviceMap.get(key));
203                                             break;
204
205                                         case OnapCommandHttpConstants.VERSION:
206                                             srv.setVersion(serviceMap.get(key).toString());
207                                             break;
208
209                                         case OnapCommandHttpConstants.AUTH:
210                                             Object obj = serviceMap.get(key);
211                                             srv.setAuthType(obj.toString());
212                                             break;
213
214                                         case OnapCommandHttpConstants.MODE:
215                                             Object mode = serviceMap.get(key);
216                                             srv.setMode(mode.toString());
217                                             break;
218                                     }
219                                 }
220
221                                 cmd.setService(srv);
222                             }
223                             break;
224
225                         case OnapCommandHttpConstants.SUCCESS_CODES:
226                             if (validate) {
227                                 validateHttpSccessCodes(errorList, (List<Object>) valMap.get(key1));
228                             }
229                             cmd.setSuccessStatusCodes((ArrayList) valMap.get(key1));
230                             break;
231
232                         case OnapCommandHttpConstants.RESULT_MAP:
233                             if (validate) {
234                                 validateHttpResultMap(errorList, values);
235                             }
236                             cmd.setResultMap((Map<String, String>) valMap.get(key1));
237                             break;
238
239                         case OnapCommandHttpConstants.SAMPLE_RESPONSE:
240                             // (mrkanag) implement sample response handling
241                             break;
242                     }
243                 }
244             }
245         }catch (OnapCommandException e) {
246             OnapCommandUtils.throwOrCollect(e, errorList, validate);
247         }
248
249         //Handle the parameters for auth:
250         // for commands, copy params from login command to this command
251         if (!cmd.getService().isNoAuth()) {
252             if (cmd.getInfo().getCommandType().equals(OnapCommandType.AUTH)) {
253                 OnapCommandUtils.throwOrCollect(new OnapCommandInvalidSchema(
254                         cmd.getSchemaName(), "For auth type commands, http->service->auth should be none"),
255                         errorList,
256                         validate);
257             } else {
258                 OnapCommand login = OnapCommandSchemaHttpLoader.findAuthCommand(cmd, "login");
259                 OnapCommandUtils.copyParamSchemasFrom(login, cmd);
260             }
261         } else {
262             //with service->auth: none,
263             //normal cmd: ignore all auth parms
264             if (!cmd.getInfo().getCommandType().equals(OnapCommandType.AUTH)) {
265                 cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_USERNAME).setInclude(false);
266                 cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_PASSWORD).setInclude(false);
267                 cmd.getParametersMap().get(OnapCommandHttpConstants.DEFAULT_PARAMETER_NO_AUTH).setInclude(false);
268             } else {
269                 //auth: login and logout commands, ignore no-auth
270                 cmd.getParametersMap().get(OnapCommandHttpConstants.DEFAULT_PARAMETER_NO_AUTH).setInclude(false);
271                 //auth: only for logout commands, ignore username and password too
272                 if (!cmd.getName().endsWith(OnapCommandHttpConstants.AUTH_SERVICE_LOGIN)) {
273                     cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_USERNAME).setInclude(false);
274                     cmd.getParametersMap().get(OnapCommandHttpConstants.DEAFULT_PARAMETER_PASSWORD).setInclude(false);
275                 }
276             }
277         }
278
279         return errorList;
280     }
281
282     public static ArrayList<String> validateHttpSchemaSection(Map<String, ?> values) {
283         ArrayList<String> errorList = new ArrayList<>();
284         Map<String, ?> map = (Map<String, ?>) values.get(OnapCommandHttpConstants.HTTP);
285         Map<String, Object> requestMap = (Map<String, Object>) map.get(OnapCommandHttpConstants.REQUEST);
286
287         if (requestMap != null && !requestMap.isEmpty()) {
288             OnapCommandUtils.validateTags(errorList, requestMap, OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_REQUEST_PARAMS),
289                     OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_REQUEST_MANDATORY_PARAMS), OnapCommandHttpConstants.REQUEST);
290             String method = (String) requestMap.get(OnapCommandHttpConstants.METHOD);
291             if (method != null && !method.isEmpty()) {
292                 if (!OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_METHODS).contains(method.toLowerCase())) {
293                     errorList.add(ATTRIBUTE + OnapCommandHttpConstants.METHOD + "' under '" + OnapCommandHttpConstants.REQUEST + "' is invalid, correct types are "
294                             + OnapCommandConfig.getCommaSeparatedList(OnapCommandHttpConstants.HTTP_METHODS).toString());
295                 }
296             } else {
297                 errorList.add("Http request method cann't be null or empty");
298             }
299
300             Set<String> requestParams = getRequestParams(values);
301
302             Set<String> uriParams = validateHttpUri(errorList, requestMap);
303
304             Set<String> bodyParams = validateHttpBody(errorList, requestMap);
305
306             Set<String> headerParams = validateHttpHeaders(requestMap);
307
308             Set<String> queryParams = validateHttpQueries(requestMap);
309
310             HashSet<String> totoalParams = new HashSet<>(uriParams);
311             totoalParams.addAll(bodyParams);
312             totoalParams.addAll(headerParams);
313             totoalParams.addAll(queryParams);
314
315             List<String> nonDeclaredParams = totoalParams.stream().filter(param -> !requestParams.contains(param))
316                     .collect(Collectors.toList());
317
318             nonDeclaredParams.stream().forEach(p -> errorList.add("The parameter '" + p
319                     + "' declared under 'parameters:' section is not mapped into request section."));
320         } else {
321             errorList.add(OnapCommandUtils.emptySection(OnapCommandHttpConstants.REQUEST));
322         }
323         return errorList;
324     }
325
326     public static void validateHttpSccessCodes(List<String> errorList, List<Object> requestSuccessCodes) {
327
328         if (requestSuccessCodes == null || requestSuccessCodes.isEmpty()) {
329             errorList.add(OnapCommandHttpConstants.HTTP_SUCCESS_CODE_INVALID);
330             return;
331         }
332
333         for (Object successCode : requestSuccessCodes) {
334             Integer code = (Integer) successCode;
335             if (code < 200 || code >= 300) {
336                 if ( code != 404) {
337                     errorList.add(OnapCommandHttpConstants.HTTP_SUCCESS_CODE_INVALID);
338                 }
339             }
340         }
341
342     }
343
344     public static void validateHttpResultMap(List<String> errorList, Map<String, ?> values) throws OnapCommandException {
345         Map<String, ?> valMap = (Map<String, ?>) values.get(OnapCommandHttpConstants.HTTP);
346         List<Map<String, String>> attributes = (List<Map<String, String>>) ((Map<String, ?>)values.get(OnapCommandConstants.RESULTS)).get(OnapCommandConstants.ATTRIBUTES);
347         Set<String> resultMapParams = ((Map<String, String>) valMap.get(OnapCommandHttpConstants.RESULT_MAP)).keySet();
348
349         Set<String> resultAttNames = attributes.stream().map(map -> map.get(OnapCommandConstants.NAME))
350                 .collect(Collectors.toSet());
351
352         List<String> invaliResultMapParams = resultMapParams.stream()
353                 .filter(p -> !resultAttNames.contains(p)).collect(Collectors.toList());
354         List<String> attributesMissing = resultAttNames.stream()
355                 .filter(p -> !resultMapParams.contains(p)).collect(Collectors.toList());
356         invaliResultMapParams.addAll(attributesMissing);
357
358         if (!invaliResultMapParams.isEmpty()) {
359             OnapCommandUtils.throwOrCollect(new OnapCommandHttpInvalidResultMap(invaliResultMapParams), errorList, true);
360         }
361     }
362
363     public static Set<String> validateHttpQueries(Map<String, Object> requestMap) {
364         Map<String, Object> queries = (Map<String, Object>) requestMap.get(OnapCommandHttpConstants.QUERIES);
365         Set<String> queryParamNames = new HashSet<>();
366         if (queries != null) {
367             for (Entry<String, Object> entry : queries.entrySet()) {
368                 OnapCommandUtils.parseParameters(String.valueOf(entry.getValue()), queryParamNames);
369             }
370         }
371         return queryParamNames;
372     }
373
374     public static Set<String> validateHttpHeaders(Map<String, Object> requestMap) {
375
376         Map<String, Object> headers = (Map<String, Object>) requestMap.get(OnapCommandHttpConstants.HEADERS);
377         Set<String> headerParamNames = new HashSet<>();
378         if (headers != null) {
379             for (Entry<String, Object> entry : headers.entrySet()) {
380                 OnapCommandUtils.parseParameters(String.valueOf(entry.getValue()), headerParamNames);
381             }
382         }
383         return headerParamNames;
384     }
385
386     public static Set<String> validateHttpBody(List<String> errorList, Map<String, Object> requestMap) {
387         Set<String> bodyParamNames = new HashSet<>();
388         Object bodyString = requestMap.get(OnapCommandHttpConstants.BODY);
389         if (bodyString == null) {
390             return bodyParamNames;
391         }
392
393         String body = String.valueOf(bodyString);
394
395         if (body == null || "".equals(body)) {
396             errorList.add(OnapCommandHttpConstants.HTTP_BODY_JSON_EMPTY);
397         } else {
398             try {
399                 new ObjectMapper().readValue(body, JSONObject.class);
400             } catch (IOException e1) { // NOSONAR
401                 errorList.add(OnapCommandHttpConstants.HTTP_BODY_FAILED_PARSING);
402             }
403         }
404
405         OnapCommandUtils.parseParameters(body, bodyParamNames);
406
407         return bodyParamNames;
408     }
409
410     public static Set<String> validateHttpUri(List<String> errorList, Map<String, Object> requestMap) {
411         Set<String> uriParamNames = new HashSet<>();
412         String uri = (String) requestMap.get(OnapCommandHttpConstants.URI);
413         if (uri == null || uri.isEmpty()) {
414             errorList.add(OnapCommandUtils.emptySection(OnapCommandHttpConstants.URI));
415             return uriParamNames;
416         }
417         OnapCommandUtils.parseParameters(uri, uriParamNames);
418         return uriParamNames;
419     }
420
421     public static Set<String> getRequestParams(Map<String, ?> yamlMap) {
422
423         Set<String> set = new HashSet<>();
424
425         @SuppressWarnings("unchecked")
426         List<Map<String, Object>> inputParams = (List<Map<String, Object>>) yamlMap.get(OnapCommandConstants.PARAMETERS);
427
428         if (inputParams != null) {
429             for (Map<String, Object> map : inputParams) {
430                 for (Entry<String, Object> entry : map.entrySet()) {
431                     Object key = entry.getKey();
432
433                     if (OnapCommandConstants.NAME.equals(key)) {
434                         set.add(String.valueOf(entry.getValue()));
435                         break;
436                     }
437                 }
438             }
439         }
440
441         return set;
442     }
443
444     /**
445         *
446         * @param authAction login/logout
447         * @return
448         * @throws OnapCommandException
449         */
450        public static OnapCommand findAuthCommand(OnapHttpCommand forCmd, String authAction) throws OnapCommandException {
451            OnapCommand auth = null;
452            try {
453                //mrkanag: fix this to discover the auth command by matching info->product & service
454                auth = OnapCommandRegistrar.getRegistrar().get(
455                        forCmd.getInfo().getService() + "-" +
456                        forCmd.getService().getAuthType() + "-" + authAction,
457                        forCmd.getInfo().getProduct());
458            } catch (OnapCommandNotFound e) {  // NOSONAR
459                auth = OnapCommandRegistrar.getRegistrar().get(
460                        forCmd.getService().getAuthType() + "-" + authAction,
461                        forCmd.getInfo().getProduct());
462            }
463
464            return auth;
465        }
466
467
468 }