feat(backend): add backend API support for generalized collections#1076
feat(backend): add backend API support for generalized collections#1076
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Adds backend support for public variant collections with owner-only modification, including persistence, REST endpoints, and test coverage. It also extends organism LAPIS config with lineageFields to validate mutation-list lineage filters.
Changes:
- Add new collections/variants data model, Flyway migration, and CRUD endpoints (
/collections). - Introduce
MutationListDefinition+ polymorphicVariant/VariantRequest/VariantUpdateAPI types (query vs mutation-list), including lineage filter validation. - Add controller-level integration tests and a dev PostgreSQL Docker Compose setup.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/src/main/resources/db/migration/V1.1__collections_and_variants.sql | Adds collections/variants tables + constraints and indexes. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionTable.kt | Exposed table/entity for collections. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/VariantTable.kt | Exposed table/entity for polymorphic variants (query vs mutationList). |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt | Implements list/get/create/update/delete business logic and authorization checks. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsController.kt | New REST controller exposing /collections CRUD endpoints. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/ExceptionHandler.kt | Adds ForbiddenException handling (403). |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/util/ConvertToUuid.kt | Extracts UUID parsing + 400 error mapping for invalid UUIDs. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/subscription/SubscriptionModel.kt | Reuses shared UUID/organism validation helpers. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/config/DashboardsConfig.kt | Adds lineageFields to LAPIS config and an organism validation helper. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Collection.kt | Defines Collection request/response/update DTOs. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Variant.kt | Defines polymorphic Variant + request/update DTOs. |
| backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/MutationListDefinition.kt | Defines mutation list structure and lineage filter capture via JsonAnySetter. |
| backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt | Adds end-to-end tests for collections/variants behaviors. |
| backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsClient.kt | Adds MockMvc test client for collections endpoints. |
| backend/src/test/kotlin/org/genspectrum/dashboardsbackend/TestHelpers.kt | Adds dummy collection/variant request builders for tests. |
| backend/src/test/resources/application.yaml | Adds test lineageFields config for Covid LAPIS. |
| backend/src/main/resources/application-dashboards-prod.yaml | Adds prod lineageFields config for Covid LAPIS. |
| backend/src/main/resources/application-local-db.yaml | Updates local DB JDBC URL to match new dev DB naming. |
| backend/docker-compose.dev.yaml | Adds dev PostgreSQL compose file. |
| backend/README.md | Documents dev DB startup and local run instructions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/MutationListDefinition.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsController.kt
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/config/DashboardsConfig.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionTable.kt
Outdated
Show resolved
Hide resolved
b8f0106 to
ccacf1d
Compare
| @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 { |
There was a problem hiding this comment.
Ah, I just saw that it looks good for the GET request, but not in the POST.
There was a problem hiding this comment.
I can't find it anymore how we did it in LAPIS, can you link it? IMO it's a shortcoming of the generator that this is missing and I personally don't mind it much, I'd rather just solve this with a doc string and move on.
...nd/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt
Outdated
Show resolved
Hide resolved
...nd/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt
Outdated
Show resolved
Hide resolved
...nd/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt
Outdated
Show resolved
Hide resolved
...nd/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt
Outdated
Show resolved
Hide resolved
...nd/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsControllerTest.kt
Outdated
Show resolved
Hide resolved
| lineageFields: | ||
| - "pangoLineage" | ||
| - "nextcladePangoLineage" |
There was a problem hiding this comment.
I assume that we need this for all organisms? And I think we also have the lineage fields in the organism configs in the website already? -> we could move it from there to here in a next step.
There was a problem hiding this comment.
Yes we would need this for all organisms, but only if we want to support lineage filtering. I'm assuming we want that. But I think forthis PR it's fine to just have it in here?
What to you mean with "And I think we also have the lineage fields in the organism configs in the website already"?
There was a problem hiding this comment.
For now this is fine.
We also have it here to some extent:
dashboards/website/src/views/covid.ts
Lines 77 to 83 in 8e8d24d
Looking at it, it's not as similar as I thought, but we could think about moving it from there to this config here.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/VariantTable.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt
Outdated
Show resolved
Hide resolved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ith Variant" This reverts commit 513ad8c.
|
To summarize, we have these two things outstanding now:
And it's not 100% certain that we can go ahead with the current MutationListVariant definition. |
fengelniederhammer
left a comment
There was a problem hiding this comment.
A few minor things, otherwise looks good 👍
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/ExceptionHandler.kt
Outdated
Show resolved
Hide resolved
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/VariantTable.kt
Outdated
Show resolved
Hide resolved
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsPutTest.kt
Show resolved
Hide resolved
* docs: make the correct type of `type` appear in OpenAPI * fix import
|
Okay, so we want to change the MutationList JSON structure again; I think I might do that in a separate PR that we can then merge into this one. |
fengelniederhammer
left a comment
There was a problem hiding this comment.
As discussed, let's merge this and do the renaming and stuff in a next PR.

resolves #1045
Summary
Adds collections:
{ "id": "550e8400-e29b-41d4-a716-446655440000", "name": "My Variant Collection", "ownedBy": "user123", "organism": "covid", "description": "A collection tracking important SARS-CoV-2 variants", "variants": [ { "type": "query", "id": "660e8400-e29b-41d4-a716-446655440001", "collectionId": "550e8400-e29b-41d4-a716-446655440000", "name": "BA.2 in USA", "description": "BA.2 lineage cases in the United States", "countQuery": "country='USA' & lineage='BA.2'", "coverageQuery": "country='USA'" }, { "type": "mutationList", "id": "660e8400-e29b-41d4-a716-446655440002", "collectionId": "550e8400-e29b-41d4-a716-446655440000", "name": "Omicron Key Mutations", "description": "Important spike protein mutations for Omicron", "mutationList": { "aaMutations": ["S:N501Y", "S:E484K", "S:K417N"] } } ] }A collection has a name and description and a list of variants. A variant is either defined as a lineage and a set of mutations, or as a query (just a string).
A collection has an owner and only the owner can modify or delete a collection. All collections are public.
New endpoints
For the updating of collections: Name, description and variants can be updated independently, only the one you want to update needs to be supplied. For the variant list though, for each variant if you want to update one property, all other properties must also be given. This is where I think maybe we can change stuff later. It is easiest to do it like this though, also if you want to maybe remove the optional coverage query, its easy to implement like this.
Variants
A variant is either defined as a count (and optional coverage) query or as a list of mutations; the format is copied from how it is done in CovSpectrum:
{ "nextcladePangoLineage": "BA.2", "aaMutations": [ "S:N501Y", "ORF1a:P3395H", "ORF1b:P314L", ], "nucMutations": [ "C241T", "C3037T", "A23063T" ], "aaInsertions": [ "S:214:EPE", ], "nucInsertions": [], }PR Checklist