Skip to content

Commit

Permalink
Add @OpenAPIDefinition to ExomiserPrioritiserServer
Browse files Browse the repository at this point in the history
Update PrioritiserController endpoints from `/api/v1/prioritise` to `/api/v1/prioritise/gene`
Update PrioritiserService.prioritise() method to prioritiseGenes
Set `hiphive` as the default prioritiser and set requirement to not required.
Add a README.md
  • Loading branch information
julesjacobsen committed Dec 19, 2024
1 parent 2220643 commit fd1c1e5
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 30 deletions.
63 changes: 63 additions & 0 deletions exomiser-rest-prioritiser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Exomiser Prioritiser REST API
===

Requirements
--
The jar file built from this maven module, or pre-built versions can be found on GitHub e.g.
https://github.com/exomiser/Exomiser/releases/download/14.1.0/exomiser-rest-prioritiser-14.1.0.jar

And a current version of the phenotype data. The data is updated a few times a year and the release announcements are
also on GitHub: https://github.com/exomiser/Exomiser/discussions/categories/data-release

In this example we're using the 2410_phenotype data release which can be found here:
https://g-879a9f.f5dc97.75bc.dn.glob.us/data/2410_phenotype.zip


Setup
--
This is a Spring Boot application, which means it can probably be configured to run the way you need for your setup. It
will require configuration either using a properties file, which you can find in
`src/main/resources/application.properties`. Alternatively, these can be provided as command-line arguments or environment
variables when launching the application. More info on configuration of Spring Boot applications can be found in their
[docs](https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.files)

To set up on your local machine:
- Create a new directory called `exomiser` and download the jar, application.properties and zip files into this. Extract
the zip file so that you have should now have a `2410_phenotype` subfolder.

- Edit the application.properties so that it looks like this:
```
exomiser.data-directory=full/path/to/your/new/exomiser/dir
exomiser.phenotype.data-version=2410
```
You might need to delete the keys starting `info` - they will be present in the app, and you shouldn't need to change them.

- In a terminal, launch the app using `java -jar exomiser-rest-prioritiser-14.1.0.jar` from the `exomiser` folder. You
should see a bunch of logging output which stops after a few seconds with these lines:
```
2024-12-18T10:23:33.827Z INFO 452968 --- [exomiser-prioritiser-service] [ main] o.m.e.r.p.api.PrioritiserController : Started PrioritiserController with GeneIdentifier cache of 19762 entries
2024-12-18T10:23:34.249Z INFO 452968 --- [exomiser-prioritiser-service] [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint(s) beneath base path '/actuator'
2024-12-18T10:23:34.305Z INFO 452968 --- [exomiser-prioritiser-service] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8085 (http) with context path '/exomiser-prioritiser'
2024-12-18T10:23:34.322Z INFO 452968 --- [exomiser-prioritiser-service] [ main] o.m.e.r.p.ExomiserPrioritiserServer : Started ExomiserPrioritiserServer in 6.056 seconds (process running for 6.676)
```
It is now ready to use. Where you keep the jar and the data files is up to you. You just need to tell the application
where the data can be found using the full path to the parent directory where the data has been unpacked in the
application.properties. For example, if you unpacked the data to `/data/2410_phenotype` then`exomiser.data-directory=/data`
and `exomiser.phenotype.data-version=2410`.
Note that if you have an existing Exomiser CLI installation, you can add this jar file to that directory and the REST
service will use the properties from the existing `application.properties`. Alternatively the `exomiser.data-directory`
and `exomiser.phenotype.data-version` can be supplied as command-line arguments when starting the jar without the need
for an `application.properties` file.
Running
---
There is an OpenAPI 3 page which should be accessible here if everything went successfully:
```shell
http://localhost:8085/exomiser-prioritiser/swagger-ui/index.html
```

This contains examples of the input parameters and the expected output.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

package org.monarchinitiative.exomiser.rest.prioritiser;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.monarchinitiative.exomiser.autoconfigure.ExomiserAutoConfiguration;
import org.monarchinitiative.exomiser.autoconfigure.genome.GenomeAnalysisServiceAutoConfiguration;
import org.springframework.boot.SpringApplication;
Expand All @@ -34,6 +36,13 @@
ExomiserAutoConfiguration.class,
GenomeAnalysisServiceAutoConfiguration.class
})
@OpenAPIDefinition(
info = @Info(
title = "Exomiser Prioritiser API",
version = "1.0.0",
description = "API for prioritising genes based on phenotype semantic similarity")

)
public class ExomiserPrioritiserServer {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@

package org.monarchinitiative.exomiser.rest.prioritiser.api;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand All @@ -42,7 +40,7 @@
* @author Jules Jacobsen <jules.jacobsen@sanger.ac.uk>
*/
@RestController
@RequestMapping("api/v1")
@RequestMapping("api/v1/prioritise")
@Tag(name = "Prioritiser", description = "API endpoints for phenotype-based gene prioritisation")
public class PrioritiserController {

Expand Down Expand Up @@ -73,8 +71,8 @@ public PrioritiserController(PrioritiserService prioritiserService) {
description = "Invalid input parameters"
)
})
@GetMapping(value = "prioritise", produces = MediaType.APPLICATION_JSON_VALUE)
public PrioritiserResultSet prioritise(
@GetMapping(value = "gene", produces = MediaType.APPLICATION_JSON_VALUE)
public PrioritiserResultSet prioritiseGenes(
@Parameter(
description = "Set of HPO phenotype identifiers",
example = "[\"HP:0001156\", \"HP:0001363\", \"HP:0011304\", \"HP:0010055\"]",
Expand All @@ -90,11 +88,15 @@ public PrioritiserResultSet prioritise(
@RequestParam(value = "genes", required = false, defaultValue = "") Set<Integer> genesIds,

@Parameter(
description = "Name of the prioritiser algorithm to use. One of ['hiphive', 'phenix', 'phive']",
description = "Name of the prioritiser algorithm to use. One of ['hiphive', 'phenix', 'phive']. " +
"Defaults to 'hiphive' which allows for cross-species and PPI hits. 'phenix' is a" +
" legacy prioritiser which will only prioritise human disease-gene associations. It is" +
" the equivalent of 'hiphive' with prioritiser-params='human'. 'phive' is just the" +
" mouse subset of hiphive, equivalent to 'hiphive' with prioritiser-params='mouse'.",
example = "hiphive",
required = true
required = false
)
@RequestParam(value = "prioritiser") String prioritiserName,
@RequestParam(value = "prioritiser", defaultValue = "hiphive") String prioritiserName,

@Parameter(
description = "Additional parameters for the prioritiser. This is optional for the 'hiphive' prioritiser." +
Expand All @@ -120,7 +122,7 @@ public PrioritiserResultSet prioritise(
.limit(limit)
.build();

return prioritise(prioritiserRequest);
return prioritiseGenes(prioritiserRequest);
}

@Operation(
Expand All @@ -142,18 +144,18 @@ public PrioritiserResultSet prioritise(
)
})
@PostMapping(
value = "prioritise",
value = "gene",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public PrioritiserResultSet prioritise(
public PrioritiserResultSet prioritiseGenes(
@Parameter(
description = "Prioritisation request parameters",
required = true
)
@RequestBody PrioritiserRequest prioritiserRequest
) {
return prioritiserService.prioritise(prioritiserRequest);
return prioritiserService.prioritiseGenes(prioritiserRequest);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ public record PrioritiserRequest(
List<Integer> genes,

@Schema(
description = "Name of the prioritiser algorithm to use. One of ['hiphive', 'phenix', 'phive']",
description = "Name of the prioritiser algorithm to use. One of ['hiphive', 'phenix', 'phive']. " +
"Defaults to 'hiphive' which allows for cross-species and PPI hits. 'phenix' is a" +
" legacy prioritiser which will only prioritise human disease-gene associations. It is" +
" the equivalent of 'hiphive' with prioritiser-params='human'. 'phive' is just the" +
" mouse subset of hiphive, equivalent to 'hiphive' with prioritiser-params='mouse'.",
example = "hiphive",
requiredMode = Schema.RequiredMode.REQUIRED
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
String prioritiser,

Expand Down Expand Up @@ -104,7 +108,7 @@ public static Builder builder() {
public static class Builder {
private Collection<String> phenotypes = new ArrayList<>();
private Collection<Integer> genes = new ArrayList<>();
private String prioritiser = "";
private String prioritiser = "hiphive";
private String prioritiserParams = "";
private int limit;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.monarchinitiative.exomiser.rest.prioritiser.service;

import io.swagger.v3.oas.annotations.Parameter;
import org.monarchinitiative.exomiser.core.model.Gene;
import org.monarchinitiative.exomiser.core.model.GeneIdentifier;
import org.monarchinitiative.exomiser.core.prioritisers.HiPhiveOptions;
Expand All @@ -12,7 +11,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -29,7 +27,7 @@ public record PrioritiserService(Map<Integer, GeneIdentifier> geneIdentifiers, P
logger.info("Started PrioritiserService with GeneIdentifier cache of {} entries", geneIdentifiers.size());
}

public PrioritiserResultSet prioritise(PrioritiserRequest prioritiserRequest){
public PrioritiserResultSet prioritiseGenes(PrioritiserRequest prioritiserRequest){
logger.info("{}", prioritiserRequest);

Instant start = Instant.now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
@WebMvcTest(PrioritiserController.class)
class PrioritiserControllerTest {

private static final String API_V_1_PRIORITISE_GENE = "/api/v1/prioritise/gene";

@Autowired
private MockMvc mockMvc;

Expand Down Expand Up @@ -90,10 +92,10 @@ class GetPrioritisationTests {
@Test
@DisplayName("Should return prioritised results with valid parameters")
void shouldReturnPrioritisedResults() throws Exception {
when(prioritiserService.prioritise(any()))
when(prioritiserService.prioritiseGenes(any()))
.thenReturn(sampleResultSet);

mockMvc.perform(get("/api/v1/prioritise")
mockMvc.perform(get(API_V_1_PRIORITISE_GENE)
.param("phenotypes", "HP:0001250,HP:0001251")
.param("genes", "1234,5678")
.param("prioritiser", "phenix")
Expand All @@ -110,10 +112,10 @@ void shouldReturnPrioritisedResults() throws Exception {
@Test
@DisplayName("Should handle missing optional parameters")
void shouldHandleMissingOptionalParams() throws Exception {
when(prioritiserService.prioritise(any()))
when(prioritiserService.prioritiseGenes(any()))
.thenReturn(sampleResultSet);

mockMvc.perform(get("/api/v1/prioritise")
mockMvc.perform(get(API_V_1_PRIORITISE_GENE)
.param("phenotypes", "HP:0001250")
.param("prioritiser", "phenix")
.accept(MediaType.APPLICATION_JSON))
Expand All @@ -124,7 +126,7 @@ void shouldHandleMissingOptionalParams() throws Exception {
@Test
@DisplayName("Should return 400 when required parameters are missing")
void shouldReturn400WhenMissingRequiredParams() throws Exception {
mockMvc.perform(get("/api/v1/prioritise")
mockMvc.perform(get(API_V_1_PRIORITISE_GENE)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest());
}
Expand All @@ -137,10 +139,10 @@ class PostPrioritisationTests {
@Test
@DisplayName("Should process valid POST request")
void shouldProcessValidPostRequest() throws Exception {
when(prioritiserService.prioritise(any(PrioritiserRequest.class)))
when(prioritiserService.prioritiseGenes(any(PrioritiserRequest.class)))
.thenReturn(sampleResultSet);

mockMvc.perform(post("/api/v1/prioritise")
mockMvc.perform(post(API_V_1_PRIORITISE_GENE)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(sampleRequest))
.accept(MediaType.APPLICATION_JSON))
Expand All @@ -161,10 +163,10 @@ void shouldHandleMinimalPostRequest() throws Exception {
0
);

when(prioritiserService.prioritise(any(PrioritiserRequest.class)))
when(prioritiserService.prioritiseGenes(any(PrioritiserRequest.class)))
.thenReturn(new PrioritiserResultSet(minimalRequest, 50L, List.of()));

mockMvc.perform(post("/api/v1/prioritise")
mockMvc.perform(post(API_V_1_PRIORITISE_GENE)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(minimalRequest))
.accept(MediaType.APPLICATION_JSON))
Expand All @@ -178,7 +180,7 @@ void shouldHandleMinimalPostRequest() throws Exception {
void shouldReturn400ForInvalidRequestBody() throws Exception {
String invalidJson = "{\"phenotypes\": null, \"prioritiser\": null}";

mockMvc.perform(post("/api/v1/prioritise")
mockMvc.perform(post(API_V_1_PRIORITISE_GENE)
.contentType(MediaType.APPLICATION_JSON)
.content(invalidJson)
.accept(MediaType.APPLICATION_JSON))
Expand All @@ -194,10 +196,10 @@ void shouldHandleEmptyResults() throws Exception {
List.of()
);

when(prioritiserService.prioritise(any(PrioritiserRequest.class)))
when(prioritiserService.prioritiseGenes(any(PrioritiserRequest.class)))
.thenReturn(emptyResultSet);

mockMvc.perform(post("/api/v1/prioritise")
mockMvc.perform(post(API_V_1_PRIORITISE_GENE)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(sampleRequest))
.accept(MediaType.APPLICATION_JSON))
Expand Down

0 comments on commit fd1c1e5

Please sign in to comment.