-
Notifications
You must be signed in to change notification settings - Fork 0
feat(backend): add backend API support for generalized collections #1076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fhennig
wants to merge
12
commits into
main
Choose a base branch
from
generalized-collections-pt1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,802
−26
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d468adc
Add tables
fhennig 1ce6a4d
add a 'create collection' endpoint
fhennig dea93de
Add 'GET' for collections; add tests
fhennig 47a909e
Add setup to spin up local postgres instance for testing
fhennig 602b213
Add 'GET' for collections by ID
fhennig 96d2126
foo
fhennig 45b35a9
change mutation list defintion
fhennig ab9058b
Add delete implementation
fhennig 0adfe39
put pt1
fhennig 0e11bdb
dedicated Variant.kt api file
fhennig e5ed635
Change collection & variant IDs from UUID to Long
fhennig ccacf1d
review
fhennig File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| services: | ||
| postgres: | ||
| image: postgres:16-alpine | ||
| container_name: dashboards-backend-db | ||
| environment: | ||
| POSTGRES_DB: dashboards-backend-db | ||
| POSTGRES_USER: postgres | ||
| POSTGRES_PASSWORD: unsecure | ||
| ports: | ||
| - "9022:5432" | ||
| volumes: | ||
| - postgres_data:/var/lib/postgresql/data | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "pg_isready -U postgres"] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| volumes: | ||
| postgres_data: |
81 changes: 81 additions & 0 deletions
81
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Collection.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package org.genspectrum.dashboardsbackend.api | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema | ||
|
|
||
| @Schema( | ||
| description = "A collection of variants", | ||
| example = """ | ||
| { | ||
| "id": 1, | ||
| "name": "My Collection", | ||
| "ownedBy": "user123", | ||
| "organism": "covid", | ||
| "description": "A collection of interesting variants", | ||
| "variants": [] | ||
| } | ||
| """, | ||
| ) | ||
| data class Collection( | ||
| val id: Long, | ||
| val name: String, | ||
| val ownedBy: String, | ||
| val organism: String, | ||
| val description: String?, | ||
| val variants: List<Variant>, | ||
| ) | ||
|
|
||
| @Schema( | ||
| description = "Request to create a collection", | ||
| example = """ | ||
| { | ||
| "name": "My Collection", | ||
| "organism": "covid", | ||
| "description": "A collection of interesting variants", | ||
| "variants": [ | ||
| { | ||
| "type": "query", | ||
| "name": "BA.2 in USA", | ||
| "description": "BA.2 lineage cases in USA", | ||
| "countQuery": "country='USA' & lineage='BA.2'", | ||
| "coverageQuery": "country='USA'" | ||
| } | ||
| ] | ||
| } | ||
| """, | ||
| ) | ||
| data class CollectionRequest( | ||
| val name: String, | ||
| val organism: String, | ||
| val description: String? = null, | ||
| val variants: List<VariantRequest>, | ||
| ) | ||
|
|
||
| @Schema( | ||
| description = "Request to update a collection", | ||
| example = """ | ||
| { | ||
| "name": "Updated Collection Name", | ||
| "description": "Updated description", | ||
| "variants": [ | ||
| { | ||
| "type": "query", | ||
| "id": 1, | ||
| "name": "BA.2 in USA", | ||
| "description": "BA.2 lineage cases in USA", | ||
| "countQuery": "country='USA' & lineage='BA.2'", | ||
| "coverageQuery": "country='USA'" | ||
| }, | ||
| { | ||
| "type": "query", | ||
| "name": "New Variant Without ID", | ||
| "countQuery": "country='Germany'" | ||
| } | ||
| ] | ||
| } | ||
| """, | ||
| ) | ||
| data class CollectionUpdate( | ||
| val name: String? = null, | ||
| val description: String? = null, | ||
| val variants: List<VariantUpdate>? = null, | ||
| ) |
51 changes: 51 additions & 0 deletions
51
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/MutationListDefinition.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package org.genspectrum.dashboardsbackend.api | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonAnyGetter | ||
| import com.fasterxml.jackson.annotation.JsonAnySetter | ||
| import com.fasterxml.jackson.annotation.JsonIgnore | ||
|
|
||
| /** | ||
| * A JSON object with mutation lists (keys: aaMutations, nucMutations, ...) | ||
| * as well as lineage filtering (keys are defined by the organism config) | ||
| */ | ||
| data class MutationListDefinition( | ||
| val aaMutations: List<String>? = null, | ||
| val nucMutations: List<String>? = null, | ||
| val aaInsertions: List<String>? = null, | ||
| val nucInsertions: List<String>? = null, | ||
| ) { | ||
| @JsonIgnore | ||
| private val lineageFiltersInternal: MutableMap<String, String> = mutableMapOf() | ||
|
|
||
| val lineageFilters: Map<String, String> | ||
| get() = lineageFiltersInternal | ||
|
|
||
| @get:JsonAnyGetter | ||
| val additionalProperties: Map<String, String> | ||
| get() = lineageFiltersInternal | ||
|
|
||
| @JsonAnySetter | ||
| fun put(key: String, value: Any) { | ||
| if (key !in KNOWN_FIELDS && value is String) { | ||
| lineageFiltersInternal[key] = value | ||
| } | ||
| } | ||
|
Comment on lines
+27
to
+32
|
||
|
|
||
| companion object { | ||
| private val KNOWN_FIELDS = setOf("aaMutations", "nucMutations", "aaInsertions", "nucInsertions") | ||
|
|
||
| fun create( | ||
| aaMutations: List<String>? = null, | ||
| nucMutations: List<String>? = null, | ||
| aaInsertions: List<String>? = null, | ||
| nucInsertions: List<String>? = null, | ||
| lineageFilters: Map<String, String> = emptyMap(), | ||
| ): MutationListDefinition { | ||
| val definition = MutationListDefinition(aaMutations, nucMutations, aaInsertions, nucInsertions) | ||
| lineageFilters.forEach { (key, value) -> | ||
| definition.put(key, value) | ||
| } | ||
| return definition | ||
| } | ||
| } | ||
| } | ||
196 changes: 196 additions & 0 deletions
196
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Variant.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| package org.genspectrum.dashboardsbackend.api | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonCreator | ||
| import com.fasterxml.jackson.annotation.JsonProperty | ||
| import com.fasterxml.jackson.annotation.JsonSubTypes | ||
| import com.fasterxml.jackson.annotation.JsonTypeInfo | ||
| import io.swagger.v3.oas.annotations.media.Schema | ||
| import org.genspectrum.dashboardsbackend.api.Variant.MutationListVariant | ||
| import org.genspectrum.dashboardsbackend.api.Variant.QueryVariant | ||
|
|
||
| @JsonTypeInfo( | ||
| use = JsonTypeInfo.Id.NAME, | ||
| include = JsonTypeInfo.As.PROPERTY, | ||
| property = "type", | ||
| ) | ||
| @JsonSubTypes( | ||
| JsonSubTypes.Type(value = QueryVariant::class, name = "query"), | ||
| JsonSubTypes.Type(value = MutationListVariant::class, name = "mutationList"), | ||
| ) | ||
| @Schema( | ||
| description = "Base interface for different variant types", | ||
| ) | ||
| sealed interface Variant { | ||
| val id: Long | ||
| val collectionId: Long | ||
|
|
||
| enum class QueryVariantType { | ||
| @JsonProperty("query") | ||
| QUERY, | ||
| } | ||
|
|
||
| enum class MutationListVariantType { | ||
| @JsonProperty("mutationList") | ||
| MUTATION_LIST, | ||
| } | ||
|
|
||
| @Schema( | ||
| description = "A variant defined by LAPIS queries", | ||
| example = """ | ||
| { | ||
| "type": "query", | ||
| "id": 1, | ||
| "collectionId": 2, | ||
| "name": "BA.2 in USA", | ||
| "description": "BA.2 lineage cases in USA", | ||
| "countQuery": "country='USA' & lineage='BA.2'", | ||
| "coverageQuery": "country='USA'" | ||
| } | ||
| """, | ||
| ) | ||
| data class QueryVariant @JsonCreator constructor( | ||
| override val id: Long, | ||
| override val collectionId: Long, | ||
| val name: String, | ||
| val description: String?, | ||
| val countQuery: String, | ||
| val coverageQuery: String? = null, | ||
| ) : Variant { | ||
| val type: QueryVariantType = QueryVariantType.QUERY | ||
| } | ||
|
|
||
| @Schema( | ||
| description = "A variant defined by a list of mutations", | ||
| example = """ | ||
| { | ||
| "type": "mutationList", | ||
| "id": 1, | ||
| "collectionId": 2, | ||
| "name": "Omicron mutations", | ||
| "description": "Key mutations for Omicron", | ||
| "mutationList": { | ||
| "aaMutations": ["S:N501Y", "S:E484K", "S:K417N"] | ||
| } | ||
| } | ||
| """, | ||
| ) | ||
| data class MutationListVariant @JsonCreator constructor( | ||
| override val id: Long, | ||
| override val collectionId: Long, | ||
| val name: String, | ||
| val description: String?, | ||
| val mutationList: MutationListDefinition, | ||
| ) : Variant { | ||
| val type: MutationListVariantType = MutationListVariantType.MUTATION_LIST | ||
| } | ||
| } | ||
|
|
||
| @JsonTypeInfo( | ||
| use = JsonTypeInfo.Id.NAME, | ||
| include = JsonTypeInfo.As.PROPERTY, | ||
| property = "type", | ||
| ) | ||
| @JsonSubTypes( | ||
| JsonSubTypes.Type(value = VariantRequest.QueryVariantRequest::class, name = "query"), | ||
| JsonSubTypes.Type(value = VariantRequest.MutationListVariantRequest::class, name = "mutationList"), | ||
| ) | ||
| @Schema( | ||
| description = "Request to create a variant", | ||
| ) | ||
| sealed interface VariantRequest { | ||
| @Schema( | ||
| description = "Request to create a query variant", | ||
| example = """ | ||
| { | ||
| "type": "query", | ||
| "name": "BA.2 in USA", | ||
| "description": "BA.2 lineage cases in USA", | ||
| "countQuery": "country='USA' & lineage='BA.2'", | ||
| "coverageQuery": "country='USA'" | ||
| } | ||
| """, | ||
| ) | ||
| data class QueryVariantRequest( | ||
| val name: String, | ||
| val description: String? = null, | ||
| val countQuery: String, | ||
| val coverageQuery: String? = null, | ||
| ) : VariantRequest | ||
|
|
||
| @Schema( | ||
| description = "Request to create a mutation list variant", | ||
| example = """ | ||
| { | ||
| "type": "mutationList", | ||
| "name": "Omicron mutations", | ||
| "description": "Key mutations for Omicron", | ||
| "mutationList": { | ||
| "aaMutations": ["S:N501Y", "S:E484K", "S:K417N"] | ||
| } | ||
| } | ||
| """, | ||
| ) | ||
| data class MutationListVariantRequest( | ||
| val name: String, | ||
| val description: String? = null, | ||
| val mutationList: MutationListDefinition, | ||
| ) : VariantRequest | ||
| } | ||
|
|
||
| @JsonTypeInfo( | ||
| use = JsonTypeInfo.Id.NAME, | ||
| include = JsonTypeInfo.As.PROPERTY, | ||
| property = "type", | ||
| ) | ||
| @JsonSubTypes( | ||
| JsonSubTypes.Type(value = VariantUpdate.QueryVariantUpdate::class, name = "query"), | ||
| JsonSubTypes.Type(value = VariantUpdate.MutationListVariantUpdate::class, name = "mutationList"), | ||
| ) | ||
| @Schema( | ||
| description = "Request to update or create a variant", | ||
| ) | ||
| sealed interface VariantUpdate { | ||
| val id: Long? | ||
|
|
||
| @Schema( | ||
| description = "Request to update or create a query variant", | ||
| example = """ | ||
| { | ||
| "type": "query", | ||
| "id": 1, | ||
| "name": "BA.2 in USA", | ||
| "description": "BA.2 lineage cases in USA", | ||
| "countQuery": "country='USA' & lineage='BA.2'", | ||
| "coverageQuery": "country='USA'" | ||
| } | ||
| """, | ||
| ) | ||
| data class QueryVariantUpdate( | ||
| override val id: Long? = null, | ||
| val name: String, | ||
| val description: String? = null, | ||
| val countQuery: String, | ||
| val coverageQuery: String? = null, | ||
| ) : VariantUpdate | ||
|
|
||
| @Schema( | ||
| description = "Request to update or create a mutation list variant", | ||
| example = """ | ||
| { | ||
| "type": "mutationList", | ||
| "id": 1, | ||
| "name": "Omicron mutations", | ||
| "description": "Key mutations for Omicron", | ||
| "mutationList": { | ||
| "aaMutations": ["S:N501Y", "S:E484K", "S:K417N"] | ||
| } | ||
| } | ||
| """, | ||
| ) | ||
| data class MutationListVariantUpdate( | ||
| override val id: Long? = null, | ||
| val name: String, | ||
| val description: String? = null, | ||
| val mutationList: MutationListDefinition, | ||
| ) : VariantUpdate | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have a Docker compose file at the root with a Postgres - would that also do the job?