2 * Copyright 2018 Huawei Technologies Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onap.vtp.scenario;
20 import java.io.FileNotFoundException;
21 import java.io.FileReader;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.text.MessageFormat;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
30 import java.util.Objects;
31 import java.util.regex.Matcher;
33 import javax.ws.rs.Consumes;
34 import javax.ws.rs.DELETE;
35 import javax.ws.rs.GET;
36 import javax.ws.rs.POST;
37 import javax.ws.rs.Path;
38 import javax.ws.rs.PathParam;
39 import javax.ws.rs.Produces;
40 import javax.ws.rs.QueryParam;
41 import javax.ws.rs.core.MediaType;
42 import javax.ws.rs.core.Response;
44 import com.google.common.collect.Maps;
45 import org.apache.commons.io.FileUtils;
46 import org.apache.commons.lang3.StringUtils;
47 import org.apache.cxf.common.util.CollectionUtils;
48 import org.eclipse.jetty.http.HttpStatus;
49 import org.glassfish.jersey.media.multipart.BodyPartEntity;
50 import org.glassfish.jersey.media.multipart.FormDataBodyPart;
51 import org.glassfish.jersey.media.multipart.FormDataParam;
52 import org.onap.vnfsdk.marketplace.common.CommonConstant;
53 import org.onap.vnfsdk.marketplace.common.FileUtil;
54 import org.onap.vnfsdk.marketplace.common.ToolUtil;
55 import org.onap.vtp.VTPResource;
56 import org.onap.vtp.error.VTPError;
57 import org.onap.vtp.error.VTPError.VTPException;
58 import org.onap.vtp.manager.DistManager;
59 import org.onap.vtp.scenario.model.VTPTestCase;
60 import org.onap.vtp.scenario.model.VTPTestScenario;
61 import org.onap.vtp.scenario.model.VTPTestSuite;
62 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseInput;
63 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseList;
64 import org.onap.vtp.scenario.model.VTPTestCase.VTPTestCaseOutput;
65 import org.onap.vtp.scenario.model.VTPTestScenario.VTPTestScenarioList;
66 import org.onap.vtp.scenario.model.VTPTestSuite.VTPTestSuiteList;
68 import com.google.gson.JsonArray;
69 import com.google.gson.JsonElement;
70 import com.google.gson.JsonObject;
72 import io.swagger.annotations.Api;
73 import io.swagger.annotations.ApiOperation;
74 import io.swagger.annotations.ApiParam;
75 import io.swagger.annotations.ApiResponse;
76 import io.swagger.annotations.ApiResponses;
79 @Api(tags = {"VTP Scenario"})
80 public class VTPScenarioResource extends VTPResource{
81 private static final String DESCRIPTION = "description";
82 private static final String PRODUCT_ARG="--product";
83 private static final String OPEN_CLI="open-cli";
84 private static final String FORMAT="--format";
85 private static final String IO_EXCEPTION_OCCURS ="IOException occurs";
86 private static final String SERVICE="service";
87 private static final String PRODUCT = "product";
88 private DistManager distManagerVtpScenarioResource = new DistManager();
89 public VTPTestScenarioList listTestScenariosHandler() throws VTPException {
90 List<String> args = new ArrayList<>();
92 args.addAll(Arrays.asList(
93 PRODUCT_ARG, OPEN_CLI, "product-list", FORMAT, "json"
97 JsonElement results = null;
99 String endPoint="/manager/scenarios";
100 return distManagerVtpScenarioResource.getScenarioListFromManager(endPoint);
104 results = this.makeRpcAndGetJson(args);
105 } catch (IOException e) {
106 LOG.error(IO_EXCEPTION_OCCURS, e);
110 VTPTestScenarioList list = new VTPTestScenarioList();
112 if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
113 JsonArray resultsArray = results.getAsJsonArray();
114 for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
115 JsonElement jsonElement = it.next();
116 JsonObject n = jsonElement.getAsJsonObject();
117 if (n.entrySet().iterator().hasNext()) {
118 String name = n.get(PRODUCT).getAsString();
120 if (OPEN_CLI.equalsIgnoreCase(name))
123 list.getScenarios().add(new VTPTestScenario().setName(name).setDescription(
124 n.get(DESCRIPTION).getAsString()));
131 private String loggerPatternBreaking(String loggerInput) {
132 return Objects.nonNull(loggerInput) ? loggerInput.replaceAll("[\n\r\t]", "_") : StringUtils.EMPTY;
137 @ApiOperation(tags = "VTP Scenario", value = " List available test scenarios", response = VTPTestScenario.class, responseContainer = "List")
138 @Produces(MediaType.APPLICATION_JSON)
139 @ApiResponses(value = {
140 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
141 message = "Failed to perform the operation",
142 response = VTPError.class) })
143 public Response listTestScenarios() throws VTPException {
144 return Response.ok(this.listTestScenariosHandler().getScenarios().toString(), MediaType.APPLICATION_JSON).build();
147 public VTPTestSuiteList listTestSutiesHandler(String scenario) throws VTPException {
148 List<String> args = new ArrayList<>();
150 args.addAll(Arrays.asList(
151 PRODUCT_ARG, OPEN_CLI, "service-list", PRODUCT_ARG, scenario, FORMAT, "json"
154 JsonElement results = null;
156 String url="/manager/scenarios/"+scenario+"/testsuites";
157 return distManagerVtpScenarioResource.getSuiteListFromManager(url);
160 results = this.makeRpcAndGetJson(args);
161 } catch (IOException e) {
162 LOG.error(IO_EXCEPTION_OCCURS,e);
166 VTPTestSuiteList list = new VTPTestSuiteList();
168 if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
169 JsonArray resultsArray = results.getAsJsonArray();
170 for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
171 JsonElement jsonElement = it.next();
172 JsonObject n = jsonElement.getAsJsonObject();
173 if (n.entrySet().iterator().hasNext()) {
174 list.getSuites().add(new VTPTestSuite().setName(n.get(SERVICE).getAsString()).setDescription(
175 n.get(DESCRIPTION).getAsString()));
183 @Path("/scenarios/{scenario}/testsuites")
185 @ApiOperation(tags = "VTP Scenario", value = " List available test suties in given scenario", response = VTPTestSuite.class, responseContainer = "List")
186 @Produces(MediaType.APPLICATION_JSON)
187 @ApiResponses(value = {
188 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
189 message = "Failed to perform the operation",
190 response = VTPError.class) })
191 public Response listTestSuties(
192 @ApiParam("Test scenario name") @PathParam("scenario") String scenario) throws VTPException {
194 return Response.ok(this.listTestSutiesHandler(scenario).getSuites().toString(), MediaType.APPLICATION_JSON).build();
197 public VTPTestCaseList listTestcasesHandler(String testSuiteName, String scenario) throws VTPException {
198 List<String> args = new ArrayList<>();
200 args.addAll(Arrays.asList(
201 PRODUCT_ARG, OPEN_CLI, "schema-list", PRODUCT_ARG, scenario, FORMAT, "json"
203 if (testSuiteName != null) {
204 args.add("--service");
205 args.add(testSuiteName);
208 JsonElement results = null;
210 String url = "/manager/scenarios/" + scenario + "/testcases";
211 return distManagerVtpScenarioResource.getTestCaseListFromManager(url);
214 results = this.makeRpcAndGetJson(args);
215 } catch (IOException e) {
216 LOG.error(IO_EXCEPTION_OCCURS, e);
220 VTPTestCaseList list = new VTPTestCaseList();
222 if (results != null && results.isJsonArray() && results.getAsJsonArray().size()>0) {
223 JsonArray resultsArray = results.getAsJsonArray();
224 for (Iterator<JsonElement> it = resultsArray.iterator(); it.hasNext();) {
225 JsonElement jsonElement = it.next();
226 JsonObject n = jsonElement.getAsJsonObject();
227 if (n.entrySet().iterator().hasNext())
228 list.getTestCases().add(
229 new VTPTestCase().setTestCaseName(
230 n.get("command").getAsString()).setTestSuiteName(
231 n.get(SERVICE).getAsString()));
238 @Path("/scenarios/{scenario}/testcases")
240 @ApiOperation(tags = "VTP Scenario", value = " List available test cases", response = VTPTestCase.class, responseContainer = "List")
241 @Produces(MediaType.APPLICATION_JSON)
242 @ApiResponses(value = {
243 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
244 message = "Failed to perform the operation",
245 response = VTPError.class) })
246 public Response listTestcases(
247 @ApiParam("Test scenario name") @PathParam("scenario") String scenario,
248 @ApiParam("Test suite name") @QueryParam("testSuiteName") String testSuiteName
249 ) throws VTPException {
251 return Response.ok(this.listTestcasesHandler(testSuiteName, scenario).getTestCases().toString(), MediaType.APPLICATION_JSON).build();
254 public VTPTestCase getTestcaseHandler(String scenario, String testSuiteName, String testCaseName) throws VTPException {
255 List<String> args = new ArrayList<>();
256 args.addAll(Arrays.asList(
257 PRODUCT_ARG, OPEN_CLI, "schema-show", PRODUCT_ARG, scenario, "--service", testSuiteName, "--command", testCaseName , FORMAT, "json"
259 JsonElement results = null;
261 results = this.makeRpcAndGetJson(args);
262 } catch (IOException e) {
263 LOG.error(IO_EXCEPTION_OCCURS,e);
266 JsonObject schema = results.getAsJsonObject().getAsJsonObject("schema");
268 VTPTestCase tc = new VTPTestCase();
269 tc.setTestCaseName(schema.get("name").getAsString());
270 tc.setDescription(schema.get(DESCRIPTION).getAsString());
271 tc.setTestSuiteName(schema.get(SERVICE).getAsString());
272 tc.setAuthor(schema.get("author").getAsString());
273 JsonElement inputsJson = schema.get("inputs");
274 if (inputsJson != null && inputsJson.isJsonArray()) {
275 formatResponseData(tc, inputsJson);
278 JsonElement outputsJson = schema.get("outputs");
279 if (outputsJson != null && outputsJson.isJsonArray() && outputsJson.getAsJsonArray().size()>0) {
280 for (final JsonElement jsonElement: outputsJson.getAsJsonArray()) {
281 JsonObject outputJson = jsonElement.getAsJsonObject();
282 VTPTestCaseOutput output = new VTPTestCaseOutput();
283 output.setName(outputJson.get("name").getAsString());
284 output.setDescription(outputJson.get(DESCRIPTION).getAsString());
285 output.setType(outputJson.get("type").getAsString());
287 tc.getOutputs().add(output);
294 private void formatResponseData(VTPTestCase tc, JsonElement inputsJson) {
295 for (final JsonElement jsonElement: inputsJson.getAsJsonArray()) {
296 JsonObject inputJson = jsonElement.getAsJsonObject();
297 VTPTestCaseInput input = new VTPTestCaseInput();
299 input.setName(inputJson.get("name").getAsString());
300 input.setDescription(inputJson.get(DESCRIPTION).getAsString());
301 input.setType(inputJson.get("type").getAsString());
303 if (inputJson.get("is_optional") != null)
304 input.setIsOptional(inputJson.get("is_optional").getAsBoolean());
306 if (inputJson.get("default_value") != null)
307 input.setDefaultValue(inputJson.get("default_value").getAsString());
309 if (inputJson.get("metadata") != null)
310 input.setMetadata(inputJson.get("metadata"));
312 tc.getInputs().add(input);
316 @Path("/scenarios/{scenario}/testsuites/{testSuiteName}/testcases/{testCaseName}")
318 @ApiOperation(tags = "VTP Scenario", value = "Retrieve test cases details like inputs outputs and test suite name", response = VTPTestCase.class)
319 @Produces(MediaType.APPLICATION_JSON)
320 @ApiResponses(value = {
321 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500,
322 message = "Failed to perform the operation", response = VTPError.class),
323 @ApiResponse(code = HttpStatus.NOT_FOUND_404,
324 message = "Test case does not exist", response = VTPError.class)})
325 public Response getTestcase(
326 @ApiParam("Test scenario name") @PathParam("scenario") String scenario,
327 @ApiParam(value = "Test case name") @PathParam("testSuiteName") String testSuiteName,
328 @ApiParam(value = "Test case name") @PathParam("testCaseName") String testCaseName)
329 throws VTPException {
331 return Response.ok(this.getTestcaseHandler(scenario, testSuiteName, testCaseName).toString(), MediaType.APPLICATION_JSON).build();
336 @ApiOperation(tags = "VTP Scenario", value = "Create Scenario")
337 @Consumes(MediaType.MULTIPART_FORM_DATA)
338 @Produces(MediaType.APPLICATION_JSON)
339 @ApiResponses(value = {
340 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
341 public Response storageScenarios(@ApiParam(value = "file form data body parts", required = true)
342 @FormDataParam("files") List<FormDataBodyPart> bodyParts) throws VTPException {
343 bodyParts.forEach(bodyPart -> {
344 BodyPartEntity entity = (BodyPartEntity) bodyPart.getEntity();
345 String fileName = bodyPart.getContentDisposition().getFileName();
346 if (!ToolUtil.isYamlFile(new File(fileName))) {
347 LOG.error("The fileName {} is not yaml !!!", fileName);
350 String scenario = fileName.substring(0, fileName.indexOf("-registry"));
351 File scenarioDir = new File(VTP_YAML_STORE, scenario);
352 File yamlFile = new File(VTP_YAML_STORE, fileName);
354 // 1、store the scenario yaml file and create the scenario dir
356 FileUtils.deleteQuietly(yamlFile);
357 FileUtils.deleteDirectory(scenarioDir);
358 FileUtils.forceMkdir(scenarioDir);
359 FileUtils.copyInputStreamToFile(entity.getInputStream(), yamlFile);
360 } catch (IOException e) {
361 LOG.error("Save yaml {} failed", fileName, e);
364 // 2、create the testsuits dir and copy the testcase to current scenarios by commands
366 Map<String, Object> yamlInfos = Maps.newHashMap();
367 try (FileReader fileReader = new FileReader(yamlFile)) {
368 yamlInfos = snakeYaml().load(fileReader);
370 for (Object service : (List) yamlInfos.get("services")) {
371 processCurrentScenarioCommands(scenario, scenarioDir, service);
373 } catch (Exception e) {
374 LOG.error("Parse testcase yaml failed !!!", e);
377 return Response.ok("Save yaml success", MediaType.APPLICATION_JSON).build();
380 private void processCurrentScenarioCommands(String scenario, File scenarioDir, Object service)
381 throws IOException, FileNotFoundException {
382 Map<String, Object> serviceMap = (Map<String, Object>) service;
383 String testsuite = serviceMap.get("name").toString();
384 File testsuiteDir = new File(scenarioDir, testsuite);
385 FileUtils.forceMkdir(testsuiteDir);
386 if (!serviceMap.containsKey("commands")) {
389 for (Object cmd : (List) serviceMap.get("commands")) {
390 File source = new File(VTP_YAML_STORE, cmd.toString().replaceAll("::", Matcher.quoteReplacement(File.separator)));
391 if (!source.isFile()) {
392 LOG.error("Source {} is not a yaml file !!!", source.getName());
395 File dest = new File(testsuiteDir, cmd.toString().substring(cmd.toString().lastIndexOf("::") + 2));
396 FileUtils.copyFile(source, dest);
398 // 3、modify the testcase scenario and testsuite
399 Map<String, Object> result = Maps.newHashMap();
400 try (FileReader fileReader = new FileReader(dest)) {
401 result = snakeYaml().load(fileReader);
403 Map<String, Object> info = (Map<String, Object>) result.get("info");
404 info.put(PRODUCT, scenario);
405 info.put(SERVICE, testsuite);
406 try (FileWriter fileWriter = new FileWriter(dest)) {
407 snakeYaml().dump(result, fileWriter);
413 @Path("/scenarios/{scenarioName}")
415 @ApiOperation(tags = "VTP Scenario", value = "Delete yaml string")
416 @Produces(MediaType.APPLICATION_JSON)
417 @ApiResponses(value = {
418 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
419 public Response deleteScenario(@ApiParam("Test scenario yaml") @PathParam("scenarioName") String scenarioName) throws VTPException {
420 String scenario = scenarioName.substring(0, scenarioName.indexOf("-registry"));
421 File scenarioDir = new File(VTP_YAML_STORE, scenario);
422 List<File> yamls = FileUtil.searchFiles(scenarioDir, CommonConstant.YAML_SUFFIX);
423 if (!CollectionUtils.isEmpty(yamls)) {
424 if (LOG.isInfoEnabled()) {
425 LOG.error("The scenario yaml {} has sub testcase yamls, delete failed", loggerPatternBreaking(scenarioName));
427 LOG.error("The scenario yaml {} has sub testcase yamls, delete failed", scenarioName);
428 throw new VTPException(
429 new VTPError().setMessage(MessageFormat.format("The scenario yaml {0} has sub testcase yamls, delete failed !!!", scenarioName))
430 .setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR_500));
434 FileUtils.deleteQuietly(new File(VTP_YAML_STORE, scenarioName));
435 FileUtils.deleteDirectory(scenarioDir);
436 } catch (IOException e) {
437 LOG.error("Delete scenario yaml {} failed", scenarioName, e);
438 throw new VTPException(
439 new VTPError().setMessage("Delete yaml failed !!!").setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR_500));
441 return Response.ok("Delete yaml success", MediaType.APPLICATION_JSON).build();
446 @ApiOperation(tags = "VTP Scenario", value = "Create test case")
447 @Consumes(MediaType.MULTIPART_FORM_DATA)
448 @Produces(MediaType.APPLICATION_JSON)
449 @ApiResponses(value = {
450 @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Failed to perform the operation", response = VTPError.class)})
451 public Response storageTestcases(@ApiParam(value = "file form data body parts", required = true)
452 @FormDataParam("files") List<FormDataBodyPart> bodyParts) throws VTPException {
453 bodyParts.forEach(bodyPart -> {
454 BodyPartEntity entity = (BodyPartEntity) bodyPart.getEntity();
455 String fileName = bodyPart.getContentDisposition().getFileName();
456 if (ToolUtil.isYamlFile(new File(fileName))) {
457 // 1、store the testcase yaml file
458 Map<String, Object> result = snakeYaml().load(entity.getInputStream());
459 Map<String, Object> info = (Map<String, Object>) result.get("info");
461 File yamlFile = new File(VTP_YAML_STORE, info.get(PRODUCT) + File.separator + info.get(SERVICE) + File.separator + fileName);
463 FileUtils.deleteQuietly(yamlFile);
464 FileUtils.copyInputStreamToFile(entity.getInputStream(), yamlFile);
465 } catch (IOException e) {
466 LOG.error("Save testcase yaml {} failed", yamlFile.getName(), e);
469 // 2、store the testcase script file
470 File scriptFile = new File(VTP_SCRIPT_STORE, fileName);
472 FileUtils.deleteQuietly(scriptFile);
473 FileUtils.copyInputStreamToFile(entity.getInputStream(), scriptFile);
474 } catch (IOException e) {
475 LOG.error("Save testcase script {} failed", scriptFile.getName(), e);
479 return Response.ok("Save success", MediaType.APPLICATION_JSON).build();