Skip to content

Commit

Permalink
feat: add unaligned nucleotide sequence route
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKellerer committed Jan 25, 2024
1 parent 7c0f597 commit adda1dc
Show file tree
Hide file tree
Showing 10 changed files with 823 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ const val ALIGNED_AMINO_ACID_SEQUENCE_ENDPOINT_DESCRIPTION =
const val ALIGNED_SINGLE_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION =
"""Returns a string of fasta formatted aligned nucleotide sequences. Only sequences matching the
specified sequence filters are considered."""
const val UNALIGNED_SINGLE_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION =
"""Returns a string of fasta formatted unaligned nucleotide sequences. Only sequences matching the
specified sequence filters are considered."""
const val ALIGNED_MULTI_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION =
"""Returns a string of fasta formatted aligned nucleotide sequences of the requested segment.
Only sequences matching the specified sequence filters are considered."""
const val UNALIGNED_MULTI_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION =
"""Returns a string of fasta formatted unaligned nucleotide sequences of the requested segment.
Only sequences matching the specified sequence filters are considered."""

const val AGGREGATED_GROUP_BY_FIELDS_DESCRIPTION =
"""The fields to stratify by. If empty, only the overall count is returned"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const val NUCLEOTIDE_INSERTIONS_ROUTE = "/nucleotideInsertions"
const val AMINO_ACID_INSERTIONS_ROUTE = "/aminoAcidInsertions"
const val ALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE = "/alignedNucleotideSequences"
const val ALIGNED_AMINO_ACID_SEQUENCES_ROUTE = "/alignedAminoAcidSequences"
const val UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE = "/unalignedNucleotideSequences"

@RestController
@RequestMapping("/sample")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.genspectrum.lapis.model.SiloQueryModel
import org.genspectrum.lapis.openApi.AminoAcidInsertions
import org.genspectrum.lapis.openApi.AminoAcidMutations
import org.genspectrum.lapis.openApi.LapisAlignedMultiSegmentedNucleotideSequenceResponse
import org.genspectrum.lapis.openApi.LapisUnalignedMultiSegmentedNucleotideSequenceResponse
import org.genspectrum.lapis.openApi.Limit
import org.genspectrum.lapis.openApi.NUCLEOTIDE_SEQUENCE_REQUEST_SCHEMA
import org.genspectrum.lapis.openApi.NucleotideInsertions
Expand Down Expand Up @@ -112,4 +113,74 @@ class MultiSegmentedSequenceController(
segment,
)
}

@GetMapping("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/{segment}", produces = ["text/x-fasta"])
@LapisUnalignedMultiSegmentedNucleotideSequenceResponse
fun getUnalignedNucleotideSequence(
@PathVariable(name = "segment", required = true)
@Segment
segment: String,
@PrimitiveFieldFilters
@RequestParam
sequenceFilters: GetRequestSequenceFilters?,
@NucleotideSequencesOrderByFields
@RequestParam
orderBy: List<OrderByField>?,
@NucleotideMutations
@RequestParam
nucleotideMutations: List<NucleotideMutation>?,
@AminoAcidMutations
@RequestParam
aminoAcidMutations: List<AminoAcidMutation>?,
@NucleotideInsertions
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@AminoAcidInsertions
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
@Limit
@RequestParam
limit: Int? = null,
@Offset
@RequestParam
offset: Int? = null,
): String {
val request = SequenceFiltersRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
orderBy ?: emptyList(),
limit,
offset,
)

requestContext.filter = request

return siloQueryModel.getGenomicSequence(
request,
SequenceType.UNALIGNED,
segment,
)
}

@PostMapping("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/{segment}", produces = ["text/x-fasta"])
@LapisUnalignedMultiSegmentedNucleotideSequenceResponse
fun postUnalignedNucleotideSequence(
@PathVariable(name = "segment", required = true)
@Segment
segment: String,
@Parameter(schema = Schema(ref = "#/components/schemas/$NUCLEOTIDE_SEQUENCE_REQUEST_SCHEMA"))
@RequestBody
request: SequenceFiltersRequest,
): String {
requestContext.filter = request

return siloQueryModel.getGenomicSequence(
request,
SequenceType.UNALIGNED,
segment,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.genspectrum.lapis.model.SiloQueryModel
import org.genspectrum.lapis.openApi.AminoAcidInsertions
import org.genspectrum.lapis.openApi.AminoAcidMutations
import org.genspectrum.lapis.openApi.LapisAlignedSingleSegmentedNucleotideSequenceResponse
import org.genspectrum.lapis.openApi.LapisUnalignedSingleSegmentedNucleotideSequenceResponse
import org.genspectrum.lapis.openApi.Limit
import org.genspectrum.lapis.openApi.NUCLEOTIDE_SEQUENCE_REQUEST_SCHEMA
import org.genspectrum.lapis.openApi.NucleotideInsertions
Expand Down Expand Up @@ -106,4 +107,68 @@ class SingleSegmentedSequenceController(
referenceGenomeSchema.nucleotideSequences[0].name,
)
}

@GetMapping(UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE, produces = ["text/x-fasta"])
@LapisUnalignedSingleSegmentedNucleotideSequenceResponse
fun getUnalignedNucleotideSequences(
@PrimitiveFieldFilters
@RequestParam
sequenceFilters: GetRequestSequenceFilters?,
@NucleotideSequencesOrderByFields
@RequestParam
orderBy: List<OrderByField>?,
@NucleotideMutations
@RequestParam
nucleotideMutations: List<NucleotideMutation>?,
@AminoAcidMutations
@RequestParam
aminoAcidMutations: List<AminoAcidMutation>?,
@NucleotideInsertions
@RequestParam
nucleotideInsertions: List<NucleotideInsertion>?,
@AminoAcidInsertions
@RequestParam
aminoAcidInsertions: List<AminoAcidInsertion>?,
@Limit
@RequestParam
limit: Int? = null,
@Offset
@RequestParam
offset: Int? = null,
): String {
val request = SequenceFiltersRequest(
sequenceFilters?.filter { !SPECIAL_REQUEST_PROPERTIES.contains(it.key) } ?: emptyMap(),
nucleotideMutations ?: emptyList(),
aminoAcidMutations ?: emptyList(),
nucleotideInsertions ?: emptyList(),
aminoAcidInsertions ?: emptyList(),
orderBy ?: emptyList(),
limit,
offset,
)

requestContext.filter = request

return siloQueryModel.getGenomicSequence(
request,
SequenceType.UNALIGNED,
referenceGenomeSchema.nucleotideSequences[0].name,
)
}

@PostMapping(UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE, produces = ["text/x-fasta"])
@LapisUnalignedSingleSegmentedNucleotideSequenceResponse
fun postUnalignedNucleotideSequence(
@Parameter(schema = Schema(ref = "#/components/schemas/$NUCLEOTIDE_SEQUENCE_REQUEST_SCHEMA"))
@RequestBody
request: SequenceFiltersRequest,
): String {
requestContext.filter = request

return siloQueryModel.getGenomicSequence(
request,
SequenceType.UNALIGNED,
referenceGenomeSchema.nucleotideSequences[0].name,
)
}
}
16 changes: 16 additions & 0 deletions lapis2/src/main/kotlin/org/genspectrum/lapis/openApi/Schemas.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import org.genspectrum.lapis.controller.LIMIT_DESCRIPTION
import org.genspectrum.lapis.controller.NUCLEOTIDE_INSERTIONS_ENDPOINT_DESCRIPTION
import org.genspectrum.lapis.controller.NUCLEOTIDE_MUTATION_ENDPOINT_DESCRIPTION
import org.genspectrum.lapis.controller.OFFSET_DESCRIPTION
import org.genspectrum.lapis.controller.UNALIGNED_MULTI_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION
import org.genspectrum.lapis.controller.UNALIGNED_SINGLE_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION
import org.genspectrum.lapis.request.LAPIS_DATA_VERSION_HEADER
import org.springframework.core.annotation.AliasFor

Expand Down Expand Up @@ -161,13 +163,27 @@ annotation class LapisAlignedAminoAcidSequenceResponse
)
annotation class LapisAlignedSingleSegmentedNucleotideSequenceResponse

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@LapisResponseAnnotation(
description = UNALIGNED_SINGLE_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION,
)
annotation class LapisUnalignedSingleSegmentedNucleotideSequenceResponse

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@LapisResponseAnnotation(
description = ALIGNED_MULTI_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION,
)
annotation class LapisAlignedMultiSegmentedNucleotideSequenceResponse

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@LapisResponseAnnotation(
description = UNALIGNED_MULTI_SEGMENTED_NUCLEOTIDE_SEQUENCE_ENDPOINT_DESCRIPTION,
)
annotation class LapisUnalignedMultiSegmentedNucleotideSequenceResponse

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Parameter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,80 @@ class MultiSegmentedSequenceControllerTest(
mockMvc.perform(request)
.andExpect(status().isNotFound)
}

@Test
fun `should GET unalignedNucleotideSequences with empty filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(emptyMap()),
SequenceType.UNALIGNED,
"otherSegment",
)
} returns returnedValue

mockMvc.perform(getSample("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/otherSegment"))
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should GET unalignedNucleotideSequences with filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(mapOf("country" to "Switzerland")),
SequenceType.UNALIGNED,
"otherSegment",
)
} returns returnedValue

mockMvc.perform(getSample("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/otherSegment?country=Switzerland"))
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should POST unalignedNucleotideSequences with empty filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(emptyMap()),
SequenceType.UNALIGNED,
"otherSegment",
)
} returns returnedValue

val request = postSample("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/otherSegment")
.content("""{}""")
.contentType(MediaType.APPLICATION_JSON)

mockMvc.perform(request)
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should POST unalignedNucleotideSequences with filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(mapOf("country" to "Switzerland")),
SequenceType.UNALIGNED,
"otherSegment",
)
} returns returnedValue

val request = postSample("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE/otherSegment")
.content("""{"country":"Switzerland"}""")
.contentType(MediaType.APPLICATION_JSON)

mockMvc.perform(request)
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,80 @@ class SingleSegmentedSequenceControllerTest(
mockMvc.perform(request)
.andExpect(status().isNotFound)
}

@Test
fun `should GET unalignedNucleotideSequence with empty filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(emptyMap()),
SequenceType.UNALIGNED,
"someSegment",
)
} returns returnedValue

mockMvc.perform(getSample(UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE))
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should GET unalignedNucleotideSequence with filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(mapOf("country" to "Switzerland")),
SequenceType.UNALIGNED,
"someSegment",
)
} returns returnedValue

mockMvc.perform(getSample("$UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE?country=Switzerland"))
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should POST unalignedNucleotideSequence with empty filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(emptyMap()),
SequenceType.UNALIGNED,
"someSegment",
)
} returns returnedValue

val request = postSample(UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE)
.content("""{}""")
.contentType(MediaType.APPLICATION_JSON)

mockMvc.perform(request)
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}

@Test
fun `should POST unalignedNucleotideSequence with filter`() {
val returnedValue = "TestSequenceContent"
every {
siloQueryModelMock.getGenomicSequence(
sequenceFiltersRequest(mapOf("country" to "Switzerland")),
SequenceType.UNALIGNED,
"someSegment",
)
} returns returnedValue

val request = postSample(UNALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE)
.content("""{"country": "Switzerland"}""")
.contentType(MediaType.APPLICATION_JSON)

mockMvc.perform(request)
.andExpect(status().isOk)
.andExpect(content().string(returnedValue))
.andExpect(header().stringValues("Lapis-Data-Version", "1234"))
}
}
Loading

0 comments on commit adda1dc

Please sign in to comment.