| /********************************************************************************* |
| * 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(); |
| } |
| } |