PUT and PATCH operation support
[ccsdk/sli/plugins.git] / restconf-client / provider / src / main / java / org / onap / ccsdk / sli / plugins / restconfapicall / RestconfApiUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - CCSDK
4  * ================================================================================
5  * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.ccsdk.sli.plugins.restconfapicall;
22
23 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
24 import org.onap.ccsdk.sli.plugins.restapicall.HttpMethod;
25 import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
28 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
29 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
30 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
31
32 import java.io.File;
33 import java.io.IOException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Map;
43
44 import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.getParameters;
45 import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam;
46 import static org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode.DEFAULT_MODE;
47 import static org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource.forFile;
48 import static org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors.defaultReactor;
49 import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource.create;
50
51 /**
52  * Utilities for restconf api call node.
53  */
54 public final class RestconfApiUtils {
55
56     static final String RES_CODE = "response-code";
57
58     static final String HTTP_REQ ="httpRequest";
59
60     static final String RES_PRE = "responsePrefix";
61
62     static final String RES_MSG = "response-message";
63
64     static final String HEADER = "header.";
65
66     static final String COMMA = ",";
67
68     static final String COLON = ":";
69
70     static final String HTTP_RES = "httpResponse";
71
72     static final String REST_API_URL = "restapiUrl";
73
74     static final String UPDATED_URL = "URL was set to";
75
76     static final String COMM_FAIL = "Failed to communicate with host %s." +
77             "Request will be re-attempted using the host %s.";
78
79     static final String RETRY_COUNT = "This is retry attempt %d out of %d";
80
81     static final String RETRY_FAIL = "Retry attempt has failed. No further " +
82             "retry shall be attempted, calling setFailureResponseStatus";
83
84     static final String NO_MORE_RETRY = "Could not attempt retry";
85
86     static final String MAX_RETRY_ERR = "Maximum retries reached, calling " +
87             "setFailureResponseStatus";
88
89     static final String ATTEMPTS_MSG = "%d attempts were made out of %d " +
90             "maximum retries";
91
92     static final String REQ_ERR = "Error sending the request: ";
93
94     private static final String SLASH = "/";
95
96     private static final String DIR_PATH = "dirPath";
97
98     private static final String URL_SYNTAX = "The following URL cannot be " +
99             "parsed into URI : ";
100
101     private static final String YANG = ".yang";
102
103     private static final String YANG_FILE_ERR = "Unable to parse the YANG " +
104             "file provided";
105
106     //No instantiation.
107     private RestconfApiUtils() {
108     }
109
110     /**
111      * Returns the YANG parameters after parsing it from the map.
112      *
113      * @param paramMap parameters map
114      * @return YANG parameters
115      * @throws SvcLogicException when parsing of parameters map fail
116      */
117     static YangParameters getYangParameters(Map<String, String> paramMap)
118             throws SvcLogicException {
119         YangParameters param = (YangParameters) getParameters(
120                 paramMap, new YangParameters());
121         param.dirPath = parseParam(paramMap, DIR_PATH, false, null);
122         return param;
123     }
124
125     /**
126      * Parses the restconf URL and gives the YANG path from it, which can be
127      * used to get schema node. If it is a PUT operation, then a node must be
128      * reduced from the url to make it always point to the parent.
129      *
130      * @param url    restconf URL
131      * @param method HTTP operation
132      * @return YANG path pointing to parent
133      * @throws SvcLogicException when parsing the URL fails
134      */
135     public static String parseUrl(String url, HttpMethod method)
136             throws SvcLogicException {
137         URI uri;
138         try {
139             uri = new URI(url);
140         } catch (URISyntaxException e) {
141             throw new SvcLogicException(URL_SYNTAX + url, e);
142         }
143
144         String path = uri.getPath();
145         path = getParsedPath(path);
146         return path;
147     }
148
149     /**
150      * Returns the path which contains only the schema nodes.
151      *
152      * @param path path
153      * @return path representing schema
154      */
155     private static String getParsedPath(String path) {
156         String firstHalf;
157         if (path.contains(COLON)) {
158             String[] p = path.split(COLON);
159             if (p[0].contains(SLASH)) {
160                 int slash = p[0].lastIndexOf(SLASH);
161                 firstHalf = p[0].substring(slash + 1);
162             } else {
163                 firstHalf = p[0];
164             }
165             return firstHalf + COLON + p[1];
166         }
167         return path;
168     }
169
170     /**
171      * Returns the schema context of the YANG files present in a directory.
172      *
173      * @param di directory path
174      * @return YANG schema context
175      * @throws SvcLogicException when YANG file reading fails
176      */
177     static SchemaContext getSchemaCtxFromDir(String di)
178             throws SvcLogicException {
179         Path d = Paths.get(di);
180         File dir = d.toFile();
181         List<File> yangFiles = new LinkedList<>();
182         getYangFiles(dir, yangFiles);
183         final Collection<YangStatementStreamSource> sources =
184                 new ArrayList<>(yangFiles.size());
185         for (File file : yangFiles) {
186             try {
187                 sources.add(create(forFile(file)));
188             } catch (IOException | YangSyntaxErrorException e) {
189                 throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e);
190             }
191         }
192
193         final CrossSourceStatementReactor.BuildAction reactor = defaultReactor()
194                 .newBuild(DEFAULT_MODE).addSources(sources);
195         try {
196             return reactor.buildEffective();
197         } catch (ReactorException e) {
198             throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e);
199         }
200     }
201
202     /**
203      * Returns all the YANG files present in a directory recursively.
204      *
205      * @param dir       path of the directory
206      * @param yangFiles list of YANG files
207      */
208     private static void getYangFiles(File dir, List<File> yangFiles) {
209         if (dir.exists()) {
210             File[] files = dir.listFiles();
211             if (files != null) {
212                 processFiles(files, yangFiles);
213             }
214         }
215     }
216
217     /**
218      * Processes all the obtained files by isolating all the YANG files from
219      * all the directory of the given path recursively.
220      *
221      * @param files     files in the given path
222      * @param yangFiles YANG files list
223      */
224     private static void processFiles(File[] files, List<File> yangFiles) {
225         for (File file : files) {
226             if (file.isFile() && file.getName().endsWith(YANG)) {
227                 yangFiles.add(file);
228             } else if (file.isDirectory()) {
229                 getYangFiles(file, yangFiles);
230             }
231         }
232     }
233
234     /**
235      * Returns the updated XML request message by adding root node to it.
236      *
237      * @param req      XML request
238      * @param nodeName root node name
239      * @param modNs    module namespace of the root node
240      * @return updated XML request message
241      */
242     static String getUpdatedXmlReq(String req, String nodeName, String modNs) {
243         String rootNode = "\n<" + nodeName + " xmlns=\"" + modNs.toString() +
244                 "\">\n";
245         req = req.replaceFirst("\n", rootNode);
246         req = req + "</" + nodeName + ">";
247         return req.replaceAll(">\\s+<", "><");
248     }
249 }