Skip to content

feat(backend): add users table with GitHub sync and internal IDs#1201

Open
fhennig wants to merge 6 commits into
feat/replace-auth-astro-with-better-authfrom
feat/user-table
Open

feat(backend): add users table with GitHub sync and internal IDs#1201
fhennig wants to merge 6 commits into
feat/replace-auth-astro-with-better-authfrom
feat/user-table

Conversation

@fhennig
Copy link
Copy Markdown
Contributor

@fhennig fhennig commented May 6, 2026

resolves #1179

Summary

  • Backend: adds a users table (BIGSERIAL PK, nullable github_id) with upsert via POST /users/sync and lookup via GET /users/{id}. Migrates owned_by/user_id FK columns in collections and subscriptions from GitHub ID strings to BIGINT references to the new table.
  • E2E: updates collection test fixtures to sync a user first and pass the internal ID to collection endpoints.

PR Checklist

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

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

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

Project Deployment Actions Updated (UTC)
dashboards Ready Ready Preview, Comment May 14, 2026 9:48am

Request Review

Copy link
Copy Markdown
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

This PR introduces a first-class users table with internal numeric IDs, adds endpoints to sync/fetch users, and migrates existing ownership fields in collections/subscriptions from GitHub ID strings to internal BIGINT user references. It also updates backend tests and website E2E tests to create/sync a user first and then use the internal user ID in requests.

Changes:

  • Add users_table (BIGSERIAL PK, nullable github_id) plus /users/sync upsert and /users/{id} public lookup.
  • Migrate collections_table.owned_by and subscriptions_table.user_id from VARCHAR GitHub IDs to BIGINT FKs referencing users_table.
  • Update backend + website E2E tests/clients to work with internal Long user IDs.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
website/tests/collections/collectionForm.spec.ts Sync a user via /users/sync and use internal user ID for collection create/delete in E2E.
website/tests/collections/collectionDetail.spec.ts Same as above for collection detail E2E setup/teardown.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/UsersControllerTest.kt New controller tests for user sync/get endpoints.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/UsersClient.kt New MockMvc client helper for /users endpoints used by tests.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/SubscriptionsControllerTriggerEvaluationTest.kt Switch subscription tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/SubscriptionsControllerTest.kt Switch subscription tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/SubscriptionsClient.kt Update test client to accept Long user IDs.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsPutTest.kt Switch collections PUT tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsPostTest.kt Switch collections POST tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsGetTest.kt Switch collections GET tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsDeleteTest.kt Switch collections DELETE tests to internal Long user IDs via UsersClient.
backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsClient.kt Update test client to accept Long user IDs.
backend/src/main/resources/db/migration/V1.3__add_users_table.sql Add users table and migrate ownership columns to bigint FKs.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/util/InstantProvider.kt Centralize millisecond-truncated “now” helper.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/user/UserTable.kt Exposed table/entity for users_table plus mapping to API models.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/user/UserModel.kt Sync/upsert and fetch logic for users.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/triggerevaluation/TriggerEvaluationModel.kt Update trigger evaluation to use Long user IDs.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/subscription/SubscriptionTable.kt Change user_id column type to long.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/subscription/SubscriptionModel.kt Update subscription model APIs to accept Long user IDs.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionTable.kt Change owned_by column type to long.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt Update collection model APIs to accept Long user IDs and use shared now().
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/UsersController.kt New /users/sync and /users/{id} endpoints.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/SubscriptionsController.kt Change request param userId type to Long.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsController.kt Change request param userId type to Long.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/User.kt Add API models: User, UserSyncRequest, PublicUser.
backend/src/main/kotlin/org/genspectrum/dashboardsbackend/api/Collection.kt Change ownedBy field type from String to Long.
Comments suppressed due to low confidence (2)

backend/src/main/kotlin/org/genspectrum/dashboardsbackend/model/collection/CollectionModel.kt:90

  • With the new FK from collections.owned_by -> users_table.id, creating a collection with an unknown userId will currently bubble up as an unexpected exception (500). Please validate that the user exists (and return 404/400) before inserting, or translate the FK violation into a client error so callers get an actionable response.
    fun createCollection(request: CollectionRequest, userId: Long): Collection {
        dashboardsConfig.validateIsValidOrganism(request.organism)
        dashboardsConfig.validateCollectionsEnabled(request.organism)

        val now = now()
        val collectionEntity = CollectionEntity.new {
            name = request.name
            organism = request.organism
            description = request.description
            ownedBy = userId
            createdAt = now
            updatedAt = now
        }

backend/src/test/kotlin/org/genspectrum/dashboardsbackend/controller/CollectionsGetTest.kt:114

  • The test case says "nonexistent user", but it now creates a real user via usersClient.createUser(). Either rename the variable/test to reflect that it's an existing user with zero collections, or use an ID that is guaranteed not to exist (e.g., a very large Long) to keep testing the original scenario.
    @Test
    fun `GIVEN user has no collections WHEN getting collections for user THEN returns empty array`() {
        val nonexistentUserId = usersClient.createUser()

        val collections = collectionsClient.getCollections(userId = nonexistentUserId)

        assertThat(collections, empty())

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@GetMapping("/users/{id}", produces = [MediaType.APPLICATION_JSON_VALUE])
@Operation(
summary = "Get user by internal ID",
description = "Returns public user info by internal UUID.",
Comment on lines +14 to +30
fun syncUser(request: UserSyncRequest): User {
val now = now()
val existing = UserEntity.findByGithubId(request.githubId)
return if (existing != null) {
existing.name = request.name
existing.email = request.email
existing.updatedAt = now
existing.toUser()
} else {
UserEntity.new {
githubId = request.githubId
name = request.name
email = request.email
createdAt = now
updatedAt = now
}.toUser()
}
Comment on lines 27 to 40
@@ -40,14 +40,14 @@ class SubscriptionModel(private val dashboardsConfig: DashboardsConfig) {
.toSubscription()
Comment on lines +10 to +11
create index idx_users_github_id on users_table(github_id);

Comment on lines +24 to +38
-- Migrate collections_table.owned_by to bigint FK
alter table collections_table add column owned_by_id bigint;
update collections_table c set owned_by_id = u.id from users_table u where u.github_id = c.owned_by;
alter table collections_table alter column owned_by_id set not null;
alter table collections_table add constraint fk_collections_user foreign key (owned_by_id) references users_table(id);
alter table collections_table drop column owned_by;
alter table collections_table rename column owned_by_id to owned_by;

-- Migrate subscriptions_table.user_id to bigint FK
alter table subscriptions_table add column user_id_new bigint;
update subscriptions_table s set user_id_new = u.id from users_table u where u.github_id = s.user_id;
alter table subscriptions_table alter column user_id_new set not null;
alter table subscriptions_table add constraint fk_subscriptions_user foreign key (user_id_new) references users_table(id);
alter table subscriptions_table drop column user_id;
alter table subscriptions_table rename column user_id_new to user_id;
fhennig and others added 6 commits May 14, 2026 10:47
Adds a users_table (BIGSERIAL PK, nullable github_id) with upsert via
POST /users/sync and lookup via GET /users/{id}. Migrates owned_by and
user_id columns in collections and subscriptions from GitHub ID strings
to BIGINT foreign keys referencing the new table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…/users/{id}

Extracts the shared now() instant helper into util/InstantProvider.kt to
avoid duplication across models. Adds PublicUser DTO (id + name only) so
the public GET /users/{id} endpoint does not expose email.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…trollerTest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collections now require a Long internal user ID. Tests previously passed
string GitHub IDs directly, causing 400/500 errors. Now each test suite
syncs the user via POST /users/sync first and uses the returned internal ID.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

backend: add user table

2 participants