blob: f6a257cfe3120ade817b6af06303e5c9ac7091cc [file] [log] [blame]
/*********************************************************************************
* Copyright (c) 2020 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.cloud.manager;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.eclipse.app4mc.cloud.manager.storage.StorageException;
import org.eclipse.app4mc.cloud.manager.storage.StorageFileNotFoundException;
import org.eclipse.app4mc.cloud.manager.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
@Controller
@SessionAttributes("workflowStatus")
public class WorkflowController {
private final StorageService storageService;
// TODO make url configurable
private String CONVERTER_SERVICE_BASE_URL = "http://localhost:8080/app4mc/converter/";
private String VALIDATION_SERVICE_BASE_URL = "http://localhost:8181/app4mc/validation/";
@Autowired
public WorkflowController(StorageService storageService) {
this.storageService = storageService;
}
@GetMapping("/workflow")
public String workflow(Model model) {
model.addAttribute("allProfiles", Unirest.get(VALIDATION_SERVICE_BASE_URL + "profiles").asJson().getBody().getArray().toList());
// render the form view
return "workflow";
}
@PostMapping("/workflow")
public String handleFileUpload(
@RequestParam("file") MultipartFile file,
@RequestParam("services") String[] services,
@RequestParam("profiles") String[] validationProfiles,
Model model,
@ModelAttribute WorkflowStatus ws) {
if (ws == null) {
ws = new WorkflowStatus();
} else {
ws.clear();
}
final WorkflowStatus workflowStatus = ws;
if (file.isEmpty()) {
workflowStatus.addMessage("Select a file to upload");
return "redirect:/workflow";
}
// upload the input file
String uuid = storageService.store(file);
workflowStatus.setUuid(uuid);
workflowStatus.addMessage(file.getOriginalFilename() + " successfully uploaded!");
model.addAttribute("workflowStatus", workflowStatus);
// TODO add proper error handling
Path uploaded = storageService.load(uuid, file.getOriginalFilename());
try {
Path inputFile = uploaded;
if (Arrays.stream(services).anyMatch(s -> "Migration".equals(s))) {
inputFile = executeConversion(workflowStatus, inputFile, file.getOriginalFilename());
}
if (Arrays.stream(services).anyMatch(s -> "Validation".equals(s))) {
executeValidation(workflowStatus, inputFile, file.getOriginalFilename(), validationProfiles);
}
} catch (ProcessingFailedException e) {
workflowStatus.addError(e.getMessage());
}
return "redirect:/workflow";
}
@GetMapping("/{uuid}/files/{servicepath}/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(
@PathVariable String uuid,
@PathVariable String servicepath,
@PathVariable String filename) {
Resource file = storageService.loadAsResource(uuid, servicepath, filename);
return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN).body(file);
// return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
// "attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
@GetMapping("/{uuid}/delete")
public String delete(
@PathVariable String uuid,
@ModelAttribute WorkflowStatus workflowStatus) {
storageService.delete(uuid);
workflowStatus.clear();
return "redirect:/workflow";
}
private Path executeConversion(WorkflowStatus workflowStatus, Path inputFile, String originalFilename) {
try {
// upload to converter service
String converterUuid = Unirest.post(CONVERTER_SERVICE_BASE_URL + "upload")
.field("file", Files.newInputStream(inputFile), originalFilename)
.asString()
.getBody();
workflowStatus.addMessage("Upload to migration service succeeded");
// trigger conversion
Unirest.put(CONVERTER_SERVICE_BASE_URL + converterUuid + "/convert").asEmpty();
workflowStatus.addMessage("Migration done");
// download file
Path migrationSubDir = storageService.load(workflowStatus.getUuid(), "_migration");
Files.createDirectories(migrationSubDir);
Path migrationResult = Unirest.get(CONVERTER_SERVICE_BASE_URL + converterUuid + "/download")
.asFile(migrationSubDir.resolve(originalFilename).toString())
.getBody()
.toPath();
workflowStatus.addMessage("Migrated file downloaded");
workflowStatus.addResult(
"Migration Result",
MvcUriComponentsBuilder.fromMethodName(
WorkflowController.class,
"serveFile",
workflowStatus.getUuid(),
"_migration",
migrationResult.getFileName().toString()).build().toUri().toString());
// delete converter upload again
Unirest.delete(CONVERTER_SERVICE_BASE_URL + converterUuid + "/delete").asEmpty();
workflowStatus.addMessage("Migration service cleaned up");
return migrationResult;
} catch (StorageException | IOException e) {
throw new ProcessingFailedException("Error on file operations in converter workflow", e);
}
}
private void executeValidation(WorkflowStatus workflowStatus, Path inputFile, String originalFilename, String[] validationProfiles) {
try {
// upload to validation service
String validationUuid = Unirest.post(VALIDATION_SERVICE_BASE_URL + "upload")
.field("file", Files.newInputStream(inputFile), originalFilename)
.field("profiles", Arrays.asList(validationProfiles))
.asString()
.getBody();
workflowStatus.addMessage("Upload to validation service succeeded");
// trigger conversion
HttpResponse<?> httpResponse = Unirest.put(VALIDATION_SERVICE_BASE_URL + validationUuid + "/validate").asEmpty();
// download file
Path validationSubDir = storageService.load(workflowStatus.getUuid(), "_validation");
Files.createDirectories(validationSubDir);
Path validationResult = Unirest.get(VALIDATION_SERVICE_BASE_URL + validationUuid + "/download")
.asFile(validationSubDir.resolve("validation_result.txt").toString())
.getBody()
.toPath();
workflowStatus.addMessage("Validation result downloaded");
workflowStatus.addResult(
"Validation Result",
MvcUriComponentsBuilder.fromMethodName(
WorkflowController.class,
"serveFile",
workflowStatus.getUuid(),
"_validation",
validationResult.getFileName().toString()).build().toUri().toString());
// delete validation upload again
Unirest.delete(VALIDATION_SERVICE_BASE_URL + validationUuid + "/delete").asEmpty();
workflowStatus.addMessage("Validation service cleaned up");
if (httpResponse.getStatus() == 200) {
workflowStatus.addMessage("Validation successfull");
} else {
throw new ProcessingFailedException("Validation failed with errors");
}
} catch (StorageException | IOException e) {
throw new ProcessingFailedException("Error on file operations in validation workflow", e);
}
}
@ExceptionHandler(StorageFileNotFoundException.class)
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
return ResponseEntity.notFound().build();
}
@ModelAttribute("workflowStatus")
public WorkflowStatus workflowStatus() {
return new WorkflowStatus();
}
}