Skip to content

feat(backend): add backend API support for generalized collections#1076

Open
fhennig wants to merge 37 commits intomainfrom
generalized-collections-pt1
Open

feat(backend): add backend API support for generalized collections#1076
fhennig wants to merge 37 commits intomainfrom
generalized-collections-pt1

Conversation

@fhennig
Copy link
Contributor

@fhennig fhennig commented Mar 11, 2026

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

  • GET /collections - List collections with optional filtering by userId and organism
  • GET /collections/{id} - Get a single collection with all its variants
  • POST /collections - Create a new collection with variants
  • PUT /collections/{id} - Update a collection (partial updates supported)
  • DELETE /collections/{id} - Delete a collection (owner-only)

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

  • All necessary documentation has been adapted.
  • The implemented feature is covered by an appropriate test.

@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboards Ready Ready Preview, Comment Mar 19, 2026 8:41am

Request Review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 + polymorphic Variant/VariantRequest/VariantUpdate API 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.

Comment on lines +11 to +23
@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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type doesn't tell which value is allowed here. Can we copy the magic that we did in LAPIS over here?

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I just saw that it looks good for the GET request, but not in the POST.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I made it work: #1088

Comment on lines +7 to +9
lineageFields:
- "pangoLineage"
- "nextcladePangoLineage"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now this is fine.

We also have it here to some extent:

public readonly lineageFilters: LineageFilterConfig[] = [
{
lapisField: NEXTCLADE_PANGO_LINEAGE_FIELD_NAME,
placeholderText: 'Nextclade pango lineage',
filterType: 'lineage' as const,
},
];

Looking at it, it's not as similar as I thought, but we could think about moving it from there to this config here.

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>
@fhennig
Copy link
Contributor Author

fhennig commented Mar 18, 2026

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.

Copy link
Contributor

@fengelniederhammer fengelniederhammer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor things, otherwise looks good 👍

* docs: make the correct type of `type` appear in OpenAPI

* fix import
@fhennig
Copy link
Contributor Author

fhennig commented Mar 19, 2026

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.

Copy link
Contributor

@fengelniederhammer fengelniederhammer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, let's merge this and do the renaming and stuff in a next PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement initial generalized collections in the backend

3 participants