diff --git a/README.md b/README.md index 0bd35af..bd3e81c 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,13 @@ Installation instructions: - Do you want to install any cache files (y/n)? -> y, install GRCh37 or GRCh38 cache - Do you want to install any FASTA files (y/n)? -> y, install GRCh37 or GRCh38 FASTA - Do you want to install any plugins (y/n)? -> n + +## Connect an Ensembl database + +In order to annotate c. alterations, you must connect to a MySQL database. For an example of connecting to Ensembl's public database, you would use the defaults +show in [application.properties](target/classes/application.properties). + +However, Ensembl does not recommend connecting to their public database (as it is very slow), so it is recommended to set up a local copy of their database by following +the [instructions](https://useast.ensembl.org/info/docs/webcode/mirror/install/ensembl-data.html) on the Ensembl website. + +**IMPORTANT: Make sure the ensembl-vep docker image, the cache files, and the MySQL database are all using the same version.** \ No newline at end of file diff --git a/src/main/java/org/genomenexus/vep_wrapper/VepHgvsController.java b/src/main/java/org/genomenexus/vep_wrapper/VepHgvsController.java new file mode 100644 index 0000000..485d289 --- /dev/null +++ b/src/main/java/org/genomenexus/vep_wrapper/VepHgvsController.java @@ -0,0 +1,100 @@ +package org.genomenexus.vep_wrapper; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@RestController +@CrossOrigin(origins="*") +@RequestMapping(value= "/vep/human/hgvsc") +@ConditionalOnProperty( + value = "database.enabled", + havingValue = "true" +) +@Api(tags = "vep-hgvs-controller", description = "VEP HGVS Controller") +public class VepHgvsController { + + @Autowired + private VepRunner vepRunner; + + @RequestMapping(value = "/{hgvsc}", + method = RequestMethod.GET, + produces = "application/json") + @ApiOperation(value = "Retrieves VEP results for single c. variant specified in hgvs syntax (https://ensembl.org/info/docs/tools/vep/vep_formats.html)", + nickname = "fetchVepHgvscAnnotationByGET") + public void getVepHgvscAnnotation( + @ApiParam(value="ENST00000618231.3:c.9G>C", required=true) + @PathVariable + String hgvsc, + @ApiParam("Maximum time (in seconds) to let VEP construct a response (0 = no limit)") + @RequestParam(defaultValue = "0") + Integer responseTimeout, + HttpServletResponse response) { + OutputStream out = null; + try { + out = response.getOutputStream(); + response.setContentType("application/json"); + vepRunner.run(Arrays.asList(hgvsc), false, responseTimeout, out, true); + } catch (IOException | InterruptedException | VepLaunchFailureException e) { + e.printStackTrace(); + // TODO: throw and handle errors with global exception handler + } finally { + try { + response.flushBuffer(); + } catch (Throwable e) { + e.printStackTrace(); + // TODO: throw and handle errors with global exception handler + } + } + } + + @RequestMapping(value = "/", + method = RequestMethod.POST) + @ApiOperation(value = "Retrieves VEP results for multiple c. variants specified in hgvs syntax (https://ensembl.org/info/docs/tools/vep/vep_formats.html)", + nickname = "fetchVepHgvscAnnotationsByPOST") + public void fetchVepHgvscAnnotationsPOST( + @ApiParam(value = "List of variants in ENSEMBL hgvsc format. For example:\n" + + "[\"ENST00000618231.3:c.9G>C\", \"ENST00000471631.1:c.28_33delTCGCGG\"]", + required = true) + @RequestBody + List hgvscStrings, + @ApiParam("Maximum time (in seconds) to let VEP construct a response (0 = no limit)") + @RequestParam(defaultValue = "0") + Integer responseTimeout, + HttpServletResponse response) { + OutputStream out = null; + try { + out = response.getOutputStream(); + response.setContentType("application/json"); + vepRunner.run(hgvscStrings, true, responseTimeout, out, true); + } catch (IOException | InterruptedException | VepLaunchFailureException e) { + e.printStackTrace(); + // TODO: throw and handle errors with global exception handler + } finally { + try { + response.flushBuffer(); + } catch (Throwable e) { + e.printStackTrace(); + // TODO: throw and handle errors with global exception handler + } + } + return; + } +} diff --git a/src/main/java/org/genomenexus/vep_wrapper/VepController.java b/src/main/java/org/genomenexus/vep_wrapper/VepRegionController.java similarity index 91% rename from src/main/java/org/genomenexus/vep_wrapper/VepController.java rename to src/main/java/org/genomenexus/vep_wrapper/VepRegionController.java index 23df270..8cabd49 100644 --- a/src/main/java/org/genomenexus/vep_wrapper/VepController.java +++ b/src/main/java/org/genomenexus/vep_wrapper/VepRegionController.java @@ -15,14 +15,14 @@ @RestController @CrossOrigin(origins="*") // allow all cross-domain requests -@RequestMapping(value= "/") -@Api(tags = "vep-controller", description = "VEP Controller") -public class VepController { +@RequestMapping(value= "/vep/human/region") +@Api(tags = "vep-region-controller", description = "VEP Region Controller") +public class VepRegionController { @Autowired private VepRunner vepRunner; - @RequestMapping(value = "/vep/human/region/{region}/{allele}", + @RequestMapping(value = "/{region}/{allele}", method = RequestMethod.GET, produces = "application/json") @ApiOperation(value = "Retrieves VEP results for single variant specified in region syntax (https://ensembl.org/info/docs/tools/vep/vep_formats.html)", @@ -44,7 +44,7 @@ public void getVepAnnotation( try { out = response.getOutputStream(); response.setContentType("application/json"); - vepRunner.run(Arrays.asList(region + "/" + allele), false, responseTimeout, out); + vepRunner.run(Arrays.asList(region + "/" + allele), false, responseTimeout, out, false); } catch (IOException | InterruptedException | VepLaunchFailureException e) { e.printStackTrace(); // TODO: throw and handle errors with global exception handler @@ -58,7 +58,7 @@ public void getVepAnnotation( } } - @RequestMapping(value = "/vep/human/region", + @RequestMapping(value = "/", method = RequestMethod.POST) @ApiOperation(value = "Retrieves VEP annotations for multiple variants specified in region syntax (https://ensembl.org/info/docs/tools/vep/vep_formats.html)", nickname = "fetchVepAnnotationByRegionsPOST") @@ -83,7 +83,7 @@ public void fetchVepAnnotationByRegionsPOST( try { out = response.getOutputStream(); response.setContentType("application/json"); - vepRunner.run(regions, true, responseTimeout, out); + vepRunner.run(regions, true, responseTimeout, out, false); } catch (IOException | InterruptedException | VepLaunchFailureException e) { e.printStackTrace(); // TODO: throw and handle errors with global exception handler @@ -97,5 +97,4 @@ public void fetchVepAnnotationByRegionsPOST( } return; } - } diff --git a/src/main/java/org/genomenexus/vep_wrapper/VepRunner.java b/src/main/java/org/genomenexus/vep_wrapper/VepRunner.java index 078120e..7bd1259 100644 --- a/src/main/java/org/genomenexus/vep_wrapper/VepRunner.java +++ b/src/main/java/org/genomenexus/vep_wrapper/VepRunner.java @@ -40,9 +40,21 @@ public class VepRunner { private String vepAssembly; // Path is relative to the VEP_WORK_DIRECTORY_PATH - @Value("${vep.fastaFileRelativePath:homo_sapiens/98_GRCh37/Homo_sapiens.GRCh37.75.dna.primary_assembly.fa.gz}") + @Value("${vep.fastaFileRelativePath:homo_sapiens/112_GRCh37/Homo_sapiens.GRCh37.dna.primary_assembly.fa.gz}") private String vepFastaFileRelativePath; + @Value("${database.host}") + private String databaseHost; + + @Value("${database.port}") + private String databasePort; + + @Value("${database.user}") + private String databaseUser; + + @Value("${database.password}") + private String databasePassword; + private Path vepFastaFilePath; @Autowired private void setVepFastaFilePath() { @@ -64,21 +76,21 @@ private void createTmpDirIfNecessary() throws IOException { } /** - * Create a file containing the regions received in the input query. - * Write the user supplied regions from the "regions" argument to an output file. - * CAUTION : this function does not sort the regions into chromosomal order. The + * Create a file containing the variants received in the input query. + * Write the user supplied variants from the "variants" argument to an output file. + * CAUTION : this function does not sort the variants into chromosomal order. The * VEP command line tool is very slow when the input is not sorted. It is expected * that users of the VepRunner will always send requests that have been pre-sorted. * - * @param regions - the regions as passed by the user + * @param variants - the variants as passed by the user * @param vepInputFile - the file to be written * @return sum of two operands **/ - private void constructFileForVepProcessing(List regions, Path vepInputFile) throws IOException { + private void constructFileForVepProcessing(List variants, Path vepInputFile) throws IOException { try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(vepInputFile))) { - for (String region : regions) { - out.println(region); + for (String variant : variants) { + out.println(variant); } out.close(); } catch (IOException e) { @@ -111,7 +123,7 @@ public boolean timeIsExpired(Instant timeToKillProcess) { return Instant.now().isAfter(timeToKillProcess); } - public void run(List regions, Boolean convertToListJSON, Integer responseTimeout, OutputStream responseOut) + public void run(List variants, Boolean convertToListJSON, Integer responseTimeout, OutputStream responseOut, boolean useDatabase) throws IOException, InterruptedException, VepLaunchFailureException { printWithTimestamp("Running vep"); @@ -120,7 +132,26 @@ public void run(List regions, Boolean convertToListJSON, Integer respons Path constructedInputFile = createTempFileForVepInput(); // get vep parameters (use -Dvep.params to change) - String vepParameters = System.getProperty("vep.params", String.join(" ", + String vepParameters; + if (useDatabase) { + vepParameters = System.getProperty("vep.params", String.join(" ", + "--database", + "--host " + databaseHost, + "--user " + databaseUser, + "--password " + databasePassword, + "--port " + databasePort, + "--everything", + "--hgvsg", + "--xref_refseq", + "--format hgvs", + "--fork " + vepForkCount, + "--fasta " + vepFastaFilePath, + "--json", + "-i " + constructedInputFile, + "-o STDOUT", + "--no_stats")); + } else { + vepParameters = System.getProperty("vep.params", String.join(" ", "--cache", "--offline", "--everything", @@ -134,6 +165,7 @@ public void run(List regions, Boolean convertToListJSON, Integer respons "-i " + constructedInputFile, "-o STDOUT", "--no_stats")); + } // build command List commandElements = new ArrayList(); @@ -143,7 +175,7 @@ public void run(List regions, Boolean convertToListJSON, Integer respons } printWithTimestamp("writing constructed input file"); - constructFileForVepProcessing(regions, constructedInputFile); + constructFileForVepProcessing(variants, constructedInputFile); printWithTimestamp("processing requests"); printWithTimestamp("process command elements: " + commandElements); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..d89c01a --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,6 @@ +database: + enabled: true + host: host.docker.internal + port: 3306 + user: root + password: \ No newline at end of file