diff --git a/.gitignore b/.gitignore index bb28673b34bc3a64e034c30c37668ad3ba342825..27d6ff1faa1f0120c4d5adca9b748f381149feb2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ project/ /.project /.settings/ dependency-reduced-pom.xml +logs/ diff --git a/docker/config/application.tmpl b/docker/config/application.tmpl index e34a00c0c2d4b693fc1a4ff26d3f709b43223299..6174fb409d967a2c67b5013ecc435a72fd763929 100644 --- a/docker/config/application.tmpl +++ b/docker/config/application.tmpl @@ -78,15 +78,15 @@ endpoints: services: exareme: - miningExaremeUrl: {{ default .Env.EXAREME_URL "http://localhost:9090" }}/mining/query + queryExaremeUrl: {{ default .Env.EXAREME_URL "http://localhost:9090" }}/mining/query algorithmsUrl: {{ default .Env.EXAREME_URL "http://localhost:9090" }}/mining/algorithms.json - workflows: - workflowUrl: {{ default .Env.WORKFLOW_URL "http://localhost:9090" }} - jwtSecret: {{ default .Env.JWT_SECRET "secret" }} - workflowAuthorization: {{ default .Env.WORKFLOW_AUTHORIZATION "undefined" }} + galaxy: + galaxyUrl: {{ default .Env.GALAXY_URL "http://localhost:8090/" }} + galaxyContext: {{ default .Env.GALAXY_CONTEXT "nativeGalaxy/workflows/list" }} + galaxyApiKey: {{ .Env.GALAXY_API_KEY }} galaxyUsername: {{ default .Env.GALAXY_USERNAME "admin" }} galaxyPassword: {{ default .Env.GALAXY_PASSWORD "password" }} - galaxyContext: {{ default .Env.GALAXY_CONTEXT "nativeGalaxy" }} + keycloak: keycloakUrl: {{ .Env.KEYCLOAK_URL }} diff --git a/pom.xml b/pom.xml index 674ec19543bfb73623cdf88324f209ae513aa0c3..fb47d331018bbd301afd6350b5136c2f5f2020e5 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ <swagger2markup-maven-plugin.version>1.0.0</swagger2markup-maven-plugin.version> <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version> <hibernate4-maven-plugin.version>1.1.1</hibernate4-maven-plugin.version> - <flyway-core.version>4.0.3</flyway-core.version> + <flyway-core.version>4.2.0</flyway-core.version> <hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version> <hibernate.version>4.3.11.Final</hibernate.version> <spring-data-jpa.version>1.10.11.RELEASE</spring-data-jpa.version> @@ -218,6 +218,34 @@ <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency> + + <!-- https://mvnrepository.com/artifact/com.github.jmchilton.blend4j/blend4j --> + <dependency> + <groupId>com.github.jmchilton.blend4j</groupId> + <artifactId>blend4j</artifactId> + <version>0.2.0</version> + </dependency> + + <dependency> + <groupId>com.squareup.retrofit2</groupId> + <artifactId>retrofit</artifactId> + <version>2.5.0</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson --> + <dependency> + <groupId>com.squareup.retrofit2</groupId> + <artifactId>converter-gson</artifactId> + <version>2.4.0</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/logging-interceptor --> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>logging-interceptor</artifactId> + <version>3.12.1</version> + </dependency> + <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> diff --git a/src/main/java/eu/hbp/mip/MIPApplication.java b/src/main/java/eu/hbp/mip/MIPApplication.java index befb16ec91ee35a0bbd2c9a7631f30be9cde4b21..e6ccd8e1e2d1e71107a9d262b21e02bb070eefae 100644 --- a/src/main/java/eu/hbp/mip/MIPApplication.java +++ b/src/main/java/eu/hbp/mip/MIPApplication.java @@ -9,7 +9,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; @SpringBootApplication public class MIPApplication { diff --git a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java index 5c03ec4ebf5a95f424e4214de582c7967ce14d93..6b973dc6575c2027a344b0a872591f1b465e9a87 100644 --- a/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java +++ b/src/main/java/eu/hbp/mip/configuration/PersistenceConfiguration.java @@ -46,5 +46,4 @@ public class PersistenceConfiguration { flyway.setDataSource(portalDataSource()); return flyway; } - } diff --git a/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java new file mode 100644 index 0000000000000000000000000000000000000000..efe69c07207cd1b247b20cc9a953a37cb8bea7b5 --- /dev/null +++ b/src/main/java/eu/hbp/mip/controllers/AlgorithmsApi.java @@ -0,0 +1,157 @@ +package eu.hbp.mip.controllers; + +import com.github.jmchilton.blend4j.galaxy.GalaxyInstance; +import com.github.jmchilton.blend4j.galaxy.GalaxyInstanceFactory; +import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; +import com.github.jmchilton.blend4j.galaxy.beans.Workflow; +import com.google.gson.Gson; +import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; +import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; +import eu.hbp.mip.model.AlgorithmDTO; +import eu.hbp.mip.model.galaxy.WorkflowDTO; +import eu.hbp.mip.utils.HTTPUtil; +import eu.hbp.mip.utils.UserActionLogging; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import retrofit2.Call; +import retrofit2.Response; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@RestController +@RequestMapping(value = "/algorithms", produces = {APPLICATION_JSON_VALUE}) +@Api(value = "/algorithms") +public class AlgorithmsApi { + + private static final Gson gson = new Gson(); + + @Value("#{'${services.exareme.algorithmsUrl}'}") + private String exaremeAlgorithmsUrl; + + @Value("#{'${services.galaxy.galaxyUrl}'}") + private String galaxyUrl; + + @Value("#{'${services.galaxy.galaxyApiKey}'}") + private String galaxyApiKey; + + @ApiOperation(value = "List all algorithms", response = String.class) + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity<List<AlgorithmDTO>> getAlgorithms() { + UserActionLogging.LogAction("List all algorithms", ""); + + List<AlgorithmDTO> exaremeAlgorithms = getExaremeAlgorithms(); + List<AlgorithmDTO> galaxyAlgorithms = getGalaxyWorkflows(); + + List<AlgorithmDTO> algorithms = new LinkedList<>(); + if (exaremeAlgorithms != null) { + algorithms.addAll(exaremeAlgorithms); + } else { + UserActionLogging.LogAction("List all algorithms", + "Getting exareme algorithms failed and returned null"); + } + if (galaxyAlgorithms != null) { + algorithms.addAll(galaxyAlgorithms); + } else { + UserActionLogging.LogAction("List all algorithms", + "Getting galaxy workflows failed and returned null"); + } + + return ResponseEntity.ok(algorithms); + } + + /** + * This method gets all the available exareme algorithms and + * + * @return a list of AlgorithmDTOs or null if something fails + */ + public List<AlgorithmDTO> getExaremeAlgorithms() { + UserActionLogging.LogAction("List exareme algorithms", ""); + + List<AlgorithmDTO> algorithms = new LinkedList<>(); + // Get exareme algorithms + try { + StringBuilder response = new StringBuilder(); + HTTPUtil.sendGet(exaremeAlgorithmsUrl, response); + + algorithms = gson.fromJson(response.toString(), algorithms.getClass()); + } catch (IOException e) { + UserActionLogging.LogAction("List exareme algorithms", "An exception occurred: " + e.getMessage()); + return null; + } + + UserActionLogging.LogAction("List exareme algorithms", + "Completed, returned " + algorithms.size() + " algorithms."); + return algorithms; + } + + /** + * This method gets all the available galaxy workflows, converts them into algorithms and + * + * @return a list of AlgorithmDTOs or null if something fails + */ + public List<AlgorithmDTO> getGalaxyWorkflows() { + UserActionLogging.LogAction("List Galaxy workflows", ""); + + List<Workflow> workflowList = null; + try { + // Get all the workflows with the galaxy client + final GalaxyInstance instance = GalaxyInstanceFactory.get(galaxyUrl, galaxyApiKey); + final WorkflowsClient workflowsClient = instance.getWorkflowsClient(); + + workflowList = new ArrayList<>(workflowsClient.getWorkflows()); + } catch (Exception e) { + UserActionLogging.LogAction("List Galaxy workflows", "Error when calling list galaxy workflows: " + e.getMessage()); + + return null; + } + + // Get the workflow details with the custom client to receive them as a WorkflowDTO + List<WorkflowDTO> workflows = new LinkedList<>(); + // Create the request client + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + for (Workflow workflow : workflowList) { + // Call Galaxy to run the workflow + Call<WorkflowDTO> call = service.getWorkflowFromGalaxy(workflow.getId(), galaxyApiKey); + try { + Response<WorkflowDTO> response = call.execute(); + + if (response.code() == 200) { // Call succeeded + workflows.add(response.body()); + + } else { // Something unexpected happened + String msgErr = gson.toJson(response.errorBody()); + UserActionLogging.LogAction("List Galaxy workflows", "Error Response: " + msgErr); + return null; + } + } catch (Exception e) { + UserActionLogging.LogAction("List Galaxy workflows", "An exception occurred: " + e.getMessage()); + return null; + } + } + UserActionLogging.LogAction("List Galaxy workflows", "Workflows fetched: " + workflows.size()); + + // Convert the workflows to algorithms + List<AlgorithmDTO> algorithms = new LinkedList<>(); + for (WorkflowDTO workflow : workflows) { + UserActionLogging.LogAction("List Galaxy workflows", "Converting workflow: " + workflow); + + algorithms.add(workflow.convertToAlgorithmDTO()); + + UserActionLogging.LogAction("List Galaxy workflows", + "Converted algorithm: " + algorithms.get(algorithms.size() - 1)); + } + + UserActionLogging.LogAction("List Galaxy workflows", "Completed!"); + return algorithms; + } +} diff --git a/src/main/java/eu/hbp/mip/controllers/DatasetsApi.java b/src/main/java/eu/hbp/mip/controllers/DatasetsApi.java deleted file mode 100644 index 897828faff819f71c108bbde751598b6b494776f..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/controllers/DatasetsApi.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Created by mirco on 04.12.15. - */ - -package eu.hbp.mip.controllers; - -import io.swagger.annotations.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - - -@RestController -@RequestMapping(value = "/datasets", produces = {APPLICATION_JSON_VALUE}) -@Api(value = "/datasets", description = "the datasets API") -public class DatasetsApi { - - private static final Logger LOGGER = LoggerFactory.getLogger(DatasetsApi.class); - - -} diff --git a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java index deef20f755c0fd961e09a238eb662a671a9532e2..f876e315ed224bfc667b6ff6fb9337a2bafb361f 100644 --- a/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java +++ b/src/main/java/eu/hbp/mip/controllers/ExperimentApi.java @@ -1,48 +1,60 @@ package eu.hbp.mip.controllers; -import com.google.gson.*; +import com.github.jmchilton.blend4j.galaxy.GalaxyInstance; +import com.github.jmchilton.blend4j.galaxy.GalaxyInstanceFactory; +import com.github.jmchilton.blend4j.galaxy.WorkflowsClient; +import com.github.jmchilton.blend4j.galaxy.beans.Workflow; +import com.github.jmchilton.blend4j.galaxy.beans.WorkflowDetails; +import com.github.jmchilton.blend4j.galaxy.beans.WorkflowInputDefinition; import com.google.common.collect.Lists; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - +import com.google.gson.*; +import eu.hbp.mip.controllers.galaxy.retrofit.RetroFitGalaxyClients; +import eu.hbp.mip.controllers.galaxy.retrofit.RetrofitClientInstance; import eu.hbp.mip.model.*; +import eu.hbp.mip.model.ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO; +import eu.hbp.mip.model.galaxy.ErrorResponse; +import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; +import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; import eu.hbp.mip.repositories.ExperimentRepository; import eu.hbp.mip.repositories.ModelRepository; import eu.hbp.mip.utils.HTTPUtil; +import eu.hbp.mip.utils.UserActionLogging; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import eu.hbp.mip.utils.JWTUtil; -import eu.hbp.mip.utils.UserActionLogging; +import retrofit2.Call; +import retrofit2.Response; import java.io.IOException; import java.util.*; + +import static java.lang.Thread.sleep; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -/** - * Created by habfast on 21/04/16. +/* + This api is creating experiments and running it's algorithm on the + exareme or galaxy clients. */ + @RestController -@RequestMapping(value = "/experiments", produces = { APPLICATION_JSON_VALUE }) -@Api(value = "/experiments", description = "the experiments API") +@RequestMapping(value = "/experiments", produces = {APPLICATION_JSON_VALUE}) +@Api(value = "/experiments") public class ExperimentApi { - //private static final Logger LOGGER = LoggerFactory.getLogger(ExperimentApi.class); - private static final Gson gson = new Gson(); private static final Gson gsonOnlyExposed = new GsonBuilder().serializeNulls() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").excludeFieldsWithoutExposeAnnotation().create(); - @Value("#{'${services.exareme.miningExaremeUrl:http://localhost:9090/mining/query}'}") - public String miningExaremeQueryUrl; + @Value("#{'${services.exareme.queryExaremeUrl}'}") + private String queryExaremeUrl; @Value("#{'${services.workflows.workflowUrl}'}") private String workflowUrl; @@ -50,6 +62,12 @@ public class ExperimentApi { @Value("#{'${services.workflows.jwtSecret}'}") private String jwtSecret; + @Value("#{'${services.galaxy.galaxyUrl}'}") + private String galaxyUrl; + + @Value("#{'${services.galaxy.galaxyApiKey}'}") + private String galaxyApiKey; + @Autowired private UserInfo userInfo; @@ -59,94 +77,6 @@ public class ExperimentApi { @Autowired private ExperimentRepository experimentRepository; - @ApiOperation(value = "Create an experiment on Exareme", response = Experiment.class) - @RequestMapping(value = "/exareme", method = RequestMethod.POST) - public ResponseEntity<String> runExaremeExperiment(@RequestBody ExperimentQuery expQuery) { - //LOGGER.info("send ExaremeExperiment"); - - Experiment experiment = saveExperiment(expQuery); - - String algoCode = expQuery.getAlgorithms().get(0).getCode(); - List<AlgorithmParam> params = expQuery.getAlgorithms().get(0).getParameters(); - new Thread(() -> { - List<HashMap<String, String>> queryList = new ArrayList<HashMap<String, String>>(); - - if (params != null) { - for (AlgorithmParam p : params) { - queryList.add(makeObject(p.getName(), p.getValue())); - } - } - - String query = gson.toJson(queryList); - String url = miningExaremeQueryUrl + "/" + algoCode; - - // Results are stored in the experiment object - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, query, results); - experiment.setResult("[" + results.toString() + "]"); - experiment.setHasError(code >= 400); - experiment.setHasServerError(code >= 500); - } catch (IOException e) { - //LOGGER.trace("Invalid UUID", e); - //LOGGER.warn("Exareme experiment failed to run properly !"); - experiment.setHasError(true); - experiment.setHasServerError(true); - experiment.setResult(e.getMessage()); - } - finishExperiment(experiment); - }).start(); - - UserActionLogging.LogAction("create ExaremeExperiment", "no info"); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - - @ApiOperation(value = "Create a workflow", response = Experiment.class) - @RequestMapping(value = "/workflow", method = RequestMethod.POST) - public ResponseEntity<String> runWorkflow(@RequestBody ExperimentQuery expQuery) { - - Experiment experiment = saveExperiment(expQuery); - - String algoCode = expQuery.getAlgorithms().get(0).getCode(); - List<AlgorithmParam> params = expQuery.getAlgorithms().get(0).getParameters(); - - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - - HashMap<String, String> queryMap = new HashMap<String, String>(); - - if (params != null) { - for (AlgorithmParam p : params) { - queryMap.put(p.getName(), p.getValue()); - } - } - - String query = gson.toJson(queryMap); - String url = workflowUrl + "/runWorkflow/" + algoCode; - // Results are stored in the experiment object - - new Thread(() -> { - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendAuthorizedHTTP(url, query, results, "POST", "Bearer " + token); - experiment.setResult("[" + results.toString() + "]"); - experiment.setHasError(code >= 400); - experiment.setHasServerError(code >= 500); - } catch (IOException e) { - //LOGGER.trace("Invalid UUID", e); - experiment.setHasError(true); - experiment.setHasServerError(true); - experiment.setResult(e.getMessage()); - } - finishExperiment(experiment); - }).start(); - - UserActionLogging.LogAction("create workflow", "no info"); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - @ApiOperation(value = "get an experiment", response = Experiment.class) @RequestMapping(value = "/{uuid}", method = RequestMethod.GET) public ResponseEntity<String> getExperiment( @@ -157,8 +87,7 @@ public class ExperimentApi { try { experimentUuid = UUID.fromString(uuid); } catch (IllegalArgumentException iae) { - //LOGGER.trace("Invalid UUID", iae); - //LOGGER.warn("An invalid Experiment UUID was received ! " + uuid); + UserActionLogging.LogAction("Get Experiment", "Invalid Experiment UUID."); return ResponseEntity.badRequest().body("Invalid Experiment UUID"); } @@ -167,95 +96,24 @@ public class ExperimentApi { if (experiment == null) { return new ResponseEntity<>("Not found", HttpStatus.NOT_FOUND); } - - UserActionLogging.LogAction("Get an experiment ", " uuid : "+ uuid); - - return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); - } - - @ApiOperation(value = "get workflow status", response = String.class) - @RequestMapping(value = "/workflow/status/{historyId}", method = RequestMethod.GET) - public ResponseEntity<String> getWorkflowStatus( - @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId) { - - UserActionLogging.LogAction("Get a workflow status", " historyId : "+ historyId); - - String url = workflowUrl + "/getWorkflowStatus/" + historyId; - try { - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - StringBuilder response = new StringBuilder(); - HTTPUtil.sendAuthorizedHTTP(url, "", response, "GET", "Bearer " + token); - JsonElement element = new JsonParser().parse(response.toString()); - - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); - } - - } - - // TODO: factorize workflow results - @ApiOperation(value = "get workflow results", response = String.class) - @RequestMapping(value = "/workflow/results/{historyId}", method = RequestMethod.GET) - public ResponseEntity<String> getWorkflowResults( - @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId) { - UserActionLogging.LogAction("Get workflow results", " historyId : "+ historyId); - - String url = workflowUrl + "/getWorkflowResults/" + historyId; - try { - StringBuilder response = new StringBuilder(); - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - HTTPUtil.sendAuthorizedHTTP(url, "", response, "GET", "Bearer " + token); - JsonElement element = new JsonParser().parse(response.toString()); - - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); - } - } - @ApiOperation(value = "get workflow result body", response = String.class) - @RequestMapping(value = "/workflow/resultsbody/{historyId}/content/{resultId}", method = RequestMethod.GET) - public ResponseEntity<String> getWorkflowResultBody( - @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId, - @ApiParam(value = "resultId", required = true) @PathVariable("resultId") String resultId) { - - UserActionLogging.LogAction("Get workflow result content", " historyId : "+ historyId + " resultId : "+ resultId); - - String url = workflowUrl + "/getWorkflowResultsBody/" + historyId + "/contents/" + resultId; - try { - StringBuilder response = new StringBuilder(); - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - HTTPUtil.sendAuthorizedHTTP(url, "", response, "GET", "Bearer " + token); - JsonElement element = new JsonParser().parse(response.toString()); + UserActionLogging.LogAction("Get an experiment ", " uuid : " + uuid); - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); - } + return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } - @ApiOperation(value = "get workflow result details", response = String.class) - @RequestMapping(value = "/workflow/resultsdetails/{historyId}/content/{resultId}", method = RequestMethod.GET) - public ResponseEntity<String> getWorkflowResultsDetails( - @ApiParam(value = "historyId", required = true) @PathVariable("historyId") String historyId, - @ApiParam(value = "resultId", required = true) @PathVariable("resultId") String resultId) { - UserActionLogging.LogAction("Get workflow result details", " historyId : "+ historyId + " resultId : "+ resultId); + @ApiOperation(value = "Create an experiment", response = Experiment.class) + @RequestMapping(value = "/runAlgorithm", method = RequestMethod.POST) + public ResponseEntity<String> runExperiment(@RequestBody ExperimentExecutionDTO experimentExecutionDTO) { + UserActionLogging.LogAction("Run algorithm", "Running the algorithm..."); - String url = workflowUrl + "/getWorkflowResultsDetails/" + historyId + "/contents/" + resultId; - try { - StringBuilder response = new StringBuilder(); - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - HTTPUtil.sendAuthorizedHTTP(url, "", response, "GET", "Bearer " + token); - JsonElement element = new JsonParser().parse(response.toString()); + // Get the type of algorithm + String algorithmType = experimentExecutionDTO.getAlgorithms().get(0).getType(); - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); + if (algorithmType.equals("workflow")) { + return runGalaxyWorkflow(experimentExecutionDTO); + } else { + return runExaremeAlgorithm(experimentExecutionDTO); } } @@ -264,7 +122,7 @@ public class ExperimentApi { public ResponseEntity<String> markExperimentAsViewed( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as viewed", " uuid : "+ uuid); + UserActionLogging.LogAction("Mark an experiment as viewed", " uuid : " + uuid); Experiment experiment; UUID experimentUuid; @@ -283,7 +141,7 @@ public class ExperimentApi { experiment.setResultsViewed(true); experimentRepository.save(experiment); - UserActionLogging.LogAction("Experiment updated (marked as viewed)", " "); + UserActionLogging.LogAction("Experiment updated (marked as viewed)", " "); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } @@ -293,8 +151,8 @@ public class ExperimentApi { public ResponseEntity<String> markExperimentAsShared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as shared", " uuid : "+ uuid); - + UserActionLogging.LogAction("Mark an experiment as shared", " uuid : " + uuid); + return doMarkExperimentAsShared(uuid, true); } @@ -302,27 +160,27 @@ public class ExperimentApi { @RequestMapping(value = "/{uuid}/markAsUnshared", method = RequestMethod.GET) public ResponseEntity<String> markExperimentAsUnshared( @ApiParam(value = "uuid", required = true) @PathVariable("uuid") String uuid) { - UserActionLogging.LogAction("Mark an experiment as unshared", " uuid : "+ uuid); - + UserActionLogging.LogAction("Mark an experiment as unshared", " uuid : " + uuid); + return doMarkExperimentAsShared(uuid, false); } @ApiOperation(value = "list experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = { "maxResultCount" }) + @RequestMapping(method = RequestMethod.GET, params = {"maxResultCount"}) public ResponseEntity<String> listExperiments( @ApiParam(value = "maxResultCount") @RequestParam int maxResultCount) { - UserActionLogging.LogAction("List experiments", " maxResultCount : "+ maxResultCount); - + UserActionLogging.LogAction("List experiments", " maxResultCount : " + maxResultCount); + return doListExperiments(false, null); } @ApiOperation(value = "list experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = { "slug", "maxResultCount" }) + @RequestMapping(method = RequestMethod.GET, params = {"slug", "maxResultCount"}) public ResponseEntity<String> listExperiments(@ApiParam(value = "slug") @RequestParam("slug") String modelSlug, - @ApiParam(value = "maxResultCount") @RequestParam("maxResultCount") int maxResultCount) { + @ApiParam(value = "maxResultCount") @RequestParam("maxResultCount") int maxResultCount) { - UserActionLogging.LogAction("List experiments", " modelSlug : "+ modelSlug); + UserActionLogging.LogAction("List experiments", " modelSlug : " + modelSlug); if (maxResultCount <= 0 && (modelSlug == null || "".equals(modelSlug))) { return new ResponseEntity<>("You must provide at least a slug or a limit of result", @@ -333,9 +191,9 @@ public class ExperimentApi { } @ApiOperation(value = "list my experiments", response = Experiment.class, responseContainer = "List") - @RequestMapping(method = RequestMethod.GET, params = { "mine" }) + @RequestMapping(method = RequestMethod.GET, params = {"mine"}) public ResponseEntity<String> listMyExperiments(@ApiParam(value = "mine") @RequestParam("mine") boolean mine) { - UserActionLogging.LogAction("List my experiments", " mine : "+ mine); + UserActionLogging.LogAction("List my experiments", " mine : " + mine); return doListExperiments(true, null); } @@ -352,7 +210,7 @@ public class ExperimentApi { } if (modelSlug != null && !"".equals(modelSlug)) { - for (Iterator<Experiment> it = expList.iterator(); it.hasNext();) { + for (Iterator<Experiment> it = expList.iterator(); it.hasNext(); ) { Experiment e = it.next(); e.setResult(null); e.setAlgorithms(null); @@ -385,43 +243,527 @@ public class ExperimentApi { experiment.setShared(shared); experimentRepository.save(experiment); - - UserActionLogging.LogAction("Experiment updated (marked as shared)", ""); + + UserActionLogging.LogAction("Experiment updated (marked as shared)", ""); return new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } + /* ------------------------------- EXPERIMENT MODEL METHODS ----------------------------------------------------*/ + + public Experiment createExperiment(ExperimentExecutionDTO experimentExecutionDTO){ + User user = userInfo.getUser(); + + Experiment experiment = new Experiment(); + experiment.setUuid(UUID.randomUUID()); + experiment.setCreatedBy(user); + experiment.setAlgorithms(gson.toJson(experimentExecutionDTO.getAlgorithms())); + Model model = modelRepository.findOne(experimentExecutionDTO.getModel()); + experiment.setModel(model); + experiment.setName(experimentExecutionDTO.getName()); + experimentRepository.save(experiment); + + UserActionLogging.LogAction("Created an experiment", " id : " + experiment.getUuid()); + UserActionLogging.LogAction("Created an experiment", " algorithms : " + experiment.getAlgorithms()); + UserActionLogging.LogAction("Created an experiment", " model : " + experiment.getModel().getSlug()); + UserActionLogging.LogAction("Created an experiment", " name : " + experiment.getName()); + return experiment; + } + + private void saveExperiment(Experiment experiment) { + UserActionLogging.LogAction("Saved an experiment", " id : " + experiment.getUuid()); + UserActionLogging.LogAction("Saved an experiment", " algorithms : " + experiment.getAlgorithms()); + UserActionLogging.LogAction("Saved an experiment", " model : " + experiment.getModel().getSlug()); + UserActionLogging.LogAction("Saved an experiment", " name : " + experiment.getName()); + UserActionLogging.LogAction("Saved an experiment", " historyId : " + experiment.getWorkflowHistoryId()); + UserActionLogging.LogAction("Saved an experiment", " status : " + experiment.getWorkflowStatus()); + + experimentRepository.save(experiment); + + UserActionLogging.LogAction("Experiment saved", ""); + } + private void finishExperiment(Experiment experiment) { experiment.setFinished(new Date()); experimentRepository.save(experiment); - UserActionLogging.LogAction("Experiment updated (finished)",""); + UserActionLogging.LogThreadAction("Experiment finished!", ""); } - private HashMap<String, String> makeObject(String name, String value) { - HashMap<String, String> o = new HashMap<String, String>(); - o.put("name", name); - o.put("value", value); + /* -------------------------------------- EXAREME CALLS ---------------------------------------------------------*/ + + /** + * The runExaremeExperiment will POST the algorithm to the exareme client + * + * @param experimentExecutionDTO is the request with the experiment information + * @return the response to be returned + */ + public ResponseEntity<String> runExaremeAlgorithm(ExperimentExecutionDTO experimentExecutionDTO) { + UserActionLogging.LogAction("Run exareme algorithm", "Running the algorithm..."); + + Experiment experiment = createExperiment(experimentExecutionDTO); + + // Run the 1st algorithm from the list + String algorithmName = experimentExecutionDTO.getAlgorithms().get(0).getName(); + + // Get the parameters + List<ExperimentExecutionDTO.AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> algorithmParameters + = experimentExecutionDTO.getAlgorithms().get(0).getParameters(); + + String body = gson.toJson(algorithmParameters); + String url = queryExaremeUrl + "/" + algorithmName; + UserActionLogging.LogAction("Run exareme algorithm", "url: " + url + ", body: " + body); + + ResponseEntity<String> response = new ResponseEntity<>(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); + UserActionLogging.LogAction("Run exareme algorithm", + "Completed, returning: " + experiment.toString()); - return o; + UserActionLogging.LogAction("Run exareme algorithm", + "Starting exareme execution thread"); + new Thread(() -> { + // ATTENTION: Inside the Thread only LogThreadAction should be used, not LogAction! + UserActionLogging.LogThreadAction("Run exareme algorithm", + "Thread started!"); + + try { + UserActionLogging.LogThreadAction("Run exareme algorithm", + "Thread started!"); + StringBuilder results = new StringBuilder(); + int code = HTTPUtil.sendPost(url, body, results); + + UserActionLogging.LogThreadAction("Run exareme algorithm", + "Algorithm finished with code: " + code); + + // Results are stored in the experiment object + experiment.setResult("[" + results.toString() + "]"); + experiment.setHasError(code >= 400); + experiment.setHasServerError(code >= 500); + } catch (Exception e) { + UserActionLogging.LogThreadAction("Run exareme algorithm", + "There was an exception: " + e.getMessage()); + + experiment.setHasError(true); + experiment.setHasServerError(true); + experiment.setResult(e.getMessage()); + } + UserActionLogging.LogThreadAction("Run exareme algorithm", + "Finished the experiment: " + experiment.toString()); + finishExperiment(experiment); + + UserActionLogging.LogThreadAction("Run exareme algorithm", + "Finished!"); + }).start(); + + return response; } - private Experiment saveExperiment(ExperimentQuery expQuery) { + /* --------------------------------------- GALAXY CALLS ---------------------------------------------------------*/ - Experiment experiment = new Experiment(); - experiment.setUuid(UUID.randomUUID()); - User user = userInfo.getUser(); - experiment.setAlgorithms(gson.toJson(expQuery.getAlgorithms())); - experiment.setValidations(gson.toJson(expQuery.getValidations())); - experiment.setName(expQuery.getName()); - experiment.setCreatedBy(user); - experiment.setModel(modelRepository.findOne(expQuery.getModel())); - experimentRepository.save(experiment); + /** + * The runWorkflow will POST the algorithm to the galaxy client + * + * @param experimentExecutionDTO is the request with the experiment information + * @return the response to be returned + */ + public ResponseEntity<String> runGalaxyWorkflow(ExperimentExecutionDTO experimentExecutionDTO) { + UserActionLogging.LogAction("Run workflow", "Running a workflow..."); - UserActionLogging.LogAction("Saved an experiment", " id : "+experiment.getUuid()); + Experiment experiment = createExperiment(experimentExecutionDTO); - return experiment; + // Run the 1st algorithm from the list + String workflowId = experimentExecutionDTO.getAlgorithms().get(0).getName(); + + // Get the parameters + List<AlgorithmExecutionParamDTO> algorithmParameters + = experimentExecutionDTO.getAlgorithms().get(0).getParameters(); + + // Convert the parameters to workflow parameters + HashMap<String, String> algorithmParamsIncludingEmpty = new HashMap<>(); + if (algorithmParameters != null) { + for (AlgorithmExecutionParamDTO param : algorithmParameters) { + algorithmParamsIncludingEmpty.put(param.getName(), param.getValue()); + } + } + + // Get all the algorithm parameters because the frontend provides only the non-null + final GalaxyInstance instance = GalaxyInstanceFactory.get(galaxyUrl, galaxyApiKey); + final WorkflowsClient workflowsClient = instance.getWorkflowsClient(); + Workflow workflow = null; + for (Workflow curWorkflow : workflowsClient.getWorkflows()) { + if (curWorkflow.getId().equals(workflowId)) { + workflow = curWorkflow; + break; + } + } + if (workflow == null) { + UserActionLogging.LogAction("Run workflow", + "Could not find algorithm code: " + workflowId); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse("Could not find galaxy algorithm.").toString()); + } + final WorkflowDetails workflowDetails = workflowsClient.showWorkflow(workflow.getId()); + for (Map.Entry<String, WorkflowInputDefinition> workflowParameter : workflowDetails.getInputs().entrySet()) { + if (!(algorithmParamsIncludingEmpty.containsKey(workflowParameter.getValue().getUuid()))) { + algorithmParamsIncludingEmpty.put(workflowParameter.getValue().getUuid(), ""); + } + } + + // Create the body of the request + HashMap<String, HashMap<String, String>> requestBody = new HashMap<>(); + requestBody.put("inputs", algorithmParamsIncludingEmpty); + JsonObject requestBodyJson = new JsonParser().parse(gson.toJson(requestBody)).getAsJsonObject(); + + // Create the request client + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + UserActionLogging.LogAction("Run workflow", "Running Galaxy workflow with id: " + workflow.getId()); + + // Call Galaxy to run the workflow + Call<PostWorkflowToGalaxyDtoResponse> call = service.postWorkflowToGalaxy(workflow.getId(), galaxyApiKey, requestBodyJson); + try { + Response<PostWorkflowToGalaxyDtoResponse> response = call.execute(); + + if (response.code() == 200) { // Call succeeded + String responseBody = gson.toJson(response.body()); + UserActionLogging.LogAction("Run workflow", "Response: " + responseBody); + + String historyId = (String) new JSONObject(responseBody).get("history_id"); + experiment.setWorkflowHistoryId(historyId); + experiment.setWorkflowStatus("running"); + experiment.setHasError(false); + experiment.setHasServerError(response.code() >= 500); + + } else { // Something unexpected happened + String msgErr = gson.toJson(response.errorBody()); + UserActionLogging.LogAction("Run workflow", "Error Response: " + msgErr); + + // Values are read from streams. + JSONObject jObjectError = new JSONObject(msgErr); + String errMsg = jObjectError.get("err_msg").toString(); + + experiment.setResult("[" + new ErrorResponse(errMsg).toString() + "]"); + experiment.setHasError(response.code() >= 400); + experiment.setHasServerError(response.code() >= 500); + } + + } catch (Exception e) { + UserActionLogging.LogAction("Run workflow", "An exception occurred: " + e.getMessage()); + experiment.setHasError(true); + experiment.setHasServerError(true); + experiment.setResult(e.getMessage()); + } + saveExperiment(experiment); + + // Start the process of fetching the status + updateWorkflowExperiment(experiment); + + UserActionLogging.LogAction("Run workflow", "Run workflow completed!"); + + return new ResponseEntity(gsonOnlyExposed.toJson(experiment.jsonify()), HttpStatus.OK); } + + /** + * This method creates a thread that will fetch the workflow result when it is ready + * + * @param experiment will be used to fetch it's workflow status, it should have the workflowHistoryId initialized + * and the result should not already be fetched + * @return nothing, just updates the experiment + */ + public void updateWorkflowExperiment(Experiment experiment) { + + if (experiment == null) { + UserActionLogging.LogAction("Update workflow experiment", "The experiment does not exist."); + return; + } + + UserActionLogging.LogAction("Update workflow experiment", + " Experiment id : " + experiment.getUuid()); + + if (experiment.getWorkflowHistoryId() == null) { + UserActionLogging.LogAction("Update workflow experiment", "History Id does not exist."); + return; + } + + UserActionLogging.LogAction("Update workflow experiment", "Starting Thread..." ); + new Thread(() -> { + while(true) { + // ATTENTION: Inside the Thread only LogThreadAction should be used, not LogAction! + UserActionLogging.LogThreadAction("Update workflow experiment", "Thread is running..."); + + try { + sleep(2000); + } catch (InterruptedException e) { + UserActionLogging.LogThreadAction("Update workflow experiment", + "Sleep was disrupted: " + e.getMessage()); + } + + UserActionLogging.LogThreadAction("Update workflow experiment", + "Fetching status for experiment Id: " + experiment.getUuid()); + + String state = getWorkflowStatus(experiment.getWorkflowHistoryId()); + UserActionLogging.LogThreadAction("Update workflow experiment", "State is: " + state); + + switch (state) { + case "running": + // Do nothing, when the experiment is created the status is set to running + UserActionLogging.LogThreadAction("Update workflow experiment", + "Workflow is still running."); + break; + + case "completed": + // Get only the job result that is visible + List<GalaxyWorkflowResult> workflowJobsResults = getWorkflowResults(experiment.getWorkflowHistoryId()); + UserActionLogging.LogThreadAction("Update workflow experiment", + "Results are: " + workflowJobsResults.toString()); + + boolean resultFound = false; + for (GalaxyWorkflowResult jobResult : workflowJobsResults) { + if (jobResult.getVisible()) { + UserActionLogging.LogThreadAction("Update workflow experiment", + "Visible result are: " + jobResult.getId()); + + String result = getWorkflowResultBody(experiment.getWorkflowHistoryId(), jobResult.getId()); + + UserActionLogging.LogThreadAction("Update workflow experiment", "Result: " + result); + if (result == null) { + experiment.setHasError(true); + experiment.setHasServerError(true); + } + experiment.setResult("[" + result + "]"); + experiment.setWorkflowStatus("completed"); + resultFound = true; + } + } + + if (!resultFound) { // If there is no visible result + UserActionLogging.LogThreadAction("Update workflow experiment", "No visible result"); + experiment.setResult("[" + new ErrorResponse("The workflow has no visible result.").toString() + "]"); + experiment.setHasError(true); + experiment.setHasServerError(true); + } + + finishExperiment(experiment); + break; + + case "error": + // Get the job result that failed + workflowJobsResults = getWorkflowResults(experiment.getWorkflowHistoryId()); + UserActionLogging.LogThreadAction("Update workflow experiment", + "Error results are: " + workflowJobsResults.toString()); + + boolean failedJobFound = false; + for (GalaxyWorkflowResult jobResult : workflowJobsResults) { + if (jobResult.getState().equals("error")) { + UserActionLogging.LogThreadAction("Update workflow experiment", + "Failed job is: " + jobResult.getId()); + + String result = getWorkflowJobError(jobResult.getId()); + + UserActionLogging.LogThreadAction("Update workflow experiment", "Job result: " + result); + if (result == null) { + experiment.setHasError(true); + experiment.setHasServerError(true); + } + experiment.setResult("[" + result + "]"); + experiment.setWorkflowStatus("error"); + failedJobFound = true; + } + } + + if (!failedJobFound) { // If there is no visible failed job + UserActionLogging.LogThreadAction("Update workflow experiment", "No failed result"); + experiment.setResult("[" + new ErrorResponse("The workflow has no failed result.").toString() + "]"); + experiment.setHasError(true); + experiment.setHasServerError(true); + } + finishExperiment(experiment); + break; + + default: // InternalError or unexpected result + experiment.setResult("[" + new ErrorResponse("An unexpected error occurred.").toString() + "]"); + experiment.setHasError(true); + experiment.setHasServerError(true); + finishExperiment(experiment); + break; + } + + // If result exists return + if (experiment.getResult() != null) { + UserActionLogging.LogThreadAction("Update workflow experiment", + "Result exists: " + experiment.getResult()); + return; + } + } + }).start(); + } + + + /** + * @param historyId The historyId of the workflow + * @return "running" -> When the workflow is still running + * "internalError" -> When an exception or a bad request occurred + * "error" -> When the workflow produced an error + * "completed" -> When the workflow completed successfully + */ + public String getWorkflowStatus(String historyId) { + // ATTENTION: This function is used from a Thread. Only LogThreadAction should be used, not LogAction! + UserActionLogging.LogThreadAction("Get workflow status", " History Id : " + historyId); + + // Create the request client + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> call = service.getWorkflowStatusFromGalaxy(historyId, galaxyApiKey); + + String result = null; + try { + Response<Object> response = call.execute(); + if (response.code() >= 400) { + UserActionLogging.LogThreadAction("Get workflow status", " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return "internalError"; + } + result = new Gson().toJson(response.body()); + UserActionLogging.LogThreadAction("Get workflow status", " Result: " + result); + + } catch (IOException e) { + UserActionLogging.LogThreadAction("Get workflow status" + , " An exception happened: " + e.getMessage()); + return "internalError"; + } + + String state = null; + try { + JSONObject resultJson = new JSONObject(result); + state = resultJson.getString("state"); + } catch (JSONException e) { + UserActionLogging.LogThreadAction("Get workflow status" + , " An exception happened: " + e.getMessage()); + return "internalError"; + } + + UserActionLogging.LogThreadAction("Get workflow status", " Completed!"); + switch (state) { + case "ok": + return "completed"; + case "error": + return "error"; + case "running": + case "new": + case "waiting": + case "queued": + return "running"; + default: + return "internalError"; + } + } + + /** + * @param historyId The historyId of the workflow + * @return a List<GalaxyWorkflowResult> or null when an error occurred + */ + public List<GalaxyWorkflowResult> getWorkflowResults(String historyId) { + UserActionLogging.LogThreadAction("Get workflow results", " historyId : " + historyId); + + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<List<GalaxyWorkflowResult>> call = service.getWorkflowResultsFromGalaxy(historyId, galaxyApiKey); + + List<GalaxyWorkflowResult> getGalaxyWorkflowResultList = null; + try { + Response<List<GalaxyWorkflowResult>> response = call.execute(); + if (response.code() >= 400) { + UserActionLogging.LogThreadAction("Get workflow results", " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + getGalaxyWorkflowResultList = response.body(); + UserActionLogging.LogThreadAction("Get workflow results", " Result: " + response.body()); + + } catch (IOException e) { + UserActionLogging.LogThreadAction("Get workflow results" + , " An exception happened: " + e.getMessage()); + return null; + } + + UserActionLogging.LogThreadAction("Get workflow results", " Completed!"); + return getGalaxyWorkflowResultList; + + } + + /** + * @param historyId the historyId of the workflow + * @param contentId the id of the job result that we want + * @return the result of the specific workflow job, null if there was an error + */ + public String getWorkflowResultBody(String historyId, String contentId) { + UserActionLogging.LogThreadAction("Get workflow results Body", " historyId : " + historyId); + + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> call = + service.getWorkflowResultsBodyFromGalaxy(historyId, contentId, galaxyApiKey); + + String resultJson = null; + try { + Response<Object> response = call.execute(); + if (response.code() >= 400) { + UserActionLogging.LogThreadAction("Get workflow results Body", " Response code: " + + response.code() + "" + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + resultJson = new Gson().toJson(response.body()); + UserActionLogging.LogThreadAction("Get workflow results Body", " Result: " + resultJson); + + } catch (IOException e) { + UserActionLogging.LogThreadAction("Get workflow results Body", + " An exception happened: " + e.getMessage()); + return null; + } + + UserActionLogging.LogThreadAction("Get workflow results Body", " Completed!"); + return resultJson; + } + + + /** + * @param jobId the id of the workflow job that failed + * @return the error that was produced or null if an error occurred + */ + public String getWorkflowJobError(String jobId) { + UserActionLogging.LogThreadAction("Get workflow job error", " jobId : " + jobId); + + RetroFitGalaxyClients service = RetrofitClientInstance.getRetrofitInstance().create(RetroFitGalaxyClients.class); + Call<Object> callError = service.getErrorMessageOfWorkflowFromGalaxy(jobId, galaxyApiKey); + + String fullError = null; + String returnError = null; + try { + Response<Object> response = callError.execute(); + if (response.code() >= 400) { + UserActionLogging.LogThreadAction("Get workflow job error", "Response code: " + + response.code() + " with body: " + (response.errorBody() != null ? response.errorBody().string() : " ")); + return null; + } + + // Parsing the stderr of the job that failed + String jsonString = new Gson().toJson(response.body()); + JsonElement jsonElement = new JsonParser().parse(jsonString); + JsonObject rootObject = jsonElement.getAsJsonObject(); + fullError = rootObject.get("stderr").getAsString(); + UserActionLogging.LogThreadAction("Get workflow job error", "Error: " + fullError); + + String[] arrOfStr = fullError.split("ValueError", 0); + String specError = arrOfStr[arrOfStr.length - 1]; + returnError = specError.substring(1); + UserActionLogging.LogThreadAction("Get workflow job error", "Parsed Error: " + returnError); + + } catch (IOException e) { + UserActionLogging.LogThreadAction("Get workflow job error", "Exception: " + e.getMessage()); + return null; + } + + UserActionLogging.LogThreadAction("Get workflow job error", "Completed successfully!"); + + return returnError; + } + + } diff --git a/src/main/java/eu/hbp/mip/controllers/JWTApi.java b/src/main/java/eu/hbp/mip/controllers/JWTApi.java deleted file mode 100644 index a1cb6afd55431bb859170b52c9daa7007ca69c42..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/controllers/JWTApi.java +++ /dev/null @@ -1,40 +0,0 @@ -package eu.hbp.mip.controllers; - -import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.http.HttpStatus; -import eu.hbp.mip.model.User; -import eu.hbp.mip.model.UserInfo; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.*; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import eu.hbp.mip.utils.JWTUtil; -import eu.hbp.mip.utils.UserActionLogging; - -@RestController -@RequestMapping(value = "/jwt", produces = { TEXT_PLAIN_VALUE }) -@Api(value = "/jwt", description = "the jwt API") -public class JWTApi { - - @Autowired - private UserInfo userInfo; - - @Value("#{'${services.workflows.jwtSecret}'}") - private String jwtSecret; - - @ApiOperation(value = "Create a JSON Web Token", response = String.class) - @RequestMapping(method = RequestMethod.POST) - public ResponseEntity<String> createJWT() { - - UserActionLogging.LogAction("Create a JSON Web Token", ""); - - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - - return ResponseEntity.status(HttpStatus.CREATED).body(token); - } -} diff --git a/src/main/java/eu/hbp/mip/controllers/MethodsApi.java b/src/main/java/eu/hbp/mip/controllers/MethodsApi.java deleted file mode 100644 index f88f6795156f566b4b4e6e70288a5e4d535a67da..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/controllers/MethodsApi.java +++ /dev/null @@ -1,77 +0,0 @@ -package eu.hbp.mip.controllers; - -import com.google.gson.*; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import eu.hbp.mip.model.User; -import eu.hbp.mip.model.UserInfo; -import eu.hbp.mip.utils.HTTPUtil; -import org.springframework.beans.factory.annotation.Value; -import java.io.IOException; -import eu.hbp.mip.utils.JWTUtil; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import org.springframework.beans.factory.annotation.Autowired; -import eu.hbp.mip.utils.UserActionLogging; - -@RestController -@RequestMapping(value = "/methods", produces = { APPLICATION_JSON_VALUE }) -@Api(value = "/methods", description = "the methods API") -public class MethodsApi { - - - private static final Gson gson = new Gson(); - - @Value("#{'${services.exareme.algorithmsUrl:http://localhost:9090/mining/algorithms.json}'}") - private String exaremeAlgorithmsUrl; - - @Value("#{'${services.workflows.workflowUrl}'}") - private String workflowUrl; - - @Value("#{'${services.workflows.jwtSecret}'}") - private String jwtSecret; - - @Autowired - private UserInfo userInfo; - - @ApiOperation(value = "List Exareme algorithms and validations", response = String.class) - @RequestMapping(value = "/exareme", method = RequestMethod.GET) - public ResponseEntity<Object> getExaremeAlgorithms() { - UserActionLogging.LogAction("List Exareme algorithms and validations", ""); - - try { - StringBuilder response = new StringBuilder(); - HTTPUtil.sendGet(exaremeAlgorithmsUrl, response); - JsonElement element = new JsonParser().parse(response.toString()); - - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); - } - - } - - @ApiOperation(value = "List Galaxy workflows", response = String.class) - @RequestMapping(value = "/workflows", method = RequestMethod.GET) - public ResponseEntity<Object> getWorkflows() { - UserActionLogging.LogAction("List Galaxy workflows", ""); - - try { - User user = userInfo.getUser(); - String token = JWTUtil.getJWT(jwtSecret, user.getEmail()); - - StringBuilder response = new StringBuilder(); - HTTPUtil.sendAuthorizedHTTP(workflowUrl + "/getAllWorkflowWithDetails", "", response, "GET", "Bearer " + token); - JsonElement element = new JsonParser().parse(response.toString()); - - return ResponseEntity.ok(gson.toJson(element)); - } catch (IOException e) { - return ResponseEntity.status(500).body(e.getMessage()); - } - - } - -} diff --git a/src/main/java/eu/hbp/mip/controllers/MiningApi.java b/src/main/java/eu/hbp/mip/controllers/MiningApi.java index 13272fc7355ed509e5b030429409a98b83ac55e1..3adcc6269e8120147c6c81367c91b928c1d5d486 100644 --- a/src/main/java/eu/hbp/mip/controllers/MiningApi.java +++ b/src/main/java/eu/hbp/mip/controllers/MiningApi.java @@ -45,16 +45,16 @@ public class MiningApi { @Autowired private UserInfo userInfo; - @Value("#{'${services.exareme.miningExaremeUrl:http://localhost:9090/mining/query}'}") - public String miningExaremeQueryUrl; + @Value("#{'${services.exareme.queryExaremeUrl:http://localhost:9090/mining/query}'}") + public String queryExaremeUrl; - @ApiOperation(value = "Create an histogram on Exareme", response = String.class) - @RequestMapping(value = "/exareme", method = RequestMethod.POST) - public ResponseEntity runExaremeMining(@RequestBody List<HashMap<String, String>> queryList) { + @ApiOperation(value = "Create a histogram on Exareme", response = String.class) + @RequestMapping(value = "/histograms", method = RequestMethod.POST) + public ResponseEntity runExaremeHistograms(@RequestBody List<HashMap<String, String>> queryList) { UserActionLogging.LogAction("Run an histogram", ""); String query = gson.toJson(queryList); - String url = miningExaremeQueryUrl + "/" + "HISTOGRAMS"; + String url = queryExaremeUrl + "/" + "MULTIPLE_HISTOGRAMS"; try { StringBuilder results = new StringBuilder(); @@ -66,13 +66,13 @@ public class MiningApi { } } - @ApiOperation(value = "Create an descriptive statistic on Exareme", response = String.class) - @RequestMapping(value = "/exareme-stats", method = RequestMethod.POST) + @ApiOperation(value = "Create a descriptive statistic on Exareme", response = String.class) + @RequestMapping(value = "/descriptive_stats", method = RequestMethod.POST) public ResponseEntity runExaremeDescriptiveStats(@RequestBody List<HashMap<String, String>> queryList) { UserActionLogging.LogAction("Run descriptive stats", ""); String query = gson.toJson(queryList); - String url = miningExaremeQueryUrl + "/" + "DESCRIPTIVE_STATS"; + String url = queryExaremeUrl + "/" + "DESCRIPTIVE_STATS"; try { StringBuilder results = new StringBuilder(); @@ -84,24 +84,11 @@ public class MiningApi { } } - @ApiOperation(value = "Perform an non persisted algorithm on Exareme", response = String.class) - @RequestMapping(value = "/exareme/{algorithmName}", method = RequestMethod.POST) - public ResponseEntity runExaremeAlgorithm( - @RequestBody List<HashMap<String, String>> queryList, - @ApiParam(value = "algorithmName", required = true) @PathVariable("algorithmName") String algorithmName - ) { - UserActionLogging.LogAction("Run algo", ""); + @ApiOperation(value = "Check if a formula is valid", response = String.class) + @RequestMapping(value = "/checkFormula", method = RequestMethod.POST) + public ResponseEntity checkFormulaValidity(String formula) { + UserActionLogging.LogAction("Check Formula Validity", ""); - String query = gson.toJson(queryList); - String url = miningExaremeQueryUrl + "/" + algorithmName; - - try { - StringBuilder results = new StringBuilder(); - int code = HTTPUtil.sendPost(url, query, results); - - return ResponseEntity.ok(gson.toJson(results.toString())); - } catch (IOException e) { - return new ResponseEntity<>("Not found", HttpStatus.BAD_REQUEST); - } + return ResponseEntity.ok(""); } } diff --git a/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java new file mode 100644 index 0000000000000000000000000000000000000000..612dce6ca5b557f23deafdf873978c0f442eb1d9 --- /dev/null +++ b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetroFitGalaxyClients.java @@ -0,0 +1,31 @@ +package eu.hbp.mip.controllers.galaxy.retrofit; + +import com.google.gson.JsonObject; +import eu.hbp.mip.model.galaxy.GalaxyWorkflowResult; +import eu.hbp.mip.model.galaxy.PostWorkflowToGalaxyDtoResponse; +import eu.hbp.mip.model.galaxy.WorkflowDTO; +import retrofit2.Call; +import retrofit2.http.*; + +import java.util.List; + +public interface RetroFitGalaxyClients { + + @GET("workflows/{workflowId}") + Call<WorkflowDTO> getWorkflowFromGalaxy(@Path("workflowId") String workflowId, @Query("key") String key); + + @POST("workflows/{workflowId}/invocations") + Call<PostWorkflowToGalaxyDtoResponse> postWorkflowToGalaxy(@Path("workflowId") String workflowId, @Query("key") String key, @Body JsonObject body); + + @GET("histories/{historyId}") + Call<Object> getWorkflowStatusFromGalaxy(@Path("historyId") String historyId, @Query("key") String key); + + @GET("histories/{historyId}/contents") + Call<List<GalaxyWorkflowResult>> getWorkflowResultsFromGalaxy(@Path("historyId") String historyId, @Query("key") String key); + + @GET("histories/{historyId}/contents/{contentId}/display") + Call<Object> getWorkflowResultsBodyFromGalaxy(@Path("historyId") String historyId, @Path("contentId") String contentId, @Query("key") String key); + + @GET("jobs/{jobId}?full=true") + Call<Object> getErrorMessageOfWorkflowFromGalaxy(@Path("jobId") String jobId, @Query("key") String key); +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetrofitClientInstance.java b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetrofitClientInstance.java new file mode 100644 index 0000000000000000000000000000000000000000..6aa218902ebd724518d56d8cca8b0c5e77aa2893 --- /dev/null +++ b/src/main/java/eu/hbp/mip/controllers/galaxy/retrofit/RetrofitClientInstance.java @@ -0,0 +1,42 @@ +package eu.hbp.mip.controllers.galaxy.retrofit; + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +import javax.annotation.PostConstruct; + +@Component +public class RetrofitClientInstance { + + private static Retrofit retrofit; + + @Value("#{'${services.galaxy.galaxyUrl}'}") + private String galaxyUrl; + + private static String BASE_URL; + + @PostConstruct + public void init() { + BASE_URL = galaxyUrl + "/api/"; + } + + public static Retrofit getRetrofitInstance() { + if (retrofit == null) { + + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); + + retrofit = new retrofit2.Retrofit.Builder() + .baseUrl(BASE_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + } + return retrofit; + } +} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/model/Algorithm.java b/src/main/java/eu/hbp/mip/model/Algorithm.java deleted file mode 100644 index bd08093a82e0cc7823fdc6c33c193a3feff774ba..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/model/Algorithm.java +++ /dev/null @@ -1,32 +0,0 @@ -package eu.hbp.mip.model; - -import java.util.LinkedList; - -/** - * Created by mirco on 09.11.16. - */ - -public class Algorithm extends ExperimentValidator { - - private boolean validation; - - public Algorithm() { - - } - - public Algorithm(String code, String name, boolean validation) { - this.validation = validation; - setCode(code); - setName(name); - setParameters(new LinkedList<>()); - } - - public boolean isValidation() { - return validation; - } - - public void setValidation(boolean validation) { - this.validation = validation; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/AlgorithmDTO.java b/src/main/java/eu/hbp/mip/model/AlgorithmDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..ed0ef2a15606ef09f139bad65c90c3cf436c2474 --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/AlgorithmDTO.java @@ -0,0 +1,220 @@ +package eu.hbp.mip.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class AlgorithmDTO { + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public List<AlgorithmParamDTO> getParameters() { + return parameters; + } + + public void setParameters(List<AlgorithmParamDTO> parameters) { + this.parameters = parameters; + } + + @SerializedName("name") + private String name; + + @SerializedName("desc") + private String desc; + + @SerializedName("label") + private String label; + + @SerializedName("type") + private String type; + + @SerializedName("parameters") + private List<AlgorithmParamDTO> parameters; + + public static class AlgorithmParamDTO { + @SerializedName("name") + private String name; + + @SerializedName("desc") + private String desc; + + @SerializedName("label") + private String label; + + @SerializedName("type") + private String type; + + @SerializedName("columnValuesSQLType") + private String columnValuesSQLType; + + @SerializedName("columnValuesIsCategorical") + private String columnValuesIsCategorical; + + @SerializedName("value") + private String value; + + @SerializedName("defaultValue") + private String defaultValue; + + @SerializedName("valueType") + private String valueType; + + @SerializedName("valueNotBlank") + private String valueNotBlank; + + @SerializedName("valueMultiple") + private String valueMultiple; + + @SerializedName("valueMin") + private String valueMin; + + @SerializedName("valueMax") + private String valueMax; + + @SerializedName("valueEnumerations") + private List<String> valueEnumerations; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getColumnValuesSQLType() { + return columnValuesSQLType; + } + + public void setColumnValuesSQLType(String columnValuesSQLType) { + this.columnValuesSQLType = columnValuesSQLType; + } + + public String getColumnValuesIsCategorical() { + return columnValuesIsCategorical; + } + + public void setColumnValuesIsCategorical(String columnValuesIsCategorical) { + this.columnValuesIsCategorical = columnValuesIsCategorical; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getValueType() { + return valueType; + } + + public void setValueType(String valueType) { + this.valueType = valueType; + } + + public String getValueNotBlank() { + return valueNotBlank; + } + + public void setValueNotBlank(String valueNotBlank) { + this.valueNotBlank = valueNotBlank; + } + + public String getValueMultiple() { + return valueMultiple; + } + + public void setValueMultiple(String valueMultiple) { + this.valueMultiple = valueMultiple; + } + + public String getValueMin() { + return valueMin; + } + + public void setValueMin(String valueMin) { + this.valueMin = valueMin; + } + + public String getValueMax() { + return valueMax; + } + + public void setValueMax(String valueMax) { + this.valueMax = valueMax; + } + + public List<String> getValueEnumerations() { + return valueEnumerations; + } + + public void setValueEnumerations(List<String> valueEnumerations) { + this.valueEnumerations = valueEnumerations; + } + } + +} diff --git a/src/main/java/eu/hbp/mip/model/AlgorithmParam.java b/src/main/java/eu/hbp/mip/model/AlgorithmParam.java deleted file mode 100644 index 62c48ba13b4cffcf647d4da97a0509735f18cac4..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/model/AlgorithmParam.java +++ /dev/null @@ -1,36 +0,0 @@ -package eu.hbp.mip.model; - -/** - * Created by mirco on 09.11.16. - */ - -public class AlgorithmParam { - - private String code; - private String value; - - public String getCode() { - return code; - - } - - public void setName(String name) { - this.code = name; - } - - public String getName() { - return this.code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/src/main/java/eu/hbp/mip/model/Experiment.java b/src/main/java/eu/hbp/mip/model/Experiment.java index a15b575c9b76895d922d7c65b3c0ea3811501a7e..902cf37da1ef0abad1f2c8836fe0a7e2654f077f 100644 --- a/src/main/java/eu/hbp/mip/model/Experiment.java +++ b/src/main/java/eu/hbp/mip/model/Experiment.java @@ -16,8 +16,6 @@ import java.util.*; @Table(name = "`experiment`") public class Experiment { - private static final Logger LOGGER = LoggerFactory.getLogger(Experiment.class); - private static final Gson gson = new Gson(); @Id @@ -45,9 +43,16 @@ public class Experiment { private String algorithms; @Column(columnDefinition="TEXT") - @Expose private String validations; + @Column(columnDefinition="TEXT") + @Expose + private String workflowHistoryId; + + @Column(columnDefinition="TEXT") + @Expose + private String workflowStatus; + @Column(columnDefinition="TEXT") @Expose private String result; @@ -139,6 +144,22 @@ public class Experiment { this.hasError = hasError; } + public String getWorkflowHistoryId() { + return workflowHistoryId; + } + + public void setWorkflowHistoryId(String workflowHistoryId) { + this.workflowHistoryId = workflowHistoryId; + } + + public String getWorkflowStatus() { + return workflowStatus; + } + + public void setWorkflowStatus(String workflowStatus) { + this.workflowStatus = workflowStatus; + } + public String getResult() { return result; } diff --git a/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java b/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..a68cd9121d852503bc01eb6b2f0d23dd8af525dd --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/ExperimentExecutionDTO.java @@ -0,0 +1,106 @@ +package eu.hbp.mip.model; + +import java.util.List; + +public class ExperimentExecutionDTO { + + private String name; + private String model; + private List<AlgorithmExecutionDTO> algorithms; + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public List<AlgorithmExecutionDTO> getAlgorithms() { + return algorithms; + } + + public void setAlgorithms(List<AlgorithmExecutionDTO> algorithms) { + this.algorithms = algorithms; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public static class AlgorithmExecutionDTO { + + private String name; + private String label; + private String type; + + private List<AlgorithmExecutionParamDTO> parameters; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public List<AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> getParameters() { + return parameters; + } + + public void setParameters(List<AlgorithmExecutionDTO.AlgorithmExecutionParamDTO> parameters) { + this.parameters = parameters; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public static class AlgorithmExecutionParamDTO { + + private String name; + private String label; + private String value; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + } + } +} diff --git a/src/main/java/eu/hbp/mip/model/ExperimentQuery.java b/src/main/java/eu/hbp/mip/model/ExperimentQuery.java deleted file mode 100644 index 333751c438099de62942918e17f019505a800de7..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/model/ExperimentQuery.java +++ /dev/null @@ -1,46 +0,0 @@ -package eu.hbp.mip.model; - -import java.util.List; - -/** - * Created by mirco on 09.11.16. - */ -public class ExperimentQuery { - - private String name; - private String model; - private List<ExperimentValidator> validations; - private List<Algorithm> algorithms; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public List<ExperimentValidator> getValidations() { - return validations; - } - - public void setValidations(List<ExperimentValidator> validations) { - this.validations = validations; - } - - public List<Algorithm> getAlgorithms() { - return algorithms; - } - - public void setAlgorithms(List<Algorithm> algorithms) { - this.algorithms = algorithms; - } -} diff --git a/src/main/java/eu/hbp/mip/model/ExperimentValidator.java b/src/main/java/eu/hbp/mip/model/ExperimentValidator.java deleted file mode 100644 index 208fc90fa7272779bf51e09ca7fa0b6f409c66d3..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/model/ExperimentValidator.java +++ /dev/null @@ -1,40 +0,0 @@ -package eu.hbp.mip.model; - -import java.util.List; - -/** - * Created by mirco on 09.11.16. - */ - -public class ExperimentValidator { - - private String code; - private String name; - - private List<AlgorithmParam> parameters; - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List<AlgorithmParam> getParameters() { - return parameters; - } - - public void setParameters(List<AlgorithmParam> parameters) { - this.parameters = parameters; - } - -} diff --git a/src/main/java/eu/hbp/mip/model/MiningQuery.java b/src/main/java/eu/hbp/mip/model/MiningQuery.java index 6ced265037ba4148d4f18e6ba06d9434e4610317..171c55032eee4b3c5f63d636c137d6ae41bfe051 100644 --- a/src/main/java/eu/hbp/mip/model/MiningQuery.java +++ b/src/main/java/eu/hbp/mip/model/MiningQuery.java @@ -1,6 +1,7 @@ package eu.hbp.mip.model; import com.google.gson.Gson; + import java.util.LinkedList; import java.util.List; @@ -14,7 +15,7 @@ public class MiningQuery { private List<Variable> grouping; private List<Variable> datasets; private String filters; - private Algorithm algorithm; + private ExperimentExecutionDTO.AlgorithmExecutionDTO algorithm; public MiningQuery() { this.variables = new LinkedList<>(); @@ -32,7 +33,9 @@ public class MiningQuery { this.variables = variables; } - public void addVariable(Variable variable) { this.variables.add(variable); } + public void addVariable(Variable variable) { + this.variables.add(variable); + } public List<Variable> getCovariables() { return covariables; @@ -42,7 +45,9 @@ public class MiningQuery { this.covariables = covariables; } - public void addCovariable(Variable variable) { this.covariables.add(variable); } + public void addCovariable(Variable variable) { + this.covariables.add(variable); + } public List<Variable> getGrouping() { return grouping; @@ -52,15 +57,21 @@ public class MiningQuery { this.grouping = grouping; } - public List<Variable> getDatasets() { return datasets; } + public List<Variable> getDatasets() { + return datasets; + } public void setDataset(List<Variable> datasets) { this.datasets = datasets; } - public void addDataset(Variable variable) { this.datasets.add(variable); } + public void addDataset(Variable variable) { + this.datasets.add(variable); + } - public void addGrouping(Variable variable) { this.grouping.add(variable); } + public void addGrouping(Variable variable) { + this.grouping.add(variable); + } public String getFilters() { return filters; @@ -70,11 +81,11 @@ public class MiningQuery { this.filters = filters; } - public Algorithm getAlgorithm() { + public ExperimentExecutionDTO.AlgorithmExecutionDTO getAlgorithm() { return algorithm; } - public void setAlgorithm(Algorithm algorithm) { + public void setAlgorithm(ExperimentExecutionDTO.AlgorithmExecutionDTO algorithm) { this.algorithm = algorithm; } diff --git a/src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java b/src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..77a171358b54561e3492dcb317acd835aeaa645f --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/galaxy/ErrorResponse.java @@ -0,0 +1,50 @@ +/* + * Developed by Kechagias Konstantinos. + * Copyright (c) 2019. MIT License + */ + +package eu.hbp.mip.model.galaxy; + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +public class ErrorResponse { + + @SerializedName("result") + List<ErrorMessage> result; + + public ErrorResponse() { + } + + public ErrorResponse(String errMsg) { + this.result = new ArrayList<>(); + this.result.add(new ErrorMessage(errMsg)); + } + + public static class ErrorMessage { + + @SerializedName("data") + String errMsg; + + @SerializedName("type") + String errType; + + public ErrorMessage() { + } + + public ErrorMessage(String errMsg) { + this.errMsg = errMsg; + this.errType = "text/plain+error"; + } + + public String getErrMsg() { + return errMsg; + } + + public void setErrMsg(String errMsg) { + this.errMsg = errMsg; + } + } +} diff --git a/src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java b/src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java new file mode 100644 index 0000000000000000000000000000000000000000..089a6a626ed1e0d38306c911b515edd3fa8fa5f0 --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/galaxy/GalaxyWorkflowResult.java @@ -0,0 +1,208 @@ +/* + * Developed by Kechagias Konstantinos. + * Copyright (c) 2019. MIT License + */ + +package eu.hbp.mip.model.galaxy; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class GalaxyWorkflowResult { + + @SerializedName("history_content_type") + private String historyContentType; + @SerializedName("update_time") + private String updateTime; + @SerializedName("name") + private String name; + @SerializedName("extension") + private String extension; + @SerializedName("type_id") + private String typeId; + @SerializedName("deleted") + private Boolean deleted; + @SerializedName("history_id") + private String historyId; + @SerializedName("tags") + private List<Object> tags = null; + @SerializedName("id") + private String id; + @SerializedName("visible") + private Boolean visible; + @SerializedName("state") + private String state; + @SerializedName("create_time") + private String createTime; + @SerializedName("hid") + private Integer hid; + @SerializedName("url") + private String url; + @SerializedName("dataset_id") + private String datasetId; + @SerializedName("type") + private String type; + @SerializedName("purged") + private Boolean purged; + + public GalaxyWorkflowResult() { + } + + public GalaxyWorkflowResult(String historyContentType, String updateTime, String name, String extension, String typeId, Boolean deleted, String historyId, List<Object> tags, String id, Boolean visible, String state, String createTime, Integer hid, String url, String datasetId, String type, Boolean purged) { + this.historyContentType = historyContentType; + this.updateTime = updateTime; + this.name = name; + this.extension = extension; + this.typeId = typeId; + this.deleted = deleted; + this.historyId = historyId; + this.tags = tags; + this.id = id; + this.visible = visible; + this.state = state; + this.createTime = createTime; + this.hid = hid; + this.url = url; + this.datasetId = datasetId; + this.type = type; + this.purged = purged; + } + + public String getHistoryContentType() { + return historyContentType; + } + + public void setHistoryContentType(String historyContentType) { + this.historyContentType = historyContentType; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getTypeId() { + return typeId; + } + + public void setTypeId(String typeId) { + this.typeId = typeId; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public String getHistoryId() { + return historyId; + } + + public void setHistoryId(String historyId) { + this.historyId = historyId; + } + + public List<Object> getTags() { + return tags; + } + + public void setTags(List<Object> tags) { + this.tags = tags; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Boolean getVisible() { + return visible; + } + + public void setVisible(Boolean visible) { + this.visible = visible; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public Integer getHid() { + return hid; + } + + public void setHid(Integer hid) { + this.hid = hid; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDatasetId() { + return datasetId; + } + + public void setDatasetId(String datasetId) { + this.datasetId = datasetId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getPurged() { + return purged; + } + + public void setPurged(Boolean purged) { + this.purged = purged; + } +} + diff --git a/src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java b/src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..edefd63266a8d1678bf82d057882e4eccab112cc --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/galaxy/PostWorkflowToGalaxyDtoResponse.java @@ -0,0 +1,93 @@ +/* + * Developed by Kechagias Konstantinos. + * Copyright (c) 2019. MIT License + */ + +package eu.hbp.mip.model.galaxy; + +import com.google.gson.annotations.SerializedName; + +public class PostWorkflowToGalaxyDtoResponse { + + + @SerializedName("update_time") + String updateTime; + String uuid; + @SerializedName("history_id") + String historyId; + String stake; + @SerializedName("workflow_id") + String workflowId; + @SerializedName("model_class") + String modelClass; + String id; + + public PostWorkflowToGalaxyDtoResponse() { + } + + public PostWorkflowToGalaxyDtoResponse(String updateTime, String uuid, String historyId, String stake, String workflowId, String modelClass, String id) { + this.updateTime = updateTime; + this.uuid = uuid; + this.historyId = historyId; + this.stake = stake; + this.workflowId = workflowId; + this.modelClass = modelClass; + this.id = id; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getHistoryId() { + return historyId; + } + + public void setHistoryId(String historyId) { + this.historyId = historyId; + } + + public String getStake() { + return stake; + } + + public void setStake(String stake) { + this.stake = stake; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public String getModelClass() { + return modelClass; + } + + public void setModelClass(String modelClass) { + this.modelClass = modelClass; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java b/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..de537a1dcc4649e0ea6b28ff967567b599c77251 --- /dev/null +++ b/src/main/java/eu/hbp/mip/model/galaxy/WorkflowDTO.java @@ -0,0 +1,162 @@ +package eu.hbp.mip.model.galaxy; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import eu.hbp.mip.model.AlgorithmDTO; + +import java.util.*; + +public class WorkflowDTO { + + @SerializedName("id") + private String id; + + @SerializedName("name") + private String name; + + @SerializedName("inputs") + private HashMap<String, WorkflowInputDTO> inputs; + + @SerializedName("steps") + private HashMap<String, WorkflowStepDTO> steps; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public HashMap<String, WorkflowInputDTO> getInputs() { + return inputs; + } + + public void setInputs(HashMap<String, WorkflowInputDTO> inputs) { + this.inputs = inputs; + } + + public HashMap<String, WorkflowStepDTO> getSteps() { + return steps; + } + + public void setSteps(HashMap<String, WorkflowStepDTO> steps) { + this.steps = steps; + } + + public class WorkflowInputDTO { + @SerializedName("uuid") + private String uuid; + + @SerializedName("label") + private String label; + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + } + + public class WorkflowStepDTO { + @SerializedName("id") + private int id; + + @SerializedName("type") + private String type; + + @SerializedName("annotation") + private String annotation; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getAnnotation() { + return annotation; + } + + public void setAnnotation(String annotation) { + this.annotation = annotation; + } + } + + public AlgorithmDTO convertToAlgorithmDTO(){ + + AlgorithmDTO algorithmDTO = new AlgorithmDTO(); + + // Transfer workflow information + algorithmDTO.setName(id); + algorithmDTO.setDesc(""); + algorithmDTO.setLabel(name); + algorithmDTO.setType("workflow"); + + // Transfer workflow parameters information + List<AlgorithmDTO.AlgorithmParamDTO> algorithmParams = new LinkedList<>(); + Gson gson = new Gson(); + for (Map.Entry<String, WorkflowInputDTO> workflowInput : getInputs().entrySet()) { + + // Convert the annotation to algorithm Parameter + AlgorithmDTO.AlgorithmParamDTO algorithmParam; + if(steps.get(workflowInput.getKey()).getAnnotation() != null) { + algorithmParam = gson.fromJson(steps.get(workflowInput.getKey()).getAnnotation(), + AlgorithmDTO.AlgorithmParamDTO.class); + }else{ + // If annotation is not provided, auto-fill some information + algorithmParam = new AlgorithmDTO.AlgorithmParamDTO(); + // When the constraints are not known, set the most relaxed constraints + algorithmParam.setDesc(""); + algorithmParam.setValue(""); + algorithmParam.setValueType("string"); + algorithmParam.setValueNotBlank("false"); + algorithmParam.setDefaultValue(""); + algorithmParam.setDefaultValue("true"); + // If label is dataset/pathology/filter/formula the type should be the same + if(workflowInput.getValue().getLabel().equals("dataset") || + workflowInput.getValue().getLabel().equals("pathology")|| + workflowInput.getValue().getLabel().equals("filter")|| + workflowInput.getValue().getLabel().equals("formula")){ + algorithmParam.setType(workflowInput.getValue().getLabel()); + }else if(workflowInput.getValue().getLabel().equals("x") || + workflowInput.getValue().getLabel().equals("y")){ + algorithmParam.setType("column"); + algorithmParam.setColumnValuesSQLType("text,real,integer"); + algorithmParam.setColumnValuesIsCategorical(""); + }else{ + algorithmParam.setType("other"); + } + } + // Set the name to the workflow id + algorithmParam.setName(workflowInput.getValue().getUuid()); + algorithmParam.setLabel(workflowInput.getValue().getLabel()); + + algorithmParams.add(algorithmParam); + } + algorithmDTO.setParameters(algorithmParams); + + return algorithmDTO; + } + +} diff --git a/src/main/java/eu/hbp/mip/utils/JWTUtil.java b/src/main/java/eu/hbp/mip/utils/JWTUtil.java deleted file mode 100644 index 1e74841842150aef10778726040b88a53d14d218..0000000000000000000000000000000000000000 --- a/src/main/java/eu/hbp/mip/utils/JWTUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package eu.hbp.mip.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.auth0.jwt.algorithms.Algorithm; -import com.auth0.jwt.JWT; - -public class JWTUtil { - - private static final Logger LOGGER = LoggerFactory.getLogger(JWT.class); - - public static String getJWT(String secret, String subject) { - LOGGER.info("getJWT"); - Algorithm algorithm = Algorithm.HMAC512(secret); - String token = JWT.create().withIssuer("mip.humanbrainproject.eu").withSubject(subject).sign(algorithm); - - return token; - } -} \ No newline at end of file diff --git a/src/main/java/eu/hbp/mip/utils/UserActionLogging.java b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java index 745fc24a6ca53951c1f43cd9db2f6fa5965a73d4..b4a96fc864a16063c39dede8ea0233fbfd41b981 100644 --- a/src/main/java/eu/hbp/mip/utils/UserActionLogging.java +++ b/src/main/java/eu/hbp/mip/utils/UserActionLogging.java @@ -4,8 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalTime; - public class UserActionLogging { @@ -13,11 +11,15 @@ public class UserActionLogging { public static void LogAction(String actionName, String actionIdInfo) { - LOGGER.info( LocalTime.now()+" User : " + LOGGER.info( " User : " + SecurityContextHolder.getContext().getAuthentication().getName() - + " called enpoint " + actionName - + " info " - + actionIdInfo); + + " called endpoint: " + actionName + + " info: " + actionIdInfo); } + // Used from Threads because LogAction won't work. + public static void LogThreadAction(String actionName, String actionIdInfo) + { + LOGGER.info( "Thread -->" + actionName + " info: " + actionIdInfo); + } } diff --git a/src/main/resources/db/migration/V6_0__AddWorkflowDetails.sql b/src/main/resources/db/migration/V6_0__AddWorkflowDetails.sql new file mode 100644 index 0000000000000000000000000000000000000000..0e508b0efda4aa4842ebc9ff52d3b688761aca5b --- /dev/null +++ b/src/main/resources/db/migration/V6_0__AddWorkflowDetails.sql @@ -0,0 +1,2 @@ +ALTER TABLE experiment ADD COLUMN workflowhistoryid text; +ALTER TABLE experiment ADD COLUMN workflowstatus text; \ No newline at end of file