diff --git a/lapis2/build.gradle b/lapis2/build.gradle index 808c6050..b92c79c9 100644 --- a/lapis2/build.gradle +++ b/lapis2/build.gradle @@ -36,6 +36,7 @@ dependencies { } testImplementation 'com.ninja-squad:springmockk:4.0.2' testImplementation 'org.mock-server:mockserver-netty:5.15.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib" } diff --git a/lapis2/src/main/antlr/org/genspectrum/lapis/model/variantqueryparser/VariantQuery.g4 b/lapis2/src/main/antlr/org/genspectrum/lapis/model/variantqueryparser/VariantQuery.g4 index 54e94e69..5bc98090 100644 --- a/lapis2/src/main/antlr/org/genspectrum/lapis/model/variantqueryparser/VariantQuery.g4 +++ b/lapis2/src/main/antlr/org/genspectrum/lapis/model/variantqueryparser/VariantQuery.g4 @@ -16,6 +16,12 @@ single: nucleotide_mutation | pangolineage_query | n_of_query + | nucleotide_insertion + | aa_mutation + | aa_insertion + | nextclade_pangolineage_query + | nextstrain_clade_lineage_query + | gisaid_clade_lineage_query ; nucleotide_mutation : nucleotide_symbol? position ambigous_nucleotide_symbol?; @@ -34,6 +40,30 @@ n_of_match_exactly: 'EXACTLY-'; n_of_number_of_matchers: NUMBER+; n_of_exprs: expr (',' expr)*; +nucleotide_insertion: 'ins_' position ':' (ambigous_nucleotide_symbol | '?')+; + +aa_mutation: gene ':' aa_symbol? position ambigous_aa_symbol?; +aa_symbol: A | R | N | D | C | E | Q | G | H | I | L | K | M | F | P | S | T | W | Y | V | ASTERISK; +ambigous_aa_symbol: aa_symbol | X | MINUS | DOT; +gene: covid_gene; +covid_gene : E | M | N | S | ORF; + +aa_insertion: 'ins_' gene ':' (ambigous_aa_symbol | '?')+; + +nextclade_pangolineage_query: nextclade_pango_lineage_prefix pangolineage_query; +nextclade_pango_lineage_prefix: 'nextcladePangoLineage:'; + +nextstrain_clade_lineage_query: nextstrain_clade_prefix nextstrain_clade_query; +nextstrain_clade_prefix: 'nextstrainClade:'; +nextstrain_clade_query: NUMBER NUMBER nextstrain_clade_character | 'RECOMBINANT'; +nextstrain_clade_character: A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z; + +gisaid_clade_lineage_query: gisaid_clade_prefix gisaid_clade_query; +gisaid_clade_prefix: 'gisaid:'; +gisaid_clade_query: gisaid_clade_character gisaid_clade_character?; +gisaid_clade_character: A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z; + + // lexer rules A: 'A'; @@ -68,3 +98,5 @@ ASTERISK: '*'; NUMBER: [0-9]; WHITESPACE: [ \r\n\t] -> skip; + +ORF: 'ORF1A' | 'ORF1B' | 'ORF3A' | 'ORF6' | 'ORF7A' | 'ORF7B' | 'ORF8' | 'ORF9B'; diff --git a/lapis2/src/main/kotlin/org/genspectrum/lapis/controller/ExceptionHandler.kt b/lapis2/src/main/kotlin/org/genspectrum/lapis/controller/ExceptionHandler.kt index fbda5d36..cc666bab 100644 --- a/lapis2/src/main/kotlin/org/genspectrum/lapis/controller/ExceptionHandler.kt +++ b/lapis2/src/main/kotlin/org/genspectrum/lapis/controller/ExceptionHandler.kt @@ -65,6 +65,21 @@ class ExceptionHandler : ResponseEntityExceptionHandler() { ), ) } + + @ExceptionHandler(NotImplementedError::class) + fun handleNotImplementedError(e: NotImplementedError): ResponseEntity { + return ResponseEntity + .status(HttpStatus.NOT_IMPLEMENTED) + .contentType(MediaType.APPLICATION_JSON) + .body( + jacksonObjectMapper().writeValueAsString( + LapisHttpErrorResponse( + "Not implemented", + "${e.message}", + ), + ), + ) + } } data class LapisHttpErrorResponse(val title: String, val message: String) diff --git a/lapis2/src/main/kotlin/org/genspectrum/lapis/model/VariantQueryCustomListener.kt b/lapis2/src/main/kotlin/org/genspectrum/lapis/model/VariantQueryCustomListener.kt index 6e62a935..0e48c536 100644 --- a/lapis2/src/main/kotlin/org/genspectrum/lapis/model/VariantQueryCustomListener.kt +++ b/lapis2/src/main/kotlin/org/genspectrum/lapis/model/VariantQueryCustomListener.kt @@ -1,10 +1,16 @@ package org.genspectrum.lapis.model import VariantQueryBaseListener +import VariantQueryParser.Aa_insertionContext +import VariantQueryParser.Aa_mutationContext import VariantQueryParser.AndContext +import VariantQueryParser.Gisaid_clade_lineage_queryContext import VariantQueryParser.MaybeContext import VariantQueryParser.N_of_queryContext +import VariantQueryParser.Nextclade_pangolineage_queryContext +import VariantQueryParser.Nextstrain_clade_queryContext import VariantQueryParser.NotContext +import VariantQueryParser.Nucleotide_insertionContext import VariantQueryParser.Nucleotide_mutationContext import VariantQueryParser.OrContext import VariantQueryParser.Pangolineage_queryContext @@ -82,4 +88,28 @@ class VariantQueryCustomListener : VariantQueryBaseListener(), ParseTreeListener expressionStack.addLast(NOf(n, matchExactly, children.reversed())) } + + override fun enterNucleotide_insertion(ctx: Nucleotide_insertionContext?) { + throw NotImplementedError("Nucleotide insertions are not supported yet.") + } + + override fun enterAa_mutation(ctx: Aa_mutationContext?) { + throw NotImplementedError("Amino acid mutations are not supported yet.") + } + + override fun enterAa_insertion(ctx: Aa_insertionContext?) { + throw NotImplementedError("Amino acid insertions are not supported yet.") + } + + override fun enterNextclade_pangolineage_query(ctx: Nextclade_pangolineage_queryContext?) { + throw NotImplementedError("Nextclade pango lineages are not supported yet.") + } + + override fun enterNextstrain_clade_query(ctx: Nextstrain_clade_queryContext?) { + throw NotImplementedError("Nextstrain clade lineages are not supported yet.") + } + + override fun enterGisaid_clade_lineage_query(ctx: Gisaid_clade_lineage_queryContext?) { + throw NotImplementedError("Gisaid clade lineages are not supported yet.") + } } diff --git a/lapis2/src/test/kotlin/org/genspectrum/lapis/model/VariantQueryFacadeTest.kt b/lapis2/src/test/kotlin/org/genspectrum/lapis/model/VariantQueryFacadeTest.kt index 877dc617..adf52f76 100644 --- a/lapis2/src/test/kotlin/org/genspectrum/lapis/model/VariantQueryFacadeTest.kt +++ b/lapis2/src/test/kotlin/org/genspectrum/lapis/model/VariantQueryFacadeTest.kt @@ -216,6 +216,78 @@ class VariantQueryFacadeTest { MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult)) } + @Test + fun `given a variant variantQuery with a 'Insertion' expression the map should throw an error`() { + val variantQuery = "ins_1234:GAG" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Nucleotide insertions are not supported yet."), + ) + } + + @Test + fun `given a valid variant variantQuery with a 'AA mutation' expression the map should throw an error`() { + val variantQuery = "S:N501Y" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Amino acid mutations are not supported yet."), + ) + } + + @Test + fun `given a valid variant variantQuery with a 'AA insertion' expression the map should throw an error`() { + val variantQuery = "ins_S:N501EPE" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Amino acid insertions are not supported yet."), + ) + } + + @Test + fun `given a valid variant variantQuery with a 'nextclade pango lineage' expression the map should throw an error`() { + val variantQuery = "nextcladePangoLineage:BA.5*" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Nextclade pango lineages are not supported yet."), + ) + } + + @Test + fun `given a valid variant variantQuery with a 'Nextstrain clade lineage' expression the map should throw an error`() { + val variantQuery = "nextstrainClade:22B" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Nextstrain clade lineages are not supported yet."), + ) + } + + @Test + fun `given a valid variant variantQuery with a 'Gisaid clade lineage' expression the map should throw an error`() { + val variantQuery = "gisaid:AB" + + val exception = assertThrows { underTest.map(variantQuery) } + + MatcherAssert.assertThat( + exception.message, + Matchers.equalTo("Gisaid clade lineages are not supported yet."), + ) + } + @Test fun `given a variantQuery with a exact 'Nof' expression then map should return the corresponding SiloQuery`() { val variantQuery = "[exactly-3-of: 123A, 234T, 345G]"